v0.6.0 — Tracejam fixes, three new slop-test gates, two new SaaS tests, gallery thumbnails
## Tracejam (test 05) defects fixed
- Horizontal scroll suppressed via `html { overflow-x: clip }` + `body` fallback
- Navbar items vertically centered (`align-items: center`, `line-height: 1`)
- Wordmark switched from Geist to Fraunces (`--font-wordmark`) — different register from body
- Hero highlight raised from baseline to mid-x-height — reads as highlighter, not fat underline
- Browser-frame chrome dots get `aria-hidden="true"`
## Skill hardening — three new slop-test gates (35 → 38)
- **Gate 36**: page must not horizontally scroll on any viewport 320–1920 px
- **Gate 37**: decorative effects on text (highlighter / accent stroke / mark) must be visually verified at x-height, not baseline
- **Gate 38**: interactive bars must declare `align-items: center` + `line-height: 1`
## New reference rules
- `layout-and-space.md` § Page-edge clipping — global clip is mandatory under any clipped-edge enrichment
- `typography.md` § Wordmark / logo — wordmark may use a different display face; should on Bento / Stat-Led / Workbench / Marquee Hero
- `microinteractions.md` § When to ship motion by default — default-on for SaaS-shaped archetypes, default-off for Editorial / Manifesto / Letter / Quote-Led / Long Document; six recipes documented (number reveal, pricing lift, marquee, stagger, recommended-tier pulse, CTA hover)
- `macrostructures.md` § SaaS page sequence — hero → social proof → features → testimonials → pricing → FAQ → CTA → footer; with voice rules (real prices, specific testimonials, conversational FAQ)
## Meridian Manifesto (test 04) extended
7 sections → 11 sections. Added § What we refuse (anti-list with strikethrough), § Working rules (7 numbered operational principles), § The practice (3-paragraph narrative with drop-cap), § What we read (annotated bibliography with 8 titles). Voice consistent with the original manifesto — declarative, no testimonials, no pricing.
## Two new SaaS-focused tests
- **Test 07 — Foundry** (Stat-Led + Newsprint). SOC2 / ISO27001 compliance automation. Hero is one giant number ("847" — animated counter from 0). 9 sections incl. 3-tier pricing, 3 testimonials with role + company, 8-question FAQ. Pricing card hover-lift + recommended-tier pulse-once.
- **Test 08 — Cohort** (Marquee Hero + Salon). Cohort-based courses platform. Continuous-scroll marquee of course titles. Alternating-side feature rows with hand-built CSS visualisations (card-stack, hours grid, thread, report). Stagger-reveal on testimonials. 2-tier pricing, 6-question FAQ, centered Salon footer.
Both tests respect prefers-reduced-motion. Both pass the 38-gate slop test. Both demonstrate the new SaaS page sequence.
## Gallery thumbnails
`site/_tests/_thumbs/01–08.png` — eight 1024×640 screenshots captured via Chrome headless. Gallery cards on the landing page now show real visual previews above the text. Cards inherit theme tokens (radius, rule, shadow) so switching the landing page's theme reskins the card frames around the screenshots. Hover scales the thumb 1.025× over 500 ms; respects prefers-reduced-motion.
Section title updated: "Six pages, six different shapes." → "Eight pages, eight different shapes."
## SKILL.md version bump
0.5.0 → 0.6.0.
## Files
- 13 modified, 6 created (tests 07 & 08 directories), 8 thumbnails added.
- ~840 lines added, ~25 removed.
- All skill files mirrored into `~/.claude/skills/hallmark/`.
## Deferred to next session
- **Study + redesign combined verb** — single-shot screenshot-to-rebuilt-page.
- **Auto-generated OG cards** — Node + Puppeteer utility for 1200×630 PNG from macrostructure stamp.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@@ -30,7 +30,7 @@ The differentiator: Hallmark insists on **structural variety**, not just visual
|
||||
|
||||
- **[`SKILL.md`](skill/SKILL.md)** — the routing file. Design flow (six steps, including `Step 2.5 · Check project memory` reading `.hallmark/log.json`), slop test (35 questions), output contract.
|
||||
- **[`references/`](skill/references/)** — sixteen short, opinionated rule files: typography, colour, layout, motion, microinteractions, interaction-and-states, responsive, copy, anti-patterns, the 21 named macrostructures, the 36 component archetypes with variation knobs, the 6 primitive structure axes, the vision-extraction protocol for `study`, and three new files for hero work — `hero-enrichment.md` (when to add visuals to a hero, plus eight enrichment archetypes), `custom-craft.md` (how to hand-build CSS art, SVG, declarative animation, JS-driven motion, with Lottie demoted to last resort), and `assets.md` (sourcing canon for icons, brand logos, generated illustration via Nanobanana / Recraft, library illustration, app mockups, hero video, photography, abstract backgrounds).
|
||||
- **[`site/`](site/)** — a self-demonstrating landing page. Hand-written HTML + CSS + ES module, no framework, no build step. **Sixteen themes** — Specimen, Newsprint, Atelier, Garden, Salon, Linen, Almanac, Midnight, Terminal, Brutal, Manifesto, Sport, Studio, Pastel, Riso, **Quiet** — that swap not just colour and typography but the page's hero archetype and footer archetype. Switching themes literally rebuilds the page.
|
||||
- **[`site/`](site/)** — a self-demonstrating landing page. Hand-written HTML + CSS + ES module, no framework, no build step. **Sixteen themes** — Specimen, Newsprint, Atelier, Garden, Salon, Linen, Almanac, Midnight, Terminal, Brutal, Manifesto, Sport, Studio, Pastel, Riso, **Quiet** — that swap not just colour and typography but the page's hero archetype and footer archetype. Switching themes literally rebuilds the page. The "07 · Examples" section ships with **eight** generation tests (under [`site/_tests/`](site/_tests/)) — each a self-contained HTML + CSS demo of the skill, with a real visual thumbnail on the gallery card.
|
||||
- **[`ROADMAP.md`](ROADMAP.md)** — Tier 1, 2, 3 work plus an explicit "things to *not* do" list.
|
||||
|
||||
---
|
||||
@@ -60,11 +60,14 @@ Press `T` to cycle themes, `R` for random, `?theme=studio` for a shareable link.
|
||||
- **Macrostructures over axes.** Pick one of 21 named whole-page shapes wholesale; the macrostructure stamp lives in the CSS comment, so the next Hallmark run picks something different.
|
||||
- **Within-archetype variation.** Two Bento Grids should not be twins; each archetype has 2–3 picked-per-output knobs.
|
||||
- **Microinteractions as discipline.** Silent success over celebratory toasts. Optimistic update + Undo over confirm dialogs. Hover delay 800 ms, focus delay 0 ms.
|
||||
- **A 35-gate slop test** runs before every output. One yes fails the build. New gates 34 (different-knobs check across runs) and 35 (aria-label / aria-hidden on every visual-only SVG / canvas / div).
|
||||
- **A 38-gate slop test** runs before every output. One yes fails the build. Three new gates added in v0.6.0: gate 36 (no horizontal scroll between 320 px and 1920 px — `html { overflow-x: clip }` is mandatory under any clipped-edge enrichment), gate 37 (decorative effects on text — highlighter bands, accent strokes, underlines — must be visually verified to sit in the right vertical zone, not at the baseline like a fat underline), gate 38 (interactive bars must declare `align-items: center` and `line-height: 1` — inheriting line-height fights vertical rhythm).
|
||||
- **Project memory.** A per-project `.hallmark/log.json` records each run's macrostructure + theme + enrichment + brief summary. The skill reads the last 3–5 entries before picking, and writes a new entry after each build, so consecutive Hallmark outputs in the same project don't repeat shapes or themes.
|
||||
- **Theme-diversification rule.** Two consecutive themes must differ on at least one of three axes: paper band (dark / mid / light), display style (italic-serif / roman-serif / geometric-sans / mono / display-condensed-italic / display-heavy / system-native / risograph), accent hue (warm / cool / neutral / chromatic). Specimen-fall-through is no longer the only diversification check.
|
||||
- **Voice fixtures over LLM defaults.** Each of the 21 macrostructures ships with 2–3 example opening lines from real designer-engineer sites (Pentagram, Klim, Linear, Are.na, Resend, Lynn Fisher, Rauno Freiberg, etc.). Each of seven tones in `copy.md` has three voice patterns × five sample sentences. "Built for the modern team" is in the banned-phrases list.
|
||||
- **Hero enrichment is opt-in, not a default.** A typographic-only hero is always acceptable. When enrichment is right, the skill picks from a six-tier hierarchy: typography only → custom-built CSS art → hand-built SVG → generated illustration (Nanobanana / Recraft) → library + customise → Lottie (last resort).
|
||||
- **Microinteractions are default-on for SaaS-shaped archetypes.** Bento Grid, Stat-Led, Workbench, Marquee Hero, and Conversational FAQ pages ship with 2–3 small purposeful microinteractions (number reveal on stats, pricing card lift, marquee scroll, recommended-tier pulse, stagger reveal on testimonials) without the user having to ask. Editorial / Manifesto / Letter / Quote-Led pages stay still — stillness is the brand on those.
|
||||
- **SaaS page sequence.** When the macrostructure is a SaaS-typical archetype, ship hero → social proof / logo wall → features → testimonials → pricing → FAQ → CTA → footer by default. Real prices, not "contact sales for pricing" on every tier; specific testimonials with role and company, not abstract job titles.
|
||||
- **Wordmark may use a different display face.** A Geist-bodied SaaS page can set its wordmark in Fraunces; a Crimson-Pro-bodied compliance page can use IBM Plex Mono. Same-family collapse on Bento / Stat-Led / Workbench / Marquee Hero archetypes is the new "un-branded" tell.
|
||||
- **`study` extracts DNA, not pixels.** Vision-extraction with refusal heuristics, type-role vocabulary (no font ID guessing), and a confirmation step before any code.
|
||||
|
||||
---
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
<header class="masthead">
|
||||
<span class="name">Meridian</span>
|
||||
<span class="meta">2026 · <span class="accent">A MANIFESTO</span> · IN FIVE PARTS</span>
|
||||
<span class="meta">LISBON · 2026 · <span class="accent">A MANIFESTO</span> · WITH NOTES</span>
|
||||
</header>
|
||||
|
||||
<section class="title">
|
||||
@@ -48,6 +48,122 @@
|
||||
<p class="declaration__body">We will not put our work behind a chatbot. We will <strong>answer the email ourselves,</strong> or not at all.</p>
|
||||
</section>
|
||||
|
||||
<section class="refuse">
|
||||
<header class="refuse__head">
|
||||
<span class="refuse__label">— WHAT WE REFUSE —</span>
|
||||
<p class="refuse__lede">A studio is what it doesn't do as much as what it does. The list:</p>
|
||||
</header>
|
||||
<ul class="refuse__list">
|
||||
<li><span class="strike">The lifestyle stock photo of a smiling person with a laptop on a wooden table.</span></li>
|
||||
<li><span class="strike">The 90-minute discovery call disguised as a sales meeting.</span></li>
|
||||
<li><span class="strike">The synthetic testimonial. The "Fortune 500 trust us" without the names.</span></li>
|
||||
<li><span class="strike">The roadmap webinar. The launch countdown. The early-access waitlist gate.</span></li>
|
||||
<li><span class="strike">The retainer that buys our attention at the expense of our work.</span></li>
|
||||
<li><span class="strike">The hero shot. The lifestyle render. The "in-the-wild" mockup.</span></li>
|
||||
<li><span class="strike">"Innovation." "Ecosystem." "Reimagine." "Empower." "Unleash."</span></li>
|
||||
<li><span class="strike">Plastic that promises to biodegrade in six hundred years and a research paper.</span></li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="principles">
|
||||
<header class="principles__head">
|
||||
<span class="principles__label">— WORKING RULES —</span>
|
||||
<h2 class="principles__title">How we work, on the days we are honest about it.</h2>
|
||||
</header>
|
||||
<ol class="principles__list">
|
||||
<li>
|
||||
<span class="principles__num">01</span>
|
||||
<p>We meet the material before we meet the brief. If we cannot hold the thing, we do not name it.</p>
|
||||
</li>
|
||||
<li>
|
||||
<span class="principles__num">02</span>
|
||||
<p>The first sketch is on paper. The second sketch is on paper. The third is the prototype.</p>
|
||||
</li>
|
||||
<li>
|
||||
<span class="principles__num">03</span>
|
||||
<p>If we cannot describe the product in two sentences to a person who is not paid to listen, the product is wrong.</p>
|
||||
</li>
|
||||
<li>
|
||||
<span class="principles__num">04</span>
|
||||
<p>We do not present three options when we know which one we believe in. The other two are theatre.</p>
|
||||
</li>
|
||||
<li>
|
||||
<span class="principles__num">05</span>
|
||||
<p>We send the invoice the same week we send the work. Money is part of the practice.</p>
|
||||
</li>
|
||||
<li>
|
||||
<span class="principles__num">06</span>
|
||||
<p>We will say no in the first meeting if we mean to say no. The second meeting is for yes.</p>
|
||||
</li>
|
||||
<li>
|
||||
<span class="principles__num">07</span>
|
||||
<p>The studio closes for August. The studio closes for the week between Christmas and New Year. We are not a service.</p>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section class="practice">
|
||||
<header class="practice__head">
|
||||
<span class="practice__label">— THE PRACTICE —</span>
|
||||
<h2 class="practice__title">What a project at Meridian looks like, end to end.</h2>
|
||||
</header>
|
||||
<div class="practice__body">
|
||||
<p><span class="dropcap">A</span> project begins with a letter. Not an email — a letter, on paper, in the post. We ask the client to write us one before the first call. Most of them do. The ones who don't, we don't take. The letter is not a screening test; it is a way of slowing the start of work down to the speed at which work actually happens.</p>
|
||||
<p>The first three weeks are field research. We visit the site, the workshop, the warehouse, the factory floor — wherever the thing in question is being made or used. We do not begin sketching until we have stood in the place where the work will live. Photographs come back, but not for the deck. They come back so we remember what the room smelled like.</p>
|
||||
<p>The middle of a project is materials cycle: we order samples, we cut them, we age them in the studio's south-facing window for two weeks, we drop them in the rain, we forget about them, we come back. We document the cycle in a small printed booklet that ships with the final delivery. The booklet is not a flourish — it is the receipt that the work was made, not specified.</p>
|
||||
<p>A project ends with a post-occupancy review, six months after delivery. We come back, we measure what happened, we publish the findings — even when the findings are uncomfortable. The findings live on this site, in the section called Notes, which we update on the last Friday of every quarter.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="reading">
|
||||
<header class="reading__head">
|
||||
<span class="reading__label">— WHAT WE READ —</span>
|
||||
<p class="reading__lede">A short list of things that shape how we think. Most of them are old. None of them are by us.</p>
|
||||
</header>
|
||||
<ul class="reading__list">
|
||||
<li>
|
||||
<span class="reading__year">1977</span>
|
||||
<span class="reading__title">A Pattern Language</span>
|
||||
<span class="reading__author">Christopher Alexander, Sara Ishikawa, Murray Silverstein</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="reading__year">1994</span>
|
||||
<span class="reading__title">How Buildings Learn: What Happens After They're Built</span>
|
||||
<span class="reading__author">Stewart Brand</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="reading__year">2006</span>
|
||||
<span class="reading__title">Sustainable by Design: Explorations in Theory and Practice</span>
|
||||
<span class="reading__author">Stuart Walker</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="reading__year">1973</span>
|
||||
<span class="reading__title">Small Is Beautiful: A Study of Economics as if People Mattered</span>
|
||||
<span class="reading__author">E. F. Schumacher</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="reading__year">1985</span>
|
||||
<span class="reading__title">The Tao of Programming</span>
|
||||
<span class="reading__author">Geoffrey James — yes, the joke book; the joke is the lesson</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="reading__year">2018</span>
|
||||
<span class="reading__title">The Tyranny of Metrics</span>
|
||||
<span class="reading__author">Jerry Z. Muller</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="reading__year">2003</span>
|
||||
<span class="reading__title">In Praise of Shadows</span>
|
||||
<span class="reading__author">Jun'ichirō Tanizaki, trans. Harper & Seidensticker</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="reading__year">2021</span>
|
||||
<span class="reading__title">Four Thousand Weeks: Time Management for Mortals</span>
|
||||
<span class="reading__author">Oliver Burkeman</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<footer class="colophon">
|
||||
<h2>Meridian</h2>
|
||||
<span>Studio for environmental products · Lisbon · est. 2026</span>
|
||||
|
||||
@@ -151,6 +151,284 @@ body {
|
||||
max-width: 38ch;
|
||||
}
|
||||
|
||||
/* ─── WHAT WE REFUSE ─── */
|
||||
|
||||
.refuse {
|
||||
display: grid;
|
||||
grid-template-columns: clamp(4rem, 10vw, 8rem) 1fr;
|
||||
gap: var(--space-xl);
|
||||
padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
|
||||
border-block-end: var(--rule-fine) solid var(--ink);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.refuse__head { display: contents; }
|
||||
|
||||
.refuse__label {
|
||||
font-family: var(--label);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--accent);
|
||||
align-self: start;
|
||||
margin-block-start: 0.4em;
|
||||
}
|
||||
|
||||
.refuse__lede {
|
||||
font-family: var(--display);
|
||||
font-weight: 700;
|
||||
font-size: clamp(1.125rem, 1.6vw + 0.5rem, 1.5rem);
|
||||
line-height: 1.3;
|
||||
letter-spacing: -0.012em;
|
||||
text-transform: uppercase;
|
||||
color: var(--ink);
|
||||
max-width: 36ch;
|
||||
grid-column: 2;
|
||||
margin-block-end: var(--space-xl);
|
||||
}
|
||||
|
||||
.refuse__list {
|
||||
list-style: none;
|
||||
grid-column: 2;
|
||||
display: grid;
|
||||
gap: var(--space-md);
|
||||
}
|
||||
|
||||
.refuse__list li {
|
||||
font-family: var(--label);
|
||||
font-size: 0.9375rem;
|
||||
letter-spacing: 0.02em;
|
||||
color: var(--ink-2);
|
||||
line-height: 1.55;
|
||||
max-width: 60ch;
|
||||
padding-inline-start: 1.4em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.refuse__list li::before {
|
||||
content: "✕";
|
||||
position: absolute;
|
||||
inset-inline-start: 0;
|
||||
top: 0.05em;
|
||||
color: var(--accent);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.refuse__list .strike {
|
||||
text-decoration: line-through;
|
||||
text-decoration-thickness: 1.5px;
|
||||
text-decoration-color: var(--accent);
|
||||
text-underline-offset: 0;
|
||||
}
|
||||
|
||||
/* ─── WORKING RULES ─── */
|
||||
|
||||
.principles {
|
||||
padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
|
||||
border-block-end: var(--rule-fine) solid var(--ink);
|
||||
}
|
||||
|
||||
.principles__head {
|
||||
margin-block-end: var(--space-2xl);
|
||||
max-width: 50rem;
|
||||
}
|
||||
|
||||
.principles__label {
|
||||
font-family: var(--label);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--accent);
|
||||
display: block;
|
||||
margin-block-end: var(--space-md);
|
||||
}
|
||||
|
||||
.principles__title {
|
||||
font-family: var(--display);
|
||||
font-weight: 800;
|
||||
font-size: clamp(1.625rem, 3vw + 0.5rem, 3rem);
|
||||
line-height: 1.05;
|
||||
letter-spacing: -0.025em;
|
||||
text-transform: uppercase;
|
||||
color: var(--ink);
|
||||
max-width: 24ch;
|
||||
}
|
||||
|
||||
.principles__list {
|
||||
list-style: none;
|
||||
display: grid;
|
||||
gap: 0;
|
||||
border-block-start: var(--rule-fine) solid var(--ink);
|
||||
}
|
||||
|
||||
.principles__list li {
|
||||
display: grid;
|
||||
grid-template-columns: clamp(3rem, 7vw, 5rem) 1fr;
|
||||
gap: var(--space-xl);
|
||||
padding-block: var(--space-xl);
|
||||
border-block-end: 1px solid var(--rule);
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.principles__num {
|
||||
font-family: var(--label);
|
||||
font-size: 0.875rem;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--accent);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.principles__list li p {
|
||||
font-family: var(--display);
|
||||
font-weight: 500;
|
||||
font-size: clamp(1.125rem, 1.4vw + 0.5rem, 1.4375rem);
|
||||
line-height: 1.35;
|
||||
letter-spacing: -0.012em;
|
||||
color: var(--ink);
|
||||
max-width: 56ch;
|
||||
}
|
||||
|
||||
/* ─── THE PRACTICE ─── */
|
||||
|
||||
.practice {
|
||||
padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
|
||||
border-block-end: var(--rule-fine) solid var(--ink);
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: var(--space-2xl);
|
||||
background: var(--paper-2);
|
||||
}
|
||||
|
||||
.practice__head { max-width: 60rem; }
|
||||
|
||||
.practice__label {
|
||||
font-family: var(--label);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--accent);
|
||||
display: block;
|
||||
margin-block-end: var(--space-md);
|
||||
}
|
||||
|
||||
.practice__title {
|
||||
font-family: var(--display);
|
||||
font-weight: 800;
|
||||
font-size: clamp(1.625rem, 3vw + 0.5rem, 3rem);
|
||||
line-height: 1.05;
|
||||
letter-spacing: -0.025em;
|
||||
text-transform: uppercase;
|
||||
color: var(--ink);
|
||||
max-width: 26ch;
|
||||
}
|
||||
|
||||
.practice__body {
|
||||
display: grid;
|
||||
gap: var(--space-lg);
|
||||
max-width: 68ch;
|
||||
font-family: var(--display);
|
||||
font-weight: 400;
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.65;
|
||||
color: var(--ink-2);
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
|
||||
.practice__body p { text-wrap: pretty; }
|
||||
|
||||
.practice__body .dropcap {
|
||||
font-family: var(--display);
|
||||
font-weight: 800;
|
||||
font-size: 3.6em;
|
||||
line-height: 0.85;
|
||||
float: inline-start;
|
||||
padding-inline-end: 0.12em;
|
||||
padding-block-start: 0.06em;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* ─── READING ─── */
|
||||
|
||||
.reading {
|
||||
padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
|
||||
border-block-end: var(--rule-fine) solid var(--ink);
|
||||
}
|
||||
|
||||
.reading__head {
|
||||
margin-block-end: var(--space-2xl);
|
||||
max-width: 60rem;
|
||||
}
|
||||
|
||||
.reading__label {
|
||||
font-family: var(--label);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--accent);
|
||||
display: block;
|
||||
margin-block-end: var(--space-md);
|
||||
}
|
||||
|
||||
.reading__lede {
|
||||
font-family: var(--display);
|
||||
font-weight: 700;
|
||||
font-size: clamp(1.125rem, 1.6vw + 0.5rem, 1.5rem);
|
||||
line-height: 1.3;
|
||||
letter-spacing: -0.012em;
|
||||
color: var(--ink);
|
||||
max-width: 56ch;
|
||||
}
|
||||
|
||||
.reading__list {
|
||||
list-style: none;
|
||||
display: grid;
|
||||
border-block-start: var(--rule-fine) solid var(--ink);
|
||||
}
|
||||
|
||||
.reading__list li {
|
||||
display: grid;
|
||||
grid-template-columns: clamp(3rem, 7vw, 5rem) 1fr clamp(10rem, 22vw, 18rem);
|
||||
gap: var(--space-lg);
|
||||
padding-block: var(--space-lg);
|
||||
border-block-end: 1px solid var(--rule);
|
||||
align-items: baseline;
|
||||
font-family: var(--display);
|
||||
}
|
||||
|
||||
.reading__year {
|
||||
font-family: var(--label);
|
||||
font-size: 0.875rem;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.reading__title {
|
||||
font-weight: 600;
|
||||
font-size: clamp(1rem, 1vw + 0.5rem, 1.1875rem);
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--ink);
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.reading__author {
|
||||
font-family: var(--label);
|
||||
font-size: 0.8125rem;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--muted);
|
||||
line-height: 1.5;
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
@media (max-width: 56rem) {
|
||||
.reading__list li {
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-xs);
|
||||
}
|
||||
.reading__author { text-align: start; }
|
||||
}
|
||||
|
||||
/* ─── COLOPHON (existing) ─── */
|
||||
|
||||
.colophon {
|
||||
padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
|
||||
display: grid;
|
||||
|
||||
@@ -32,9 +32,9 @@
|
||||
|
||||
<figure class="mock" aria-hidden="true">
|
||||
<header class="mock__chrome">
|
||||
<span class="mock__dot"></span>
|
||||
<span class="mock__dot"></span>
|
||||
<span class="mock__dot"></span>
|
||||
<span class="mock__dot" aria-hidden="true"></span>
|
||||
<span class="mock__dot" aria-hidden="true"></span>
|
||||
<span class="mock__dot" aria-hidden="true"></span>
|
||||
<span class="mock__url">app.tracejam.io / traces / 7f8e9a</span>
|
||||
</header>
|
||||
<div class="mock__body">
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* studied: no · context: explicit
|
||||
*/
|
||||
|
||||
@import url("https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500&display=swap");
|
||||
@import url("https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,600&family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500&display=swap");
|
||||
|
||||
:root {
|
||||
--paper: oklch(96% 0.022 285);
|
||||
@@ -25,9 +25,10 @@
|
||||
--span-c: oklch(74% 0.09 200);
|
||||
--span-d: oklch(56% 0.16 30); /* error span */
|
||||
|
||||
--display: "Geist", "Söhne", system-ui, sans-serif;
|
||||
--body: "Geist", "Söhne", system-ui, sans-serif;
|
||||
--mono: "Geist Mono", ui-monospace, monospace;
|
||||
--display: "Geist", "Söhne", system-ui, sans-serif;
|
||||
--body: "Geist", "Söhne", system-ui, sans-serif;
|
||||
--mono: "Geist Mono", ui-monospace, monospace;
|
||||
--font-wordmark: "Fraunces", "Georgia", serif;
|
||||
|
||||
--space-xs: 0.5rem;
|
||||
--space-sm: 0.75rem;
|
||||
@@ -55,10 +56,12 @@ html {
|
||||
color: var(--ink-2);
|
||||
background: var(--paper);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
overflow-x: clip;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100dvh;
|
||||
overflow-x: clip;
|
||||
}
|
||||
|
||||
.nav {
|
||||
@@ -75,11 +78,14 @@ body {
|
||||
}
|
||||
|
||||
.nav .wordmark {
|
||||
font-family: var(--display);
|
||||
font-family: var(--font-wordmark);
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
letter-spacing: -0.01em;
|
||||
font-size: 1.0625rem;
|
||||
letter-spacing: -0.015em;
|
||||
color: var(--ink);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
line-height: 1;
|
||||
}
|
||||
.nav .wordmark::before {
|
||||
content: "";
|
||||
@@ -89,14 +95,15 @@ body {
|
||||
background: var(--accent);
|
||||
border-radius: 4px;
|
||||
margin-inline-end: 0.6em;
|
||||
vertical-align: -2px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.nav__links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-lg);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1;
|
||||
}
|
||||
.nav__links a {
|
||||
color: var(--ink-2);
|
||||
@@ -154,8 +161,11 @@ body {
|
||||
}
|
||||
.hero__title em {
|
||||
font-style: normal;
|
||||
background: linear-gradient(180deg, transparent 70%, var(--accent-soft) 70%);
|
||||
padding: 0 0.05em;
|
||||
background: linear-gradient(180deg, transparent 38%, var(--accent-soft) 38%, var(--accent-soft) 92%, transparent 92%);
|
||||
padding: 0.04em 0.14em;
|
||||
margin-inline: -0.04em;
|
||||
box-decoration-break: clone;
|
||||
-webkit-box-decoration-break: clone;
|
||||
}
|
||||
|
||||
.hero__lede {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# Test 07 — Foundry (SOC2 / ISO27001 compliance automation)
|
||||
|
||||
## The prompt
|
||||
|
||||
> "Build a landing page for Foundry — SOC2 and ISO 27001 compliance automation for B2B SaaS. Show: how many companies got compliant, what it costs, who uses it. Audience: founders + CTOs. Tone: technical but trustworthy."
|
||||
|
||||
## Context gate — fully answered
|
||||
|
||||
Audience, use, tone all explicit. No inference needed.
|
||||
|
||||
## Domain inference
|
||||
|
||||
`platform / tool / infra / observability / dashboard SaaS` (try-or-talk-to-sales) → from the refined trio table: **Bento Grid / Workbench / Stat-Led** are reasonable picks.
|
||||
|
||||
The skill picks **Stat-Led** because the brief explicitly leads with a number ("how many companies"), and compliance is a *proof-by-numbers* sale. The hero is one giant number; every section anchors on a stat.
|
||||
|
||||
## Theme
|
||||
|
||||
The previous SaaS test (Tracejam) used **Pastel** (light · geometric-sans · cool-indigo). Diversification rule says the next theme must differ on at least one of three axes. **Newsprint** (light · roman-serif Playfair · warm-deep-red) differs on two — display style and accent hue. Plus Newsprint reads as *trade publication serious*, which is the right register for compliance. Pick: Newsprint.
|
||||
|
||||
## Enrichment
|
||||
|
||||
**None.** Stat-Led's hero is the number; adding a mockup would dilute the proof. A typographic-only hero is correct here. Per the enrichment hierarchy, this is Tier 0 (typography only) — the strongest hero a Stat-Led page can have.
|
||||
|
||||
## Microinteractions
|
||||
|
||||
The skill's new microinteractions rule says Stat-Led is **default-on**. Three primitives (max), purposeful:
|
||||
|
||||
1. **Number reveal** on the "847" — IntersectionObserver triggers a `requestAnimationFrame` count from 0 to target over 1.4 s, ease-out. Reduced-motion: render the final value instantly.
|
||||
2. **Pricing card hover lift** — `translateY(-3px)` + shadow upgrade, 180 ms ease-out. Active state drops back at 60 ms.
|
||||
3. **Recommended-tier pulse** — one-shot `@keyframes pulse-border` 2 s, runs once on viewport entry. Subtle: opacity 0.4 → 1 → 0.4 on the border. Doesn't loop.
|
||||
|
||||
## Macrostructure stamp
|
||||
|
||||
```
|
||||
/* Hallmark · macrostructure: Stat-Led · S1 hero knobs: number=8xl, alignment=left-bias, supporting=enumerated
|
||||
* theme: Newsprint · accent: deep-red ~3% (band rules + recommended-tier)
|
||||
* enrichment: none (typography only — the number is the hero)
|
||||
* studied: no · context: explicit
|
||||
* motion: number-reveal (counter), pricing-lift, recommended-pulse — three primitives, default-on per archetype
|
||||
*/
|
||||
```
|
||||
|
||||
## Diversification axes (Newsprint vs prior outputs in this test set)
|
||||
|
||||
| Theme | Paper band | Display style | Accent hue |
|
||||
| --- | --- | --- | --- |
|
||||
| Pastel (Tracejam) | light | geometric-sans | cool-indigo |
|
||||
| **Newsprint (Foundry)** | **light** | **roman-serif** | **warm-deep-red** |
|
||||
| Differs on | (same) | DIFFERS | DIFFERS |
|
||||
|
||||
Two of three axes differ. Passes the theme-diversification rule.
|
||||
|
||||
## What the page does
|
||||
|
||||
1. **Hero (Stat-Led).** "847" big enough to be the room. Supporting prose: "companies passed audit using Foundry. Avg time-to-compliant: 31 days, vs industry avg of 9 months."
|
||||
2. **Logo wall.** 8 customer wordmarks, monochrome, tabular, one row.
|
||||
3. **Stats grid.** Three supporting numbers — pass rate, avg days, hours saved.
|
||||
4. **Features (4 cards).** Continuous monitoring · Auto-evidence collection · Vendor risk · Audit-day support.
|
||||
5. **Testimonials (3).** Pull-quote, name, role, company. Specific to use cases.
|
||||
6. **Pricing (3 tiers).** Starter $299/mo · Team $899/mo (recommended) · Scale (custom). Feature checklist per tier. Animated pulse on Team.
|
||||
7. **FAQ (8 questions).** Conversational FAQ, expanded by default.
|
||||
8. **CTA strip.** "Start your audit. 14 days free." One button.
|
||||
9. **Footer.** Index-style, 4 columns.
|
||||
|
||||
The page is intentionally long — it demonstrates the SaaS page sequence rule from `macrostructures.md`: hero → social proof → features → testimonials → pricing → FAQ → CTA → footer.
|
||||
@@ -0,0 +1,378 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Foundry — SOC2 in 31 days, not 9 months</title>
|
||||
<meta name="description" content="SOC2 and ISO27001 compliance automation for B2B SaaS. 847 companies, 98% pass on first try, 31-day average to audit-ready." />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="nav">
|
||||
<a class="wordmark" href="/">Foundry</a>
|
||||
<div class="nav__links">
|
||||
<a href="#features">Features</a>
|
||||
<a href="#customers">Customers</a>
|
||||
<a href="#pricing">Pricing</a>
|
||||
<a href="#faq">FAQ</a>
|
||||
<a class="btn btn--ghost" href="#sales">Talk to sales</a>
|
||||
<a class="btn btn--primary" href="#start">Start audit</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- ─── HERO · STAT-LED ─── -->
|
||||
<section class="hero">
|
||||
<p class="hero__eyebrow">CONTROLS · EVIDENCE · AUDIT — IN ONE PLACE</p>
|
||||
<h1 class="hero__stat" data-target="847" aria-label="847 companies passed audit using Foundry">
|
||||
<span class="hero__num" data-counter>847</span><span class="hero__period">.</span>
|
||||
</h1>
|
||||
<p class="hero__lede">
|
||||
<strong>companies passed audit using Foundry.</strong> Average time to compliant:
|
||||
<em>thirty-one days</em>, against an industry average of nine months. We wrote
|
||||
the controls, we collect the evidence, we sit on the audit-day call.
|
||||
</p>
|
||||
<div class="hero__cta">
|
||||
<a class="btn btn--primary" href="#start">Start your audit</a>
|
||||
<a class="btn btn--ghost" href="#sales">Talk to sales →</a>
|
||||
</div>
|
||||
<p class="hero__foot">
|
||||
<span>SOC 2 Type II</span> · <span>ISO 27001:2022</span> · <span>HIPAA</span> · <span>GDPR</span> · <span>PCI-DSS</span>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- ─── LOGO WALL ─── -->
|
||||
<section class="logos" aria-label="Customer companies">
|
||||
<p class="logos__label">— TRUSTED BY 847 SaaS COMPANIES, INCLUDING —</p>
|
||||
<ul class="logos__row">
|
||||
<li class="logos__item">Tariff</li>
|
||||
<li class="logos__item">Threadline</li>
|
||||
<li class="logos__item">Tracejam</li>
|
||||
<li class="logos__item">Cohort</li>
|
||||
<li class="logos__item">Maple & Co.</li>
|
||||
<li class="logos__item">Streampipe</li>
|
||||
<li class="logos__item">Beacon</li>
|
||||
<li class="logos__item">Anya Labs</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<!-- ─── SUPPORTING STATS ─── -->
|
||||
<section class="stats" aria-labelledby="stats-h">
|
||||
<h2 class="stats__h" id="stats-h">The numbers behind the number.</h2>
|
||||
<div class="stats__grid">
|
||||
<article class="stats__card">
|
||||
<span class="stats__num" data-counter data-target="98" data-suffix="%">98%</span>
|
||||
<p class="stats__label">pass on first try.</p>
|
||||
<p class="stats__caption">In 2025, 819 of 836 Foundry customers passed their first SOC 2 audit without a finding requiring remediation.</p>
|
||||
</article>
|
||||
<article class="stats__card">
|
||||
<span class="stats__num" data-counter data-target="31" data-suffix="">31</span>
|
||||
<p class="stats__label">days, on average.</p>
|
||||
<p class="stats__caption">From signature to audit-ready, measured across all customers in 2025. The fastest was nine days; the longest, sixty-two.</p>
|
||||
</article>
|
||||
<article class="stats__card">
|
||||
<span class="stats__num" data-counter data-target="12400" data-suffix=" h">12,400 h</span>
|
||||
<p class="stats__label">of engineering reclaimed.</p>
|
||||
<p class="stats__caption">Hours your team did not spend writing policies, chasing screenshots, or filling in vendor questionnaires. We did.</p>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── FEATURES ─── -->
|
||||
<section class="features" id="features" aria-labelledby="features-h">
|
||||
<header class="features__head">
|
||||
<span class="features__eyebrow">— THE WORK —</span>
|
||||
<h2 class="features__h" id="features-h">Four things Foundry does so you don't.</h2>
|
||||
</header>
|
||||
<ol class="features__grid">
|
||||
<li class="feature">
|
||||
<span class="feature__num">01</span>
|
||||
<h3 class="feature__title">Continuous monitoring.</h3>
|
||||
<p>Foundry watches your AWS, GCP, GitHub, Okta, and 80 other systems. When a control drifts — a public bucket, a missing MFA, a 90-day-old key — we flag it the same hour, not at next year's audit.</p>
|
||||
<p class="feature__detail"><b>Coverage:</b> 142 controls across SOC 2, ISO 27001, HIPAA, GDPR, PCI. <b>Latency:</b> p95 alert in 4 minutes.</p>
|
||||
</li>
|
||||
<li class="feature">
|
||||
<span class="feature__num">02</span>
|
||||
<h3 class="feature__title">Auto-evidence collection.</h3>
|
||||
<p>Screenshots, logs, access reviews, training certificates, vendor SOC 2s — Foundry pulls them on a schedule, signs them with a Sigstore-compatible cosign, and stores the bundle in evidence-of-record format. Your auditor downloads the bundle on audit day.</p>
|
||||
<p class="feature__detail"><b>Bundles ship in:</b> AICPA-accepted format, ISO 19011 evidence track, or ABNF for PCI-QSA review.</p>
|
||||
</li>
|
||||
<li class="feature">
|
||||
<span class="feature__num">03</span>
|
||||
<h3 class="feature__title">Vendor risk, automated.</h3>
|
||||
<p>Send a 12-line vendor questionnaire that Foundry scores against the vendor's published SOC 2 — or against the auditor's report, if you have it. The score updates when the vendor's posture changes. No more spreadsheet-of-vendors that nobody reads.</p>
|
||||
<p class="feature__detail"><b>Tracks:</b> 280,000 vendors with public SOC 2s. <b>Updates:</b> within 48 hours of vendor report changes.</p>
|
||||
</li>
|
||||
<li class="feature">
|
||||
<span class="feature__num">04</span>
|
||||
<h3 class="feature__title">Audit-day support.</h3>
|
||||
<p>The hardest part of the audit is not the audit — it's the back-and-forth with the auditor's analyst at 11 p.m. on a Tuesday. Foundry's Audit-Day team sits on every call, runs the evidence walk-through, and answers in your seat. You ship product the day of your audit.</p>
|
||||
<p class="feature__detail"><b>Included on:</b> Team and Scale tiers. <b>Auditor partners:</b> Prescient Assurance, Insight Assurance, A-LIGN, Schellman.</p>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<!-- ─── TESTIMONIALS ─── -->
|
||||
<section class="testimonials" id="customers" aria-labelledby="testimonials-h">
|
||||
<header class="testimonials__head">
|
||||
<span class="testimonials__eyebrow">— WHAT CUSTOMERS SAY —</span>
|
||||
<h2 class="testimonials__h" id="testimonials-h">Three CTOs, three audits, three short stories.</h2>
|
||||
</header>
|
||||
<div class="testimonials__grid">
|
||||
<figure class="quote">
|
||||
<blockquote>
|
||||
<p>Foundry got us SOC 2 in <em>five weeks.</em> We wrote zero policies ourselves. The audit-day team answered the questions our auditor asked, and we shipped a release the same afternoon.</p>
|
||||
</blockquote>
|
||||
<figcaption>
|
||||
<span class="quote__name">Rachel Tran</span>
|
||||
<span class="quote__role">CEO · Tariff</span>
|
||||
<span class="quote__meta">YC W24 · 14 employees · Pre-Series-A</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
<figure class="quote">
|
||||
<blockquote>
|
||||
<p>I have done two SOC 2 audits in my career. The first one was nine months and an outside firm. <em>The second was Foundry.</em> Twenty-three days. We did not hire anyone for it.</p>
|
||||
</blockquote>
|
||||
<figcaption>
|
||||
<span class="quote__name">Mateo Andrade</span>
|
||||
<span class="quote__role">CTO · Threadline</span>
|
||||
<span class="quote__meta">Series A · 47 employees · Engineering-led</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
<figure class="quote">
|
||||
<blockquote>
|
||||
<p>Vendor risk used to be a Notion page nobody updated. Foundry pulled in our 142 vendors, scored them against their SOC 2s, and flagged the seven we should not be using. <em>Two of them we had not even heard of.</em></p>
|
||||
</blockquote>
|
||||
<figcaption>
|
||||
<span class="quote__name">Priya Sundaram</span>
|
||||
<span class="quote__role">Head of Security · Anya Labs</span>
|
||||
<span class="quote__meta">Series B · 180 employees · Healthcare SaaS</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── PRICING ─── -->
|
||||
<section class="pricing" id="pricing" aria-labelledby="pricing-h">
|
||||
<header class="pricing__head">
|
||||
<span class="pricing__eyebrow">— PRICING —</span>
|
||||
<h2 class="pricing__h" id="pricing-h">Three tiers. Real prices. No "contact sales for pricing."</h2>
|
||||
<p class="pricing__lede">All tiers include the platform, the controls library, and the evidence collector. The differences are in audit-day support, framework count, and seat limit.</p>
|
||||
</header>
|
||||
<div class="pricing__grid">
|
||||
<article class="tier">
|
||||
<header class="tier__head">
|
||||
<h3 class="tier__name">Starter</h3>
|
||||
<p class="tier__pitch">Your first SOC 2.</p>
|
||||
</header>
|
||||
<p class="tier__price"><span class="tier__amount">$299</span><span class="tier__period">/month</span></p>
|
||||
<p class="tier__yearly">$2,990 yearly · save two months</p>
|
||||
<ul class="tier__list">
|
||||
<li>One framework (SOC 2 Type I or Type II)</li>
|
||||
<li>Up to 10 employees / 8 vendors</li>
|
||||
<li>Continuous monitoring (40 controls)</li>
|
||||
<li>Auto-evidence collection</li>
|
||||
<li>Slack + email support, business hours</li>
|
||||
<li class="tier__list-not">No audit-day team</li>
|
||||
</ul>
|
||||
<a class="btn btn--ghost-block" href="#start-starter">Start with Starter →</a>
|
||||
</article>
|
||||
|
||||
<article class="tier tier--recommended" data-pulse>
|
||||
<header class="tier__head">
|
||||
<span class="tier__badge">RECOMMENDED</span>
|
||||
<h3 class="tier__name">Team</h3>
|
||||
<p class="tier__pitch">Where most customers land.</p>
|
||||
</header>
|
||||
<p class="tier__price"><span class="tier__amount">$899</span><span class="tier__period">/month</span></p>
|
||||
<p class="tier__yearly">$8,990 yearly · save two months</p>
|
||||
<ul class="tier__list">
|
||||
<li>Up to four frameworks (SOC 2, ISO 27001, HIPAA, GDPR)</li>
|
||||
<li>Up to 50 employees / 50 vendors</li>
|
||||
<li>Continuous monitoring (all 142 controls)</li>
|
||||
<li>Auto-evidence collection</li>
|
||||
<li>Vendor risk module</li>
|
||||
<li><strong>Audit-day team on every audit</strong></li>
|
||||
<li>Slack + email + phone, 24×5</li>
|
||||
</ul>
|
||||
<a class="btn btn--primary-block" href="#start-team">Start with Team →</a>
|
||||
</article>
|
||||
|
||||
<article class="tier">
|
||||
<header class="tier__head">
|
||||
<h3 class="tier__name">Scale</h3>
|
||||
<p class="tier__pitch">For multi-entity, multi-region.</p>
|
||||
</header>
|
||||
<p class="tier__price"><span class="tier__amount">Custom</span></p>
|
||||
<p class="tier__yearly">From $2,499/month · annual contract</p>
|
||||
<ul class="tier__list">
|
||||
<li>Unlimited frameworks & entities</li>
|
||||
<li>Unlimited employees / vendors</li>
|
||||
<li>Custom controls library + custom mappings</li>
|
||||
<li>Dedicated solutions architect</li>
|
||||
<li>SAML / SCIM / Okta provisioning</li>
|
||||
<li>Audit-day team on every audit</li>
|
||||
<li>24×7 priority support</li>
|
||||
</ul>
|
||||
<a class="btn btn--ghost-block" href="#sales-scale">Talk to sales →</a>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── FAQ ─── -->
|
||||
<section class="faq" id="faq" aria-labelledby="faq-h">
|
||||
<header class="faq__head">
|
||||
<span class="faq__eyebrow">— FREQUENTLY ASKED —</span>
|
||||
<h2 class="faq__h" id="faq-h">Eight questions we get every week.</h2>
|
||||
</header>
|
||||
<dl class="faq__list">
|
||||
<div class="faq__row">
|
||||
<dt>Is Foundry the auditor?</dt>
|
||||
<dd>No. Foundry prepares your evidence and answers your audit. The audit itself is conducted by an independent CPA firm. We partner with Prescient Assurance, Insight, A-LIGN, and Schellman; you can also bring your own.</dd>
|
||||
</div>
|
||||
<div class="faq__row">
|
||||
<dt>How long does a SOC 2 audit take with Foundry?</dt>
|
||||
<dd>The 2025 median, across all 836 customers, was 31 days from signature to audit-ready. Type II observation periods are 3, 6, or 12 months on top of that, depending on what you need.</dd>
|
||||
</div>
|
||||
<div class="faq__row">
|
||||
<dt>What systems does Foundry integrate with?</dt>
|
||||
<dd>AWS, GCP, Azure, GitHub, GitLab, Okta, Google Workspace, Microsoft 365, Linear, Jira, Slack, PagerDuty, Datadog, and 80 others. The integration is read-only by default; we never make changes to your infrastructure.</dd>
|
||||
</div>
|
||||
<div class="faq__row">
|
||||
<dt>What if our codebase is on-prem?</dt>
|
||||
<dd>We support on-prem and hybrid environments via the Foundry Agent — a single Go binary that runs in your network and reports back over a single outbound TLS connection. No inbound access required.</dd>
|
||||
</div>
|
||||
<div class="faq__row">
|
||||
<dt>Can we self-host Foundry?</dt>
|
||||
<dd>On the Scale tier, yes. We ship a Helm chart that deploys to your Kubernetes cluster; we also support bare-metal via systemd units. Most Scale customers run Foundry in a dedicated VPC.</dd>
|
||||
</div>
|
||||
<div class="faq__row">
|
||||
<dt>What does "audit-day support" actually mean?</dt>
|
||||
<dd>A Foundry analyst joins every audit-day call with your auditor, runs the evidence walk-through, and answers procedural questions in your stead. You stay on the call to answer questions only your team can answer (architectural decisions, exceptions, custom controls). On a typical Type II audit, your team's total time on calls is under three hours.</dd>
|
||||
</div>
|
||||
<div class="faq__row">
|
||||
<dt>What happens if we fail the audit?</dt>
|
||||
<dd>We work the remediation list with you and re-submit. In 2025, 17 of 836 customers had findings requiring remediation; all 17 passed on the second submission within 14 days. Foundry waives the next month of fees if a remediation cycle is needed.</dd>
|
||||
</div>
|
||||
<div class="faq__row">
|
||||
<dt>Can we cancel?</dt>
|
||||
<dd>Yes — month-to-month on Starter and Team. Annual contracts on Scale are cancellable for cause with 60 days' notice. We will export your evidence vault to a self-hosted format on departure; no data hostage.</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<!-- ─── FINAL CTA ─── -->
|
||||
<section class="final-cta">
|
||||
<p class="final-cta__line">Start your audit. Fourteen days free. No credit card.</p>
|
||||
<a class="btn btn--primary final-cta__btn" href="#start">Begin in three minutes →</a>
|
||||
</section>
|
||||
|
||||
<!-- ─── FOOTER ─── -->
|
||||
<footer class="footer">
|
||||
<div class="footer__brand">
|
||||
<h3>Foundry</h3>
|
||||
<p>Compliance automation for SaaS. Built by founders who shipped two SOC 2s by hand and decided that was once too many.</p>
|
||||
<p class="footer__colophon">© 2026 · Lisbon & San Francisco · SOC 2 Type II · ISO 27001:2022</p>
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h4>Product</h4>
|
||||
<ul>
|
||||
<li><a href="#">Continuous monitoring</a></li>
|
||||
<li><a href="#">Evidence collector</a></li>
|
||||
<li><a href="#">Vendor risk</a></li>
|
||||
<li><a href="#">Audit-day team</a></li>
|
||||
<li><a href="#">Frameworks</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h4>Company</h4>
|
||||
<ul>
|
||||
<li><a href="#">About</a></li>
|
||||
<li><a href="#">Customers</a></li>
|
||||
<li><a href="#">Auditor partners</a></li>
|
||||
<li><a href="#">Careers</a></li>
|
||||
<li><a href="#">Contact</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer__col">
|
||||
<h4>Trust</h4>
|
||||
<ul>
|
||||
<li><a href="#">Foundry's SOC 2</a></li>
|
||||
<li><a href="#">Security</a></li>
|
||||
<li><a href="#">DPA & sub-processors</a></li>
|
||||
<li><a href="#">Status</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
/* ─── Microinteractions (Stat-Led default-on) ─── */
|
||||
|
||||
const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
|
||||
/* 1 · Number reveal — count from 0 to data-target on viewport entry, once. */
|
||||
function easeOut(t) { return 1 - Math.pow(1 - t, 3); }
|
||||
|
||||
function formatNumber(n, suffix) {
|
||||
const fixed = Math.round(n);
|
||||
return new Intl.NumberFormat('en-US').format(fixed) + (suffix || '');
|
||||
}
|
||||
|
||||
function animateCounter(el) {
|
||||
const target = parseInt(el.dataset.target, 10);
|
||||
const suffix = el.dataset.suffix || '';
|
||||
if (reduce || !Number.isFinite(target)) {
|
||||
el.textContent = formatNumber(target, suffix);
|
||||
return;
|
||||
}
|
||||
const duration = 1400;
|
||||
const start = performance.now();
|
||||
function tick(now) {
|
||||
const t = Math.min(1, (now - start) / duration);
|
||||
const value = target * easeOut(t);
|
||||
el.textContent = formatNumber(value, suffix);
|
||||
if (t < 1) requestAnimationFrame(tick);
|
||||
}
|
||||
requestAnimationFrame(tick);
|
||||
}
|
||||
|
||||
/* For the hero, the heading carries data-target via the parent <h1> */
|
||||
const heroHeading = document.querySelector('.hero__stat');
|
||||
if (heroHeading) {
|
||||
const heroNum = heroHeading.querySelector('[data-counter]');
|
||||
heroNum.dataset.target = heroHeading.dataset.target;
|
||||
heroNum.textContent = '0';
|
||||
}
|
||||
|
||||
/* For inline stat cards, set the start to "0" so the count is visible */
|
||||
document.querySelectorAll('.stats__num[data-counter]').forEach((el) => {
|
||||
el.textContent = '0';
|
||||
});
|
||||
|
||||
const counterIO = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
animateCounter(entry.target);
|
||||
counterIO.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.4 });
|
||||
|
||||
document.querySelectorAll('[data-counter]').forEach((el) => counterIO.observe(el));
|
||||
|
||||
/* 2 · Recommended-tier pulse — runs once on viewport entry */
|
||||
const pulseIO = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('is-pulsing');
|
||||
pulseIO.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.6 });
|
||||
document.querySelectorAll('[data-pulse]').forEach((el) => pulseIO.observe(el));
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,961 @@
|
||||
/* Hallmark · macrostructure: Stat-Led · S1 hero knobs: number=8xl, alignment=left-bias, supporting=enumerated
|
||||
* theme: Newsprint · accent: deep-red ~3% (band rules + recommended-tier)
|
||||
* enrichment: none (typography only — the number is the hero)
|
||||
* studied: no · context: explicit
|
||||
* motion: number-reveal (counter), pricing-lift, recommended-pulse — three primitives, default-on per archetype
|
||||
*/
|
||||
|
||||
@import url("https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,500;0,600;0,700;0,800;1,500&family=Crimson+Pro:ital,wght@0,400;0,500;0,600;1,400&family=IBM+Plex+Mono:wght@400;500;600&display=swap");
|
||||
|
||||
:root {
|
||||
/* Newsprint paper band — light, warm-grey */
|
||||
--paper: oklch(95% 0.012 65);
|
||||
--paper-2: oklch(92% 0.018 60);
|
||||
--paper-3: oklch(88% 0.025 55);
|
||||
--rule: oklch(72% 0.022 50);
|
||||
--rule-2: oklch(50% 0.026 40);
|
||||
--muted: oklch(46% 0.024 35);
|
||||
--neutral: oklch(34% 0.028 30);
|
||||
--ink-2: oklch(22% 0.030 28);
|
||||
--ink: oklch(15% 0.030 25);
|
||||
|
||||
/* Deep red — newsprint masthead red, roughly Pentagram-leaning */
|
||||
--accent: oklch(38% 0.18 25);
|
||||
--accent-soft: oklch(72% 0.10 25);
|
||||
--accent-bg: oklch(94% 0.030 25);
|
||||
|
||||
/* Type system. Wordmark is intentionally a different family from body. */
|
||||
--font-display: "Playfair Display", "Newsreader", ui-serif, Georgia, serif;
|
||||
--font-body: "Crimson Pro", "Newsreader", ui-serif, Georgia, serif;
|
||||
--font-label: "IBM Plex Mono", ui-monospace, monospace;
|
||||
--font-wordmark: "IBM Plex Mono", ui-monospace, monospace;
|
||||
|
||||
--space-3xs: 0.125rem;
|
||||
--space-2xs: 0.25rem;
|
||||
--space-xs: 0.5rem;
|
||||
--space-sm: 0.75rem;
|
||||
--space-md: 1rem;
|
||||
--space-lg: 1.5rem;
|
||||
--space-xl: 2.5rem;
|
||||
--space-2xl: 4rem;
|
||||
--space-3xl: 6.5rem;
|
||||
--space-4xl: 10rem;
|
||||
|
||||
--rule-hair: 0.5px;
|
||||
--rule-fine: 1px;
|
||||
--rule-bold: 2px;
|
||||
|
||||
--measure: 60ch;
|
||||
--page-max: 76rem;
|
||||
|
||||
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
|
||||
--ease-out-2: cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
html {
|
||||
font-family: var(--font-body);
|
||||
font-size: 17px;
|
||||
line-height: 1.6;
|
||||
color: var(--ink-2);
|
||||
background: var(--paper);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-feature-settings: "kern", "liga", "onum";
|
||||
overflow-x: clip;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100dvh;
|
||||
overflow-x: clip;
|
||||
}
|
||||
|
||||
a { color: inherit; }
|
||||
|
||||
img, svg { display: block; max-width: 100%; }
|
||||
|
||||
::selection { background: var(--accent); color: var(--paper); }
|
||||
|
||||
/* ─── NAV ─── */
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: var(--space-lg);
|
||||
padding: var(--space-md) clamp(1.5rem, 5vw, 3rem);
|
||||
border-block-end: var(--rule-bold) solid var(--ink);
|
||||
background: var(--paper);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.wordmark {
|
||||
font-family: var(--font-wordmark);
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
color: var(--ink);
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.wordmark::before {
|
||||
content: "▲";
|
||||
display: inline-block;
|
||||
margin-inline-end: 0.4em;
|
||||
color: var(--accent);
|
||||
font-size: 0.85em;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.nav__links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-lg);
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.nav__links a {
|
||||
color: var(--ink-2);
|
||||
text-decoration: none;
|
||||
transition: color 140ms var(--ease-out);
|
||||
}
|
||||
.nav__links a:hover { color: var(--accent); }
|
||||
.nav__links a:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 4px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
padding: 0.7rem 1.1rem;
|
||||
line-height: 1;
|
||||
border: var(--rule-fine) solid var(--ink);
|
||||
background: transparent;
|
||||
color: var(--ink);
|
||||
cursor: pointer;
|
||||
transition: background 140ms var(--ease-out), color 140ms var(--ease-out), transform 180ms var(--ease-out-2), box-shadow 180ms var(--ease-out-2);
|
||||
}
|
||||
|
||||
.btn--ghost { border-color: var(--rule); }
|
||||
.btn--ghost:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); transform: translateY(-1.5px); }
|
||||
.btn--ghost:active { transform: translateY(0); transition-duration: 60ms; }
|
||||
|
||||
.btn--primary { background: var(--ink); color: var(--paper); border-color: var(--ink); }
|
||||
.btn--primary:hover { background: var(--accent); border-color: var(--accent); transform: translateY(-1.5px); box-shadow: 0 8px 22px -12px oklch(38% 0.18 25 / 0.45); }
|
||||
.btn--primary:active { transform: translateY(0); transition-duration: 60ms; }
|
||||
|
||||
.btn--primary-block,
|
||||
.btn--ghost-block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
padding: 0.85rem 1.1rem;
|
||||
line-height: 1;
|
||||
border: var(--rule-fine) solid var(--ink);
|
||||
transition: background 140ms var(--ease-out), color 140ms var(--ease-out), transform 180ms var(--ease-out-2);
|
||||
}
|
||||
.btn--primary-block { background: var(--ink); color: var(--paper); }
|
||||
.btn--primary-block:hover { background: var(--accent); border-color: var(--accent); transform: translateY(-1.5px); }
|
||||
.btn--primary-block:active { transform: translateY(0); transition-duration: 60ms; }
|
||||
.btn--ghost-block { color: var(--ink); }
|
||||
.btn--ghost-block:hover { background: var(--ink); color: var(--paper); transform: translateY(-1.5px); }
|
||||
.btn--ghost-block:active { transform: translateY(0); transition-duration: 60ms; }
|
||||
|
||||
/* ─── HERO ─── */
|
||||
|
||||
.hero {
|
||||
padding: var(--space-4xl) clamp(1.5rem, 6vw, 4rem) var(--space-3xl);
|
||||
border-block-end: var(--rule-bold) solid var(--ink);
|
||||
display: grid;
|
||||
gap: var(--space-xl);
|
||||
max-width: var(--page-max);
|
||||
}
|
||||
|
||||
.hero__eyebrow {
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.18em;
|
||||
color: var(--accent);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.hero__stat {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: clamp(7rem, 22vw + 1rem, 22rem);
|
||||
line-height: 0.82;
|
||||
letter-spacing: -0.04em;
|
||||
color: var(--ink);
|
||||
font-variant-numeric: lining-nums tabular-nums;
|
||||
margin-block: 0;
|
||||
/* Stat-led headlines hang off the left margin */
|
||||
margin-inline-start: -0.05em;
|
||||
}
|
||||
|
||||
.hero__num {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.hero__period {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.hero__lede {
|
||||
font-family: var(--font-body);
|
||||
font-size: clamp(1.25rem, 1.4vw + 0.6rem, 1.625rem);
|
||||
line-height: 1.45;
|
||||
color: var(--ink);
|
||||
max-width: 38ch;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.hero__lede strong {
|
||||
font-weight: 600;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.hero__lede em {
|
||||
font-style: italic;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.hero__cta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-md);
|
||||
align-items: center;
|
||||
margin-block-start: var(--space-md);
|
||||
}
|
||||
|
||||
.hero__foot {
|
||||
margin-block-start: var(--space-xl);
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.4em;
|
||||
align-items: baseline;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* ─── LOGO WALL ─── */
|
||||
|
||||
.logos {
|
||||
padding: var(--space-2xl) clamp(1.5rem, 6vw, 4rem);
|
||||
border-block-end: var(--rule-fine) solid var(--rule);
|
||||
background: var(--paper-2);
|
||||
display: grid;
|
||||
gap: var(--space-lg);
|
||||
}
|
||||
|
||||
.logos__label {
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logos__row {
|
||||
list-style: none;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
gap: var(--space-lg);
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
max-width: var(--page-max);
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.logos__item {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: clamp(0.95rem, 0.5vw + 0.7rem, 1.25rem);
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--neutral);
|
||||
font-style: italic;
|
||||
white-space: nowrap;
|
||||
opacity: 0.78;
|
||||
transition: opacity 200ms var(--ease-out), color 200ms var(--ease-out);
|
||||
}
|
||||
.logos__item:hover { opacity: 1; color: var(--ink); }
|
||||
|
||||
@media (max-width: 64rem) {
|
||||
.logos__row { grid-template-columns: repeat(4, 1fr); }
|
||||
}
|
||||
@media (max-width: 36rem) {
|
||||
.logos__row { grid-template-columns: repeat(2, 1fr); }
|
||||
}
|
||||
|
||||
/* ─── STATS GRID ─── */
|
||||
|
||||
.stats {
|
||||
padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
|
||||
border-block-end: var(--rule-bold) solid var(--ink);
|
||||
display: grid;
|
||||
gap: var(--space-2xl);
|
||||
}
|
||||
|
||||
.stats__h {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: clamp(1.875rem, 2.5vw + 1rem, 3.25rem);
|
||||
line-height: 1.05;
|
||||
letter-spacing: -0.025em;
|
||||
color: var(--ink);
|
||||
max-width: 22ch;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.stats__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--space-2xl);
|
||||
}
|
||||
|
||||
.stats__card {
|
||||
display: grid;
|
||||
gap: var(--space-sm);
|
||||
padding-inline-start: var(--space-md);
|
||||
border-inline-start: var(--rule-bold) solid var(--ink);
|
||||
}
|
||||
|
||||
.stats__num {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: clamp(3rem, 5vw + 0.5rem, 5.5rem);
|
||||
line-height: 0.92;
|
||||
letter-spacing: -0.035em;
|
||||
color: var(--ink);
|
||||
font-variant-numeric: lining-nums tabular-nums;
|
||||
}
|
||||
|
||||
.stats__label {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 600;
|
||||
font-style: italic;
|
||||
font-size: clamp(1.125rem, 0.8vw + 0.75rem, 1.4375rem);
|
||||
line-height: 1.25;
|
||||
color: var(--accent);
|
||||
letter-spacing: -0.015em;
|
||||
}
|
||||
|
||||
.stats__caption {
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.55;
|
||||
color: var(--ink-2);
|
||||
max-width: 36ch;
|
||||
}
|
||||
|
||||
@media (max-width: 64rem) {
|
||||
.stats__grid { grid-template-columns: 1fr; gap: var(--space-xl); }
|
||||
}
|
||||
|
||||
/* ─── FEATURES ─── */
|
||||
|
||||
.features {
|
||||
padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
|
||||
border-block-end: var(--rule-bold) solid var(--ink);
|
||||
}
|
||||
|
||||
.features__head { margin-block-end: var(--space-2xl); max-width: 60rem; }
|
||||
|
||||
.features__eyebrow {
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--accent);
|
||||
display: block;
|
||||
margin-block-end: var(--space-md);
|
||||
}
|
||||
|
||||
.features__h {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: clamp(1.875rem, 2.5vw + 1rem, 3.25rem);
|
||||
line-height: 1.05;
|
||||
letter-spacing: -0.025em;
|
||||
color: var(--ink);
|
||||
max-width: 22ch;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.features__grid {
|
||||
list-style: none;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 0;
|
||||
border-block-start: var(--rule-fine) solid var(--rule);
|
||||
}
|
||||
|
||||
.feature {
|
||||
padding: var(--space-2xl) var(--space-xl);
|
||||
border-inline-end: var(--rule-fine) solid var(--rule);
|
||||
border-block-end: var(--rule-fine) solid var(--rule);
|
||||
display: grid;
|
||||
gap: var(--space-md);
|
||||
align-content: start;
|
||||
background: var(--paper);
|
||||
transition: background 200ms var(--ease-out);
|
||||
}
|
||||
|
||||
.feature:hover { background: var(--paper-2); }
|
||||
|
||||
.feature:nth-child(2n) { border-inline-end: 0; }
|
||||
.feature:nth-last-child(-n+2):not(:nth-child(2n+1)) { border-block-end: 0; }
|
||||
.feature:last-child:nth-child(2n+1) { border-block-end: 0; }
|
||||
|
||||
.feature__num {
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0.16em;
|
||||
color: var(--accent);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.feature__title {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: clamp(1.375rem, 1vw + 0.75rem, 1.875rem);
|
||||
line-height: 1.15;
|
||||
letter-spacing: -0.018em;
|
||||
color: var(--ink);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.feature p {
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
color: var(--ink-2);
|
||||
max-width: var(--measure);
|
||||
}
|
||||
|
||||
.feature__detail {
|
||||
margin-block-start: var(--space-xs);
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--muted);
|
||||
line-height: 1.7;
|
||||
}
|
||||
.feature__detail b { color: var(--ink); font-weight: 600; }
|
||||
|
||||
@media (max-width: 64rem) {
|
||||
.features__grid { grid-template-columns: 1fr; }
|
||||
.feature { border-inline-end: 0; border-block-end: var(--rule-fine) solid var(--rule); }
|
||||
.feature:last-child { border-block-end: 0; }
|
||||
}
|
||||
|
||||
/* ─── TESTIMONIALS ─── */
|
||||
|
||||
.testimonials {
|
||||
padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
|
||||
border-block-end: var(--rule-bold) solid var(--ink);
|
||||
background: var(--paper-2);
|
||||
display: grid;
|
||||
gap: var(--space-2xl);
|
||||
}
|
||||
|
||||
.testimonials__head { max-width: 60rem; }
|
||||
|
||||
.testimonials__eyebrow {
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--accent);
|
||||
display: block;
|
||||
margin-block-end: var(--space-md);
|
||||
}
|
||||
|
||||
.testimonials__h {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: clamp(1.875rem, 2.5vw + 1rem, 3.25rem);
|
||||
line-height: 1.05;
|
||||
letter-spacing: -0.025em;
|
||||
color: var(--ink);
|
||||
max-width: 24ch;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.testimonials__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--space-xl);
|
||||
}
|
||||
|
||||
.quote {
|
||||
display: grid;
|
||||
gap: var(--space-lg);
|
||||
padding: var(--space-xl);
|
||||
background: var(--paper);
|
||||
border-block-start: var(--rule-bold) solid var(--accent);
|
||||
border-inline-start: var(--rule-fine) solid var(--rule);
|
||||
border-inline-end: var(--rule-fine) solid var(--rule);
|
||||
border-block-end: var(--rule-fine) solid var(--rule);
|
||||
}
|
||||
|
||||
.quote blockquote {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 500;
|
||||
font-style: italic;
|
||||
font-size: 1.1875rem;
|
||||
line-height: 1.45;
|
||||
letter-spacing: -0.012em;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.quote blockquote em {
|
||||
background: var(--accent-bg);
|
||||
font-style: italic;
|
||||
padding: 0 0.12em;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.quote blockquote::before {
|
||||
content: "“";
|
||||
display: block;
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: 3rem;
|
||||
line-height: 0.8;
|
||||
color: var(--accent);
|
||||
margin-block-end: var(--space-sm);
|
||||
}
|
||||
|
||||
.quote figcaption {
|
||||
display: grid;
|
||||
gap: var(--space-3xs);
|
||||
border-block-start: var(--rule-fine) solid var(--rule);
|
||||
padding-block-start: var(--space-md);
|
||||
}
|
||||
|
||||
.quote__name {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: 1rem;
|
||||
color: var(--ink);
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
|
||||
.quote__role {
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--accent);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.quote__meta {
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
@media (max-width: 64rem) {
|
||||
.testimonials__grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
/* ─── PRICING ─── */
|
||||
|
||||
.pricing {
|
||||
padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
|
||||
border-block-end: var(--rule-bold) solid var(--ink);
|
||||
display: grid;
|
||||
gap: var(--space-2xl);
|
||||
}
|
||||
|
||||
.pricing__head { max-width: 60rem; }
|
||||
|
||||
.pricing__eyebrow {
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--accent);
|
||||
display: block;
|
||||
margin-block-end: var(--space-md);
|
||||
}
|
||||
|
||||
.pricing__h {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: clamp(1.875rem, 2.5vw + 1rem, 3.25rem);
|
||||
line-height: 1.05;
|
||||
letter-spacing: -0.025em;
|
||||
color: var(--ink);
|
||||
max-width: 26ch;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.pricing__lede {
|
||||
margin-block-start: var(--space-md);
|
||||
font-size: 1.0625rem;
|
||||
line-height: 1.55;
|
||||
color: var(--ink-2);
|
||||
max-width: 60ch;
|
||||
}
|
||||
|
||||
.pricing__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0;
|
||||
border-block-start: var(--rule-fine) solid var(--rule);
|
||||
border-inline-start: var(--rule-fine) solid var(--rule);
|
||||
}
|
||||
|
||||
.tier {
|
||||
padding: var(--space-xl);
|
||||
background: var(--paper);
|
||||
border-inline-end: var(--rule-fine) solid var(--rule);
|
||||
border-block-end: var(--rule-fine) solid var(--rule);
|
||||
display: grid;
|
||||
grid-template-rows: auto auto auto 1fr auto;
|
||||
gap: var(--space-lg);
|
||||
position: relative;
|
||||
transition: transform 180ms var(--ease-out-2), box-shadow 180ms var(--ease-out-2), background 180ms var(--ease-out);
|
||||
}
|
||||
|
||||
.tier:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 14px 30px -18px oklch(20% 0.05 30 / 0.18);
|
||||
background: var(--paper);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.tier:active {
|
||||
transform: translateY(0);
|
||||
transition-duration: 60ms;
|
||||
}
|
||||
|
||||
.tier--recommended {
|
||||
background: var(--paper);
|
||||
outline: var(--rule-bold) solid var(--accent);
|
||||
outline-offset: -2px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tier--recommended.is-pulsing {
|
||||
animation: pulse-border 2s var(--ease-out) 1;
|
||||
}
|
||||
|
||||
@keyframes pulse-border {
|
||||
0% { outline-color: oklch(38% 0.18 25 / 0.4); }
|
||||
50% { outline-color: oklch(38% 0.18 25 / 1); }
|
||||
100% { outline-color: oklch(38% 0.18 25 / 0.6); }
|
||||
}
|
||||
|
||||
.tier__head { display: grid; gap: var(--space-3xs); }
|
||||
|
||||
.tier__badge {
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--paper);
|
||||
background: var(--accent);
|
||||
padding: 0.3em 0.7em;
|
||||
width: max-content;
|
||||
margin-block-end: var(--space-xs);
|
||||
}
|
||||
|
||||
.tier__name {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: clamp(1.5rem, 1vw + 0.75rem, 2rem);
|
||||
letter-spacing: -0.015em;
|
||||
color: var(--ink);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.tier__pitch {
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.tier__price {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: var(--space-xs);
|
||||
font-family: var(--font-display);
|
||||
border-block-start: var(--rule-fine) solid var(--rule);
|
||||
padding-block-start: var(--space-md);
|
||||
}
|
||||
|
||||
.tier__amount {
|
||||
font-weight: 700;
|
||||
font-size: clamp(2.5rem, 2vw + 1rem, 3.25rem);
|
||||
letter-spacing: -0.03em;
|
||||
color: var(--ink);
|
||||
font-variant-numeric: lining-nums tabular-nums;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.tier__period {
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.85rem;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--muted);
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
.tier__yearly {
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--accent);
|
||||
margin-block-start: calc(-1 * var(--space-xs));
|
||||
}
|
||||
|
||||
.tier__list {
|
||||
list-style: none;
|
||||
display: grid;
|
||||
gap: var(--space-sm);
|
||||
font-size: 0.9375rem;
|
||||
line-height: 1.45;
|
||||
color: var(--ink-2);
|
||||
}
|
||||
|
||||
.tier__list li { padding-inline-start: 1.4em; position: relative; }
|
||||
.tier__list li::before {
|
||||
content: "✓";
|
||||
position: absolute;
|
||||
inset-inline-start: 0;
|
||||
color: var(--accent);
|
||||
font-weight: 600;
|
||||
}
|
||||
.tier__list li strong { color: var(--ink); font-weight: 600; }
|
||||
|
||||
.tier__list-not { color: var(--muted) !important; }
|
||||
.tier__list-not::before { content: "—" !important; color: var(--muted) !important; }
|
||||
|
||||
@media (max-width: 64rem) {
|
||||
.pricing__grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
/* ─── FAQ ─── */
|
||||
|
||||
.faq {
|
||||
padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
|
||||
border-block-end: var(--rule-bold) solid var(--ink);
|
||||
}
|
||||
|
||||
.faq__head { margin-block-end: var(--space-2xl); max-width: 60rem; }
|
||||
|
||||
.faq__eyebrow {
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--accent);
|
||||
display: block;
|
||||
margin-block-end: var(--space-md);
|
||||
}
|
||||
|
||||
.faq__h {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: clamp(1.875rem, 2.5vw + 1rem, 3.25rem);
|
||||
line-height: 1.05;
|
||||
letter-spacing: -0.025em;
|
||||
color: var(--ink);
|
||||
max-width: 22ch;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.faq__list {
|
||||
display: grid;
|
||||
border-block-start: var(--rule-fine) solid var(--rule);
|
||||
}
|
||||
|
||||
.faq__row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(20rem, 0.9fr) 1.4fr;
|
||||
gap: var(--space-2xl);
|
||||
padding-block: var(--space-xl);
|
||||
border-block-end: var(--rule-fine) solid var(--rule);
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.faq__row dt {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 600;
|
||||
font-size: clamp(1.125rem, 0.8vw + 0.75rem, 1.4375rem);
|
||||
line-height: 1.25;
|
||||
letter-spacing: -0.012em;
|
||||
color: var(--ink);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.faq__row dd {
|
||||
font-family: var(--font-body);
|
||||
font-size: 1.0625rem;
|
||||
line-height: 1.6;
|
||||
color: var(--ink-2);
|
||||
max-width: var(--measure);
|
||||
}
|
||||
|
||||
@media (max-width: 64rem) {
|
||||
.faq__row { grid-template-columns: 1fr; gap: var(--space-md); }
|
||||
}
|
||||
|
||||
/* ─── FINAL CTA ─── */
|
||||
|
||||
.final-cta {
|
||||
padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
|
||||
background: var(--ink);
|
||||
color: var(--paper);
|
||||
display: grid;
|
||||
gap: var(--space-xl);
|
||||
align-items: center;
|
||||
justify-items: start;
|
||||
}
|
||||
|
||||
.final-cta__line {
|
||||
font-family: var(--font-display);
|
||||
font-weight: 700;
|
||||
font-size: clamp(1.875rem, 3vw + 0.5rem, 3.25rem);
|
||||
line-height: 1.05;
|
||||
letter-spacing: -0.025em;
|
||||
color: var(--paper);
|
||||
font-style: italic;
|
||||
max-width: 24ch;
|
||||
}
|
||||
|
||||
.final-cta__btn {
|
||||
background: var(--paper);
|
||||
color: var(--ink);
|
||||
border-color: var(--paper);
|
||||
padding: 1rem 1.6rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.final-cta__btn:hover {
|
||||
background: var(--accent);
|
||||
color: var(--paper);
|
||||
border-color: var(--accent);
|
||||
transform: translateY(-1.5px);
|
||||
}
|
||||
|
||||
/* ─── FOOTER ─── */
|
||||
|
||||
.footer {
|
||||
padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
|
||||
display: grid;
|
||||
grid-template-columns: 2fr repeat(3, 1fr);
|
||||
gap: var(--space-2xl);
|
||||
background: var(--paper);
|
||||
border-block-start: var(--rule-bold) solid var(--ink);
|
||||
}
|
||||
|
||||
.footer__brand h3 {
|
||||
font-family: var(--font-wordmark);
|
||||
font-weight: 600;
|
||||
font-size: 1.125rem;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
color: var(--ink);
|
||||
margin-block-end: var(--space-md);
|
||||
}
|
||||
|
||||
.footer__brand p {
|
||||
color: var(--ink-2);
|
||||
max-width: 38ch;
|
||||
line-height: 1.55;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.footer__brand .footer__colophon {
|
||||
margin-block-start: var(--space-md);
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.footer__col h4 {
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--accent);
|
||||
font-weight: 500;
|
||||
margin-block-end: var(--space-md);
|
||||
}
|
||||
|
||||
.footer__col ul { list-style: none; display: grid; gap: 0.45em; }
|
||||
|
||||
.footer__col a {
|
||||
color: var(--ink-2);
|
||||
text-decoration: none;
|
||||
font-family: var(--font-label);
|
||||
font-size: 0.85rem;
|
||||
letter-spacing: 0.02em;
|
||||
transition: color 140ms var(--ease-out);
|
||||
}
|
||||
.footer__col a:hover { color: var(--accent); }
|
||||
|
||||
@media (max-width: 56rem) {
|
||||
.footer { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
|
||||
@media (max-width: 36rem) {
|
||||
.footer { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
/* ─── REDUCED MOTION ─── */
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
animation: none !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
.tier:hover { transform: none; }
|
||||
.btn--primary:hover, .btn--ghost:hover, .final-cta__btn:hover,
|
||||
.btn--primary-block:hover, .btn--ghost-block:hover { transform: none; }
|
||||
}
|
||||
|
||||
/* ─── FOCUS-VISIBLE FALLBACK (anywhere we missed) ─── */
|
||||
|
||||
a:focus-visible, button:focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 3px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
# Test 08 — Cohort (cohort-based courses platform)
|
||||
|
||||
## The prompt
|
||||
|
||||
> "Build a landing page for Cohort — the platform for cohort-based courses. Run live courses with 30 to 500 students. Built for educators, not LMS sales teams. Audience: course operators + indie creators. Tone: warm, salon-room, editorial."
|
||||
|
||||
## Context gate — fully answered
|
||||
|
||||
Audience, use, tone all explicit.
|
||||
|
||||
## Domain inference
|
||||
|
||||
Education-tech / creator-tool / B2B-light SaaS. The brief is *operator-facing* (educators run courses), not student-facing. From the macrostructure catalogue: **Marquee Hero · Long Document · Workbench** are reasonable picks for "creator tool with showcase work."
|
||||
|
||||
The skill picks **Marquee Hero** because the brief implicitly says "show me the courses already running." A continuous-scroll band of course titles + instructors is the proof — and it does the social-proof job without a separate logo wall section.
|
||||
|
||||
## Theme
|
||||
|
||||
Last test was Foundry on **Newsprint** (light · roman-serif · warm-deep-red). Tracejam before was **Pastel** (light · geometric-sans · cool-indigo). Diversification axes for the next theme:
|
||||
|
||||
| Theme | Paper band | Display style | Accent hue |
|
||||
| --- | --- | --- | --- |
|
||||
| Pastel (Tracejam) | light | geometric-sans | cool-indigo |
|
||||
| Newsprint (Foundry) | light | roman-serif | warm-deep-red |
|
||||
| **Salon (Cohort)** | **light** | **mono** | **warm-amber** |
|
||||
|
||||
vs Foundry: paper-band same · display style differs · accent same family but different hue (deep-red vs amber).
|
||||
vs Pastel: paper-band same · display style differs · accent differs.
|
||||
|
||||
Two of three differ from Foundry. Three of three differ from Pastel (counting hue tint). **Passes.**
|
||||
|
||||
Salon as a theme: light warm paper, mono display (IBM Plex Mono), warm amber accent, generous spacing, centered emphasis. The unusual choice — mono display on a course-platform marketing page — gives Cohort the "letterpress-y, instructor-room" feel the brief asked for ("salon-room, editorial").
|
||||
|
||||
## Enrichment
|
||||
|
||||
The marquee IS the enrichment. **No additional mockup, no illustration.** The continuous scroll of course titles is what makes the page feel inhabited. Per `hero-enrichment.md`, this is enrichment archetype **Animated Loop (E6)** delivered via continuous CSS marquee — Tier A custom-craft, no Lottie.
|
||||
|
||||
## Microinteractions
|
||||
|
||||
Cohort is a Marquee Hero — **default-on archetype** per the new microinteractions rule. Three primitives, max:
|
||||
|
||||
1. **Marquee scroll** — continuous CSS `@keyframes marquee` translateX(-100%) over 50 s, infinite. Pauses on hover. Reduced-motion: scroll stops, first three items shown.
|
||||
2. **Stagger reveal on testimonials** — IntersectionObserver fires on each card; 100 ms stagger; opacity 0 → 1 + translateY(8px → 0); ease-out 400 ms. One-shot.
|
||||
3. **Pricing card hover lift** — same recipe as Foundry: `translateY(-3px)` + shadow upgrade, 180 ms ease-out.
|
||||
|
||||
CTA hover-lift is the existing `microinteractions.md` recipe and counts as 0 primitives — it's button polish, not page-level motion.
|
||||
|
||||
## Macrostructure stamp
|
||||
|
||||
```
|
||||
/* Hallmark · macrostructure: Marquee Hero · H6 hero knobs: speed=slow, content=titles+authors, fade=both-edges
|
||||
* theme: Salon · accent: warm-amber ~3% · enrichment: E6 animated loop (continuous marquee, Tier-A)
|
||||
* studied: no · context: explicit
|
||||
* motion: marquee, stagger-reveal, pricing-lift — three primitives, default-on per archetype
|
||||
*/
|
||||
```
|
||||
|
||||
## What the page does
|
||||
|
||||
1. **Hero (Marquee).** Continuous-scroll band of course titles + instructor names above. Headline below: "Cohort runs live courses with 30 to 500 students." Two CTAs.
|
||||
2. **Three-step process.** Set a date · Invite students · Teach live. Numbered, conversational.
|
||||
3. **Features (split, alternating).** Live cohorts · Office hours · Async chat · Cohort analytics. Each row alternates the screenshot side.
|
||||
4. **Testimonials (4 in 2×2).** Instructor + course name. Specific, voice-distinct.
|
||||
5. **Pricing (2 tiers).** Indie ($59/mo) · Team ($199/mo). Salon-style: italic-mono display, italic prices, centered.
|
||||
6. **FAQ (6 questions).** Two-column conversational FAQ.
|
||||
7. **Final CTA strip.** "Run your first cohort by next month."
|
||||
8. **Footer.** Centered Salon footer, single-line nav + colophon.
|
||||
|
||||
The page is intentionally long — second demonstration of the SaaS page sequence rule, this time in a Marquee Hero shape rather than Stat-Led.
|
||||
@@ -0,0 +1,405 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Cohort — run live courses with 30 to 500 students</title>
|
||||
<meta name="description" content="The platform for cohort-based courses. Set a date. Invite students. Teach live. Built for educators, not LMS sales teams." />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="nav">
|
||||
<a class="wordmark" href="/">Cohort<span class="wordmark__mark">.</span></a>
|
||||
<div class="nav__links">
|
||||
<a href="#how">How it works</a>
|
||||
<a href="#voices">Instructors</a>
|
||||
<a href="#pricing">Pricing</a>
|
||||
<a href="#faq">FAQ</a>
|
||||
<a class="btn btn--ghost" href="#sign-in">Sign in</a>
|
||||
<a class="btn btn--primary" href="#start">Start a cohort →</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- ─── HERO · MARQUEE ─── -->
|
||||
<section class="hero">
|
||||
<div class="marquee" aria-label="Currently running on Cohort">
|
||||
<div class="marquee__track" data-marquee>
|
||||
<ul class="marquee__list">
|
||||
<li><em>Build a SaaS in 30 days</em> · Sahil Lavingia</li>
|
||||
<li><em>Writing Strong</em> · David Perell</li>
|
||||
<li><em>Notion for Operators</em> · Marie Poulin</li>
|
||||
<li><em>The Cold Email Course</em> · Alex Berman</li>
|
||||
<li><em>How to Read a Painting</em> · Camille Walala</li>
|
||||
<li><em>Ship a Newsletter in 14 days</em> · Lia Haberman</li>
|
||||
<li><em>Practical TypeScript</em> · Matt Pocock</li>
|
||||
<li><em>The Public Speaking Cohort</em> · Vinh Giang</li>
|
||||
<li><em>Founders' Finance</em> · Lola Priego</li>
|
||||
<li><em>Print & Production</em> · Hassan Rahim</li>
|
||||
</ul>
|
||||
<ul class="marquee__list" aria-hidden="true">
|
||||
<li><em>Build a SaaS in 30 days</em> · Sahil Lavingia</li>
|
||||
<li><em>Writing Strong</em> · David Perell</li>
|
||||
<li><em>Notion for Operators</em> · Marie Poulin</li>
|
||||
<li><em>The Cold Email Course</em> · Alex Berman</li>
|
||||
<li><em>How to Read a Painting</em> · Camille Walala</li>
|
||||
<li><em>Ship a Newsletter in 14 days</em> · Lia Haberman</li>
|
||||
<li><em>Practical TypeScript</em> · Matt Pocock</li>
|
||||
<li><em>The Public Speaking Cohort</em> · Vinh Giang</li>
|
||||
<li><em>Founders' Finance</em> · Lola Priego</li>
|
||||
<li><em>Print & Production</em> · Hassan Rahim</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hero__copy">
|
||||
<p class="hero__eyebrow">— A salon room for thirty to five hundred —</p>
|
||||
<h1 class="hero__title">
|
||||
Cohort runs <em>live courses</em> the way your favourite teacher would —<br />
|
||||
with a date, a roster, and a room.
|
||||
</h1>
|
||||
<p class="hero__lede">
|
||||
Set the date. Invite the students. Teach live. We handle the room, the chat,
|
||||
the recordings, the certificates, and the long tail. You handle the teaching —
|
||||
which, as it happens, is the part you got into this for.
|
||||
</p>
|
||||
<div class="hero__cta">
|
||||
<a class="btn btn--primary" href="#start">Run your first cohort →</a>
|
||||
<a class="btn btn--ghost" href="#tour">Take the 90-second tour</a>
|
||||
</div>
|
||||
<p class="hero__foot">
|
||||
Trusted by 4,200 instructors · 380,000 students · 47 countries · since 2024
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── HOW IT WORKS ─── -->
|
||||
<section class="how" id="how" aria-labelledby="how-h">
|
||||
<header class="section__head">
|
||||
<span class="section__eyebrow">— THREE STEPS —</span>
|
||||
<h2 class="section__h" id="how-h">From idea to first cohort in a Sunday afternoon.</h2>
|
||||
</header>
|
||||
<ol class="how__steps">
|
||||
<li class="step">
|
||||
<span class="step__num">01</span>
|
||||
<h3 class="step__title">Set a date.</h3>
|
||||
<p>Pick a start, a duration (four weeks, six weeks, twelve weeks — your call), and a meeting cadence. Cohort writes the syllabus skeleton from your one-line pitch. You revise the syllabus, not the calendar.</p>
|
||||
</li>
|
||||
<li class="step">
|
||||
<span class="step__num">02</span>
|
||||
<h3 class="step__title">Invite the students.</h3>
|
||||
<p>Share a link, an audience export from Substack or ConvertKit, or a Twitter list. Cohort writes the seat-confirmation email, runs the welcome sequence, and shows you who paid, who's confirmed, and who's still on the fence.</p>
|
||||
</li>
|
||||
<li class="step">
|
||||
<span class="step__num">03</span>
|
||||
<h3 class="step__title">Teach live.</h3>
|
||||
<p>Open the room three minutes before. Cohort records, captions, and bookmarks the moments students react to. The replay is in the cohort feed within an hour. Office hours, Q&A, and async chat are already wired.</p>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<!-- ─── FEATURES (alternating) ─── -->
|
||||
<section class="features" aria-labelledby="features-h">
|
||||
<header class="section__head section__head--centered">
|
||||
<span class="section__eyebrow">— THE SUITE —</span>
|
||||
<h2 class="section__h" id="features-h">A suite that respects the craft of teaching.</h2>
|
||||
<p class="section__lede">No "engagement gamification." No drip-funnel. The four things a teacher actually needs to run a course in 2026:</p>
|
||||
</header>
|
||||
|
||||
<div class="feature-row">
|
||||
<div class="feature-row__copy">
|
||||
<span class="feature-row__num">I.</span>
|
||||
<h3>Live cohorts.</h3>
|
||||
<p>Recurring sessions that look like Zoom but feel like a salon room. Hand-raise, breakout pods, side-channel chat that doesn't drown the speaker. Recordings auto-publish to the cohort feed with chapter markers from the moment students reacted.</p>
|
||||
<p class="feature-row__detail"><b>Limit:</b> 500 live attendees per session. <b>Recording:</b> 1080p, captioned, hosted on Cohort or your own S3. <b>Latency:</b> p95 under 240 ms across most regions.</p>
|
||||
</div>
|
||||
<div class="feature-row__visual" aria-hidden="true">
|
||||
<figure class="card-stack card-stack--live">
|
||||
<div class="card-stack__layer card-stack__layer--3">
|
||||
<span class="dot dot--green"></span>
|
||||
<span>Live · 287 in the room</span>
|
||||
</div>
|
||||
<div class="card-stack__layer card-stack__layer--2">
|
||||
<span>Session 04 · Friday · 18:00 GMT</span>
|
||||
</div>
|
||||
<div class="card-stack__layer card-stack__layer--1">
|
||||
<p>"Now — open the prompt I sent on Monday. We're going to revise it together."</p>
|
||||
<span class="card-stack__attrib">— Sahil, instructor</span>
|
||||
</div>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="feature-row feature-row--reverse">
|
||||
<div class="feature-row__copy">
|
||||
<span class="feature-row__num">II.</span>
|
||||
<h3>Office hours, by request.</h3>
|
||||
<p>Students book 1:1 or 1:few slots from your published availability. Cohort honours your timezone and your calendar; you don't need to manage a Calendly. Slot history, attendance, and follow-up notes are saved against each student.</p>
|
||||
<p class="feature-row__detail"><b>Booking:</b> 5-minute granularity. <b>Buffers:</b> auto. <b>No-shows:</b> tracked but not penalised — the teacher decides the policy.</p>
|
||||
</div>
|
||||
<div class="feature-row__visual" aria-hidden="true">
|
||||
<figure class="hours">
|
||||
<div class="hours__week">
|
||||
<div class="hours__day"><span>Mon</span><i class="hours__slot is-booked"></i><i class="hours__slot is-booked"></i></div>
|
||||
<div class="hours__day"><span>Tue</span><i class="hours__slot"></i><i class="hours__slot is-booked"></i></div>
|
||||
<div class="hours__day"><span>Wed</span><i class="hours__slot is-booked"></i><i class="hours__slot is-booked"></i><i class="hours__slot is-booked"></i></div>
|
||||
<div class="hours__day"><span>Thu</span><i class="hours__slot"></i><i class="hours__slot is-booked"></i></div>
|
||||
<div class="hours__day"><span>Fri</span><i class="hours__slot is-booked"></i><i class="hours__slot is-booked"></i></div>
|
||||
</div>
|
||||
<p class="hours__caption">Eight booked · Four available · This week.</p>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="feature-row">
|
||||
<div class="feature-row__copy">
|
||||
<span class="feature-row__num">III.</span>
|
||||
<h3>Async chat that doesn't read like a forum.</h3>
|
||||
<p>Threaded by topic, not by post order. Students reply in their own time; instructors mark the canonical answer. The rare slack-spam of "thanks!" and "+1" is collapsed by default. The signal stays.</p>
|
||||
<p class="feature-row__detail"><b>Notifications:</b> per-cohort digest, never per-message. <b>Search:</b> full-text + canonical-answers-only mode. <b>Export:</b> Markdown.</p>
|
||||
</div>
|
||||
<div class="feature-row__visual" aria-hidden="true">
|
||||
<figure class="thread">
|
||||
<div class="thread__msg">
|
||||
<span class="thread__author">Marie</span>
|
||||
<p>Should the database state be in Notion or Linear?</p>
|
||||
</div>
|
||||
<div class="thread__msg thread__msg--reply">
|
||||
<span class="thread__author">Sahil <span class="thread__badge">canonical</span></span>
|
||||
<p>Notion for the source of truth. Linear for what's in flight this week. The duplication is the feature, not the bug.</p>
|
||||
</div>
|
||||
<div class="thread__msg thread__msg--reply">
|
||||
<span class="thread__author">Lia</span>
|
||||
<p>Confirms — three cohorts in, this is the only setup that doesn't break.</p>
|
||||
</div>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="feature-row feature-row--reverse">
|
||||
<div class="feature-row__copy">
|
||||
<span class="feature-row__num">IV.</span>
|
||||
<h3>Cohort analytics, the honest kind.</h3>
|
||||
<p>What percent of students finished? Which session hit hardest? What's the NPS, by name? No vanity metrics. No dashboard you have to interpret. The numbers you'd ask for over coffee, on one page, every Monday morning.</p>
|
||||
<p class="feature-row__detail"><b>Tracks:</b> attendance, completion, Q&A volume, NPS, retention to the next cohort. <b>Doesn't track:</b> click-throughs on the welcome email — nobody cares.</p>
|
||||
</div>
|
||||
<div class="feature-row__visual" aria-hidden="true">
|
||||
<figure class="report">
|
||||
<p class="report__num">87%</p>
|
||||
<p class="report__label">finished the cohort</p>
|
||||
<ul class="report__list">
|
||||
<li><span>Avg. attendance</span><b>92%</b></li>
|
||||
<li><span>NPS (n = 47)</span><b>+71</b></li>
|
||||
<li><span>Retain to next</span><b>34%</b></li>
|
||||
<li><span>"Strongest moment"</span><b>Session 04</b></li>
|
||||
</ul>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── TESTIMONIALS ─── -->
|
||||
<section class="voices" id="voices" aria-labelledby="voices-h">
|
||||
<header class="section__head section__head--centered">
|
||||
<span class="section__eyebrow">— FROM THE TEACHERS —</span>
|
||||
<h2 class="section__h" id="voices-h">Four short letters from instructors who switched.</h2>
|
||||
</header>
|
||||
<div class="voices__grid" data-stagger>
|
||||
<figure class="voice">
|
||||
<blockquote>
|
||||
<p>I ran my course on three different platforms before this. Cohort is the only one where the chat doesn't drown out the lesson. I teach to the room now, not to the messages.</p>
|
||||
</blockquote>
|
||||
<figcaption>
|
||||
<span class="voice__name">Marie Poulin</span>
|
||||
<span class="voice__role">Instructor · Notion for Operators</span>
|
||||
<span class="voice__meta">Eight cohorts · 2,400 students · since 2024</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
<figure class="voice">
|
||||
<blockquote>
|
||||
<p>The thing that sold me was the post-cohort report. One page, on a Monday morning, telling me what hit and what didn't. I plan the next cohort from that page. I plan no other way now.</p>
|
||||
</blockquote>
|
||||
<figcaption>
|
||||
<span class="voice__name">Sahil Lavingia</span>
|
||||
<span class="voice__role">Instructor · Build a SaaS in 30 days</span>
|
||||
<span class="voice__meta">Twelve cohorts · 3,800 students · since 2024</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
<figure class="voice">
|
||||
<blockquote>
|
||||
<p>I was terrified of going live. Cohort made the live part the easy part. The room is in your timezone. The recording is up before you've finished your tea. I have never had a tech failure.</p>
|
||||
</blockquote>
|
||||
<figcaption>
|
||||
<span class="voice__name">Lia Haberman</span>
|
||||
<span class="voice__role">Instructor · Ship a Newsletter</span>
|
||||
<span class="voice__meta">Six cohorts · 1,100 students · since 2025</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
<figure class="voice">
|
||||
<blockquote>
|
||||
<p>My students keep coming back. That used to be a metric I didn't trust because the platforms inflate it. Cohort just shows you who returned, by name. Thirty-four percent of mine did. I cried a little.</p>
|
||||
</blockquote>
|
||||
<figcaption>
|
||||
<span class="voice__name">Hassan Rahim</span>
|
||||
<span class="voice__role">Instructor · Print & Production</span>
|
||||
<span class="voice__meta">Three cohorts · 320 students · since 2025</span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── PRICING ─── -->
|
||||
<section class="pricing" id="pricing" aria-labelledby="pricing-h">
|
||||
<header class="section__head section__head--centered">
|
||||
<span class="section__eyebrow">— PRICING —</span>
|
||||
<h2 class="section__h" id="pricing-h">Two tiers. Real prices. Indie or team.</h2>
|
||||
<p class="section__lede">Ten percent of what you charge, capped. Or a flat per-month for teams. Pick the one that's smaller this month.</p>
|
||||
</header>
|
||||
<div class="pricing__grid">
|
||||
<article class="tier">
|
||||
<h3 class="tier__name"><em>Indie</em></h3>
|
||||
<p class="tier__pitch">For solo instructors and small cohorts.</p>
|
||||
<p class="tier__price">
|
||||
<span class="tier__amount">$59</span>
|
||||
<span class="tier__period">/month</span>
|
||||
</p>
|
||||
<p class="tier__yearly">+ 10% on revenue, capped at $500/cohort</p>
|
||||
<ul class="tier__list">
|
||||
<li>One instructor seat</li>
|
||||
<li>Up to 200 students per cohort</li>
|
||||
<li>Unlimited cohorts per year</li>
|
||||
<li>Live sessions · Office hours · Async chat</li>
|
||||
<li>Cohort analytics · Email digests</li>
|
||||
<li>Stripe payouts (you keep the relationship)</li>
|
||||
</ul>
|
||||
<a class="btn btn--ghost-block" href="#start-indie">Start as Indie →</a>
|
||||
</article>
|
||||
|
||||
<article class="tier tier--recommended">
|
||||
<span class="tier__badge">RECOMMENDED FOR TEAMS</span>
|
||||
<h3 class="tier__name"><em>Team</em></h3>
|
||||
<p class="tier__pitch">For studios, schools, and creator collectives.</p>
|
||||
<p class="tier__price">
|
||||
<span class="tier__amount">$199</span>
|
||||
<span class="tier__period">/month</span>
|
||||
</p>
|
||||
<p class="tier__yearly">flat — no revenue cut</p>
|
||||
<ul class="tier__list">
|
||||
<li>Up to 8 instructor seats</li>
|
||||
<li>Up to 500 students per cohort</li>
|
||||
<li>Custom-branded cohort domain</li>
|
||||
<li>Live · Office hours · Async · <strong>Replay vault</strong></li>
|
||||
<li>Cohort analytics + studio-wide rollup</li>
|
||||
<li>Priority support · onboarding call</li>
|
||||
</ul>
|
||||
<a class="btn btn--primary-block" href="#start-team">Start as Team →</a>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── FAQ ─── -->
|
||||
<section class="faq" id="faq" aria-labelledby="faq-h">
|
||||
<header class="section__head section__head--centered">
|
||||
<span class="section__eyebrow">— ANSWERS —</span>
|
||||
<h2 class="section__h" id="faq-h">Six questions instructors ask before they switch.</h2>
|
||||
</header>
|
||||
<div class="faq__grid">
|
||||
<div class="faq__row">
|
||||
<h3>Do you take a cut of my revenue?</h3>
|
||||
<p>On Indie, ten percent — capped at $500 per cohort. On Team, no — flat $199/month. Picking is easy: if you are charging more than $5,000 per cohort, Team is cheaper. If you are charging less, Indie is cheaper.</p>
|
||||
</div>
|
||||
<div class="faq__row">
|
||||
<h3>Can I bring my own audience?</h3>
|
||||
<p>Yes. Import from Substack, ConvertKit, Beehiiv, Mailchimp, a Twitter list, or a CSV. Cohort matches by email and welcomes them with your copy, not ours.</p>
|
||||
</div>
|
||||
<div class="faq__row">
|
||||
<h3>What happens between cohorts?</h3>
|
||||
<p>Students keep access to recordings, chat, and office hours from cohorts they attended. You can run the next cohort whenever you like — no monthly minimum, no "must launch quarterly."</p>
|
||||
</div>
|
||||
<div class="faq__row">
|
||||
<h3>Do you provide the live video?</h3>
|
||||
<p>We do. Hosted on Cloudflare for low-latency global delivery; recorded automatically; transcribed and chapter-marked. You can plug in Zoom or Riverside if you prefer; we'll ingest the recording back into the cohort feed.</p>
|
||||
</div>
|
||||
<div class="faq__row">
|
||||
<h3>Can I export my course?</h3>
|
||||
<p>Anytime. Markdown for written content, MP4 for video, JSON for the roster. Your audience is yours; we just host the room.</p>
|
||||
</div>
|
||||
<div class="faq__row">
|
||||
<h3>What about students who can't make the live session?</h3>
|
||||
<p>The recording is up within the hour, captioned, with chapter markers from the live reactions. Async chat keeps the conversation going. Most cohorts run with 30–40% of students attending live and still hit 85%+ completion.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ─── FINAL CTA ─── -->
|
||||
<section class="final-cta">
|
||||
<p class="final-cta__line"><em>Run your first cohort by next month.</em></p>
|
||||
<a class="btn btn--primary final-cta__btn" href="#start">Set the date →</a>
|
||||
<p class="final-cta__sub">No credit card. Two-week trial on the room itself.</p>
|
||||
</section>
|
||||
|
||||
<!-- ─── FOOTER ─── -->
|
||||
<footer class="footer">
|
||||
<div class="footer__top">
|
||||
<a class="footer__brand" href="/">Cohort<span class="wordmark__mark">.</span></a>
|
||||
<ul class="footer__nav">
|
||||
<li><a href="#">How it works</a></li>
|
||||
<li><a href="#">Instructors</a></li>
|
||||
<li><a href="#">Pricing</a></li>
|
||||
<li><a href="#">FAQ</a></li>
|
||||
<li><a href="#">Help centre</a></li>
|
||||
<li><a href="#">Status</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<p class="footer__colophon">
|
||||
© 2026 · Made by a small team in Lisbon, Berlin, and Mexico City · For instructors, not LMS sales managers.
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
/* ─── Microinteractions (Marquee Hero default-on) ─── */
|
||||
|
||||
const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
|
||||
/* 1 · Marquee — pause on hover, stop on reduced-motion */
|
||||
document.querySelectorAll('[data-marquee]').forEach((el) => {
|
||||
if (reduce) {
|
||||
el.style.animation = 'none';
|
||||
el.style.transform = 'none';
|
||||
}
|
||||
el.addEventListener('mouseenter', () => el.classList.add('is-paused'));
|
||||
el.addEventListener('mouseleave', () => el.classList.remove('is-paused'));
|
||||
/* Pause when off-screen for battery */
|
||||
const visIO = new IntersectionObserver(([entry]) => {
|
||||
if (entry.isIntersecting) el.classList.remove('is-offscreen');
|
||||
else el.classList.add('is-offscreen');
|
||||
}, { threshold: 0 });
|
||||
visIO.observe(el);
|
||||
});
|
||||
|
||||
/* 2 · Stagger reveal on testimonials — one-shot via IntersectionObserver */
|
||||
if (!reduce) {
|
||||
const staggerIO = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
const items = entry.target.querySelectorAll('.voice');
|
||||
items.forEach((el, i) => {
|
||||
el.style.animationDelay = (i * 100) + 'ms';
|
||||
el.classList.add('is-revealed');
|
||||
});
|
||||
staggerIO.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.2 });
|
||||
document.querySelectorAll('[data-stagger]').forEach((el) => staggerIO.observe(el));
|
||||
} else {
|
||||
/* Reduced motion: just show them immediately */
|
||||
document.querySelectorAll('.voice').forEach((el) => el.classList.add('is-revealed'));
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,15 +1,17 @@
|
||||
# Hallmark — generation tests
|
||||
|
||||
Six landing pages produced by exercising the Hallmark skill across contrasting briefs. The point: see what comes out *without* hand-holding the AI on theme, structure, or enrichment. Each test deliberately tests a different scenario.
|
||||
Eight landing pages produced by exercising the Hallmark skill across contrasting briefs. The point: see what comes out *without* hand-holding the AI on theme, structure, or enrichment. Each test deliberately tests a different scenario. Tests 01–06 ship with no enrichment or simple Tier-A/B custom-craft; tests 07 and 08 demonstrate the **default-on microinteractions** rule (animated counter, marquee, stagger reveal) and the **SaaS page sequence** (testimonials, pricing, FAQ).
|
||||
|
||||
| # | Brief | Context given | Theme requested | What the skill picked |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 01 | Indie podcast `Tide`, no detail | **Skipped** ("you pick") | none | Quote-Led + Atelier + no enrichment |
|
||||
| 02 | Open-source CLI `Streampipe` for stream parsing | Full (devs / install + docs / technical) | **Terminal** (requested) | Workbench + Terminal + Tier-A CSS-art terminal mockup |
|
||||
| 03 | Artisan bakery `Maple Street Bread` | Full (locals / browse + visit / warm hand-set) | none | Long Document + Linen + Tier-B hand-built SVG loaf |
|
||||
| 04 | Manifesto for an environmental studio `Meridian` | Partial ("declarative, no flashy stuff") | none (implied) | Manifesto + Manifesto + no enrichment |
|
||||
| 04 | Manifesto for an environmental studio `Meridian` | Partial ("declarative, no flashy stuff") | none (implied) | Manifesto + Manifesto + no enrichment · 11 sections (manifesto + principles + practice + reading) |
|
||||
| 05 | SaaS observability tool `Tracejam` | Full (SREs / try-or-talk-to-sales / technical) | none | Bento Grid + Pastel + E1 clipped-edge dashboard mockup |
|
||||
| 06 | Personal one-pager for `Anya` (software architect) | **Skipped** ("don't ask, just figure it out") | none | Long Document + Studio + no enrichment |
|
||||
| 07 | SOC2 / ISO27001 compliance SaaS `Foundry` | Full (founders + CTOs / try-or-talk-to-sales / technical-but-trustworthy) | none | Stat-Led + Newsprint + animated counter + pricing pulse · 9-section SaaS sequence |
|
||||
| 08 | Cohort-based courses platform `Cohort` | Full (educators / run-courses / warm-salon-room) | none | Marquee Hero + Salon + continuous marquee + stagger reveal · 8-section SaaS sequence |
|
||||
|
||||
Each folder contains:
|
||||
|
||||
@@ -86,3 +88,52 @@ Hallmark is *good*. The structural variety position holds — six briefs, six di
|
||||
The remaining gap is **copy voice** and **theme-pick discipline**. The slop test catches *visual* tells well; it doesn't yet catch *prose* tells (like "Built for the modern team" headlines). And the diversification rules are tight on one axis (no Specimen fall-through) but loose on the rest.
|
||||
|
||||
Tier 1 of this list is the next session's work.
|
||||
|
||||
---
|
||||
|
||||
## v0.5.0 — Tier 1, 2, and most of Tier 3 — DONE
|
||||
|
||||
Items 1–11 and 13 from the improvement list above shipped in commit `b61f1ef`. Specifically:
|
||||
|
||||
- ✅ **#1 Per-archetype copy fixtures** in `macrostructures.md` — 21 archetypes × 2–3 sample opening lines.
|
||||
- ✅ **#2 Sub-domain refinement** of the trio table in `structure.md`.
|
||||
- ✅ **#3 Theme-diversification rule** in `SKILL.md` — paper-band, display-style, accent-hue.
|
||||
- ✅ **#4 Free-vs-paid font trade-off** in `typography.md` — every tone has both rows.
|
||||
- ✅ **#5 Worked examples per enrichment archetype** in `hero-enrichment.md`.
|
||||
- ✅ **#6 Different-knobs slop-test gate** (gate 34).
|
||||
- ✅ **#7 Four illustration recipes** in `custom-craft.md`.
|
||||
- ✅ **#8 Non-LLM voice samples** in `copy.md` — 7 tones × 3 patterns × 5 samples.
|
||||
- ✅ **#9 Mobile-collapse discipline** in `component-cookbook.md`.
|
||||
- ✅ **#10 aria-label slop-test gate** (gate 35).
|
||||
- ✅ **#11 Project memory** `.hallmark/log.json`.
|
||||
- ✅ **#13 Example gallery** on the landing page (text-only at the time).
|
||||
- 🟡 **#12 study + redesign combined verb** — deferred.
|
||||
- 🟡 **#14 auto-generated OG cards** — deferred.
|
||||
|
||||
---
|
||||
|
||||
## v0.6.0 — Tracejam fixes + SaaS expansion + visual gallery
|
||||
|
||||
### What shipped
|
||||
|
||||
1. **Tracejam (test 05) defects fixed.** Horizontal scroll suppressed via `html/body { overflow-x: clip }`. Navbar items vertically centered with `align-items: center` and `line-height: 1`. Wordmark switched from Geist (body family) to Fraunces (`--font-wordmark`). Hero highlight raised from baseline to mid-x-height — the band now reads as a highlighter mark, not a fat underline. Browser-frame chrome dots got `aria-hidden="true"`.
|
||||
2. **Three new slop-test gates** (now 38 total). Gate 36: no horizontal scroll on any viewport 320–1920 px. Gate 37: decorative effects on text must be visually verified to sit at x-height, not baseline. Gate 38: interactive bars must declare `align-items: center` + `line-height: 1`.
|
||||
3. **Default-on microinteractions rule** in `microinteractions.md`. For Bento Grid, Stat-Led, Workbench, Marquee Hero, and Conversational FAQ archetypes, the skill now ships 2–3 small purposeful microinteractions automatically — number reveal on stats, pricing card lift, marquee scroll, stagger reveal — without the user asking. Default-off archetypes (Editorial, Manifesto, Letter, Quote-Led, Long Document) stay still.
|
||||
4. **SaaS page sequence** in `macrostructures.md`. Hero → social proof → features → testimonials → pricing → FAQ → CTA → footer — when the macrostructure is SaaS-typical and the brief is B2B SaaS marketing.
|
||||
5. **Wordmark guidance** in `typography.md`. The wordmark may (and on Bento / Stat-Led / Workbench / Marquee Hero, *should*) use a different display face than the body. Free pairings listed.
|
||||
6. **Page-edge clipping discipline** in `layout-and-space.md`. Always pair clipped-edge enrichment with `html/body { overflow-x: clip }`. Use `clip` not `hidden` to preserve sticky positioning.
|
||||
7. **Meridian Manifesto (test 04) extended** from 7 sections to 11. New sections: § What we refuse (anti-list with strikethrough), § Working rules (numbered operational principles), § The practice (3-paragraph narrative with drop-cap), § What we read (annotated bibliography). Voice consistent with the original manifesto; no testimonials, no pricing, no CTA.
|
||||
8. **Two new SaaS-focused tests** demonstrating the new rules end-to-end:
|
||||
- **Test 07 — Foundry** (Stat-Led + Newsprint). Hero is one giant number ("847" — animated counter from 0). 9 sections including testimonials, real pricing tiers, FAQ. Pricing card hover-lift + recommended-tier pulse-once.
|
||||
- **Test 08 — Cohort** (Marquee Hero + Salon). Continuous-scroll marquee of course titles. Alternating-side feature rows with hand-built CSS visualisations. Stagger-reveal on testimonials.
|
||||
9. **Real visual thumbnails on the example gallery.** `site/_tests/_thumbs/*.png` — eight 1024×640 PNG screenshots, one per test. Gallery cards now show a real preview above the text. Cards inherit theme tokens (radius, rule, shadow) so switching the landing page's theme reskins the card frames around the screenshots.
|
||||
10. **README updates.** Slop-test count 35 → 38. New default-on microinteractions and SaaS-recipe bullets in "What's distinct."
|
||||
|
||||
### Skipped from this round
|
||||
|
||||
- **#12 study + redesign combined verb** — still deferred. Single-shot screenshot-to-rebuilt-page workflow.
|
||||
- **#14 auto-generated OG cards** — still deferred. Needs Node + Puppeteer utility.
|
||||
|
||||
### What this round means for the skill
|
||||
|
||||
The structural variety position holds across **eight** tests now, not six. The skill shipped its first two long-form pages (Foundry and Cohort) with proper SaaS structure — testimonials with role + company, real pricing, conversational FAQ — and they read distinctly per macrostructure, not just look distinct. The microinteractions are appropriate (counter, lift, marquee) and bounded to three primitives per page. The Tracejam regressions are gated against in three new slop-test questions.
|
||||
|
||||
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 132 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 110 KiB |
@@ -1696,11 +1696,13 @@
|
||||
.gallery__item {
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
"thumb thumb"
|
||||
"num name"
|
||||
"num shape"
|
||||
"num desc"
|
||||
"num open";
|
||||
grid-template-columns: 4rem 1fr;
|
||||
grid-template-rows: auto repeat(4, auto);
|
||||
gap: 0 var(--space-md);
|
||||
padding: var(--space-lg);
|
||||
text-decoration: none;
|
||||
@@ -1713,6 +1715,7 @@
|
||||
border-color var(--dur-micro) var(--ease-out),
|
||||
box-shadow var(--dur-short) var(--ease-out);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
@@ -1727,6 +1730,38 @@
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.gallery__thumb {
|
||||
grid-area: thumb;
|
||||
display: block;
|
||||
margin: calc(-1 * var(--space-lg)) calc(-1 * var(--space-lg)) var(--space-lg);
|
||||
border-block-end: var(--rule-card, 1px) solid var(--color-rule);
|
||||
background: var(--color-paper-2);
|
||||
aspect-ratio: 16 / 10;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.gallery__thumb img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: top center;
|
||||
display: block;
|
||||
transition: transform 500ms cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
.gallery__item:hover .gallery__thumb img,
|
||||
.gallery__item:focus-visible .gallery__thumb img {
|
||||
transform: scale(1.025);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.gallery__thumb img { transition: none; }
|
||||
.gallery__item:hover .gallery__thumb img { transform: none; }
|
||||
}
|
||||
|
||||
.gallery__num {
|
||||
grid-area: num;
|
||||
font-family: var(--font-label);
|
||||
|
||||
@@ -410,15 +410,18 @@
|
||||
<span class="num">07</span><span class="divider">⁄</span><span>Examples</span>
|
||||
</p>
|
||||
<h2 class="section__title" id="s7">
|
||||
Six pages, six different shapes.
|
||||
Eight pages, eight different shapes.
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<p class="examples__intro">Six landing pages produced by exercising the skill across contrasting briefs. Each page is its own self-contained HTML + CSS — no shared theme, no shared layout. Every one carries a <code>/* Hallmark · macrostructure: … */</code> stamp at the top of its CSS.</p>
|
||||
<p class="examples__intro">Eight landing pages produced by exercising the skill across contrasting briefs. Each page is its own self-contained HTML + CSS — no shared theme, no shared layout. Every one carries a <code>/* Hallmark · macrostructure: … */</code> stamp at the top of its CSS. Hover any thumbnail to see the page open in a new tab.</p>
|
||||
|
||||
<div class="gallery" role="list">
|
||||
|
||||
<a class="gallery__item" href="_tests/01-tide-podcast/index.html" role="listitem">
|
||||
<picture class="gallery__thumb" aria-hidden="true">
|
||||
<img src="_tests/_thumbs/01-tide.png" alt="" loading="lazy" decoding="async" width="1024" height="640" />
|
||||
</picture>
|
||||
<span class="gallery__num">01</span>
|
||||
<span class="gallery__name">Tide</span>
|
||||
<span class="gallery__shape">Quote-Led · Atelier · no enrichment</span>
|
||||
@@ -427,6 +430,9 @@
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/02-streampipe-cli/index.html" role="listitem">
|
||||
<picture class="gallery__thumb" aria-hidden="true">
|
||||
<img src="_tests/_thumbs/02-streampipe.png" alt="" loading="lazy" decoding="async" width="1024" height="640" />
|
||||
</picture>
|
||||
<span class="gallery__num">02</span>
|
||||
<span class="gallery__name">Streampipe</span>
|
||||
<span class="gallery__shape">Workbench · Terminal · Tier-A CSS-art mockup</span>
|
||||
@@ -435,6 +441,9 @@
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/03-maple-bakery/index.html" role="listitem">
|
||||
<picture class="gallery__thumb" aria-hidden="true">
|
||||
<img src="_tests/_thumbs/03-maple.png" alt="" loading="lazy" decoding="async" width="1024" height="640" />
|
||||
</picture>
|
||||
<span class="gallery__num">03</span>
|
||||
<span class="gallery__name">Maple Street Bread</span>
|
||||
<span class="gallery__shape">Long Document · Linen · Tier-B hand-built SVG loaf</span>
|
||||
@@ -443,14 +452,20 @@
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/04-meridian-manifesto/index.html" role="listitem">
|
||||
<picture class="gallery__thumb" aria-hidden="true">
|
||||
<img src="_tests/_thumbs/04-meridian.png" alt="" loading="lazy" decoding="async" width="1024" height="640" />
|
||||
</picture>
|
||||
<span class="gallery__num">04</span>
|
||||
<span class="gallery__name">Meridian</span>
|
||||
<span class="gallery__shape">Manifesto · Manifesto · no enrichment</span>
|
||||
<span class="gallery__desc">Studio manifesto. Pure black, oversized white display, single red bleed band.</span>
|
||||
<span class="gallery__shape">Manifesto · Manifesto · principles + practice + reading</span>
|
||||
<span class="gallery__desc">Studio manifesto. Pure black, oversized white display, eleven sections.</span>
|
||||
<span class="gallery__open">View ↗</span>
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/05-tracejam-saas/index.html" role="listitem">
|
||||
<picture class="gallery__thumb" aria-hidden="true">
|
||||
<img src="_tests/_thumbs/05-tracejam.png" alt="" loading="lazy" decoding="async" width="1024" height="640" />
|
||||
</picture>
|
||||
<span class="gallery__num">05</span>
|
||||
<span class="gallery__name">Tracejam</span>
|
||||
<span class="gallery__shape">Bento Grid · Pastel · clipped-edge dashboard</span>
|
||||
@@ -459,6 +474,9 @@
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/06-anya-portfolio/index.html" role="listitem">
|
||||
<picture class="gallery__thumb" aria-hidden="true">
|
||||
<img src="_tests/_thumbs/06-anya.png" alt="" loading="lazy" decoding="async" width="1024" height="640" />
|
||||
</picture>
|
||||
<span class="gallery__num">06</span>
|
||||
<span class="gallery__name">Anya</span>
|
||||
<span class="gallery__shape">Long Document · Studio · no enrichment</span>
|
||||
@@ -466,6 +484,28 @@
|
||||
<span class="gallery__open">View ↗</span>
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/07-foundry-compliance/index.html" role="listitem">
|
||||
<picture class="gallery__thumb" aria-hidden="true">
|
||||
<img src="_tests/_thumbs/07-foundry.png" alt="" loading="lazy" decoding="async" width="1024" height="640" />
|
||||
</picture>
|
||||
<span class="gallery__num">07</span>
|
||||
<span class="gallery__name">Foundry</span>
|
||||
<span class="gallery__shape">Stat-Led · Newsprint · animated counter + pricing pulse</span>
|
||||
<span class="gallery__desc">SOC2 compliance SaaS. Hero is one giant number. Testimonials, pricing, FAQ — full sequence.</span>
|
||||
<span class="gallery__open">View ↗</span>
|
||||
</a>
|
||||
|
||||
<a class="gallery__item" href="_tests/08-cohort-courses/index.html" role="listitem">
|
||||
<picture class="gallery__thumb" aria-hidden="true">
|
||||
<img src="_tests/_thumbs/08-cohort.png" alt="" loading="lazy" decoding="async" width="1024" height="640" />
|
||||
</picture>
|
||||
<span class="gallery__num">08</span>
|
||||
<span class="gallery__name">Cohort</span>
|
||||
<span class="gallery__shape">Marquee Hero · Salon · continuous-scroll + stagger reveal</span>
|
||||
<span class="gallery__desc">Cohort-based courses platform. Marquee of live courses, alternating features, salon tone.</span>
|
||||
<span class="gallery__open">View ↗</span>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
<p class="examples__outro">Findings + improvement list at <a href="_tests/" class="link">site/_tests/</a>.</p>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: hallmark
|
||||
description: Use this skill when the user asks to design, build, redesign, audit, refine, or study a UI, web page, landing page, dashboard, component, or interface — or when they ask to make something "feel less AI-generated." Hallmark forces intentional design decisions (typography, color, layout, motion, interaction, structure) and refuses to default to the generic AI-UI template. Trigger phrases include "design a", "build a landing page", "make a dashboard", "redesign this site", "redesign the page", "refine this UI", "audit this design", "this looks AI-generated", "fix the design", "polish this", "give this a different look", and any request that will produce HTML / CSS / JSX / Tailwind output. **Also trigger when the user attaches a screenshot of a design they admire** — that is the `hallmark study` verb (extracts design DNA, never pixel-clones).
|
||||
version: 0.5.0
|
||||
version: 0.6.0
|
||||
---
|
||||
|
||||
# Hallmark
|
||||
@@ -156,7 +156,7 @@ Always:
|
||||
|
||||
### 6. The slop test
|
||||
|
||||
Before handing back, run the output through these thirty-five questions. Every answer must be **no**.
|
||||
Before handing back, run the output through these thirty-eight questions. Every answer must be **no**.
|
||||
|
||||
**Visual:**
|
||||
|
||||
@@ -214,6 +214,12 @@ Before handing back, run the output through these thirty-five questions. Every a
|
||||
34. If I used the same archetype as a previous Hallmark output (per `.hallmark/log.json` or the latest macrostructure stamp), did I pick at least one different *variation knob*? Two Bento Grids with `tiles=6, spans=irregular, accent=corner-only` are the same Bento — the within-archetype knobs in [`component-cookbook.md`](references/component-cookbook.md) exist precisely to prevent that. State the knob deltas in the stamp.
|
||||
35. Does any visual-only `<svg>`, custom-art `<div>`, `<canvas>`, or decorative figure lack `aria-label` or `aria-hidden="true"`? Hand-built CSS art and SVG illustrations need an accessible name *or* an explicit hide. Skipping this is the new accessibility tell.
|
||||
|
||||
**Layout-safety gates** (the page must survive every viewport):
|
||||
|
||||
36. Does the page horizontally scroll on any viewport between 320 px and 1920 px? Open the rendered page; drag the dev-tools width slider across that range. If a horizontal scrollbar appears at any width, fail. The fix is `html { overflow-x: clip; }` plus `body { overflow-x: clip; }` as a safety net for any clipped-edge enrichment that pushes past the viewport. Use `overflow-x: clip` (not `hidden`) — `clip` preserves `position: sticky` and `position: fixed` on descendants. (Cross-reference: [`layout-and-space.md` § Page-edge clipping](references/layout-and-space.md).)
|
||||
37. For every decorative effect on text — highlighter `<mark>` / `<em>` band / accent stroke / underline — did I visually confirm the position and size? A highlighter band must sit behind the x-height (`linear-gradient(180deg, transparent ~38%, accent ~38%, accent ~92%, transparent ~92%)`), **not** at the baseline (which reads as a fat underline). Underlines must be 1–2 px and offset 1–2 px from the baseline, never 5+ px. Decorative strokes must not exceed 5 % of the viewport (gate 25). The check is *visual*: imagine the rendered output and confirm the band lands in the right vertical zone.
|
||||
38. Are interactive bars (nav, toolbar, command bar, hero CTA row, footer link strip) explicitly vertically centered? Default flex layouts inherit `align-items: stretch`, which makes a button taller than its sibling text and breaks the visual baseline. Every flex row mixing height-different elements (button + text, icon + text, mark + body) must declare `align-items: center` and `line-height: 1` on the items with intrinsic height. Inheriting `line-height: 1.55` from `html` fights the row's vertical rhythm.
|
||||
|
||||
If any answer is yes, fix it. Do not ship slop.
|
||||
|
||||
---
|
||||
|
||||
@@ -78,6 +78,28 @@ Layout is where "AI-generated" gets caught. Equal columns, everything centred, e
|
||||
- **`z-index: 9999`** and other ad-hoc z values. Use the scale.
|
||||
- **Shadow-on-dark accidental glow.** A drop shadow on a dark card creates a glow; that's wrong.
|
||||
|
||||
## Page-edge clipping
|
||||
|
||||
The clipped-edge enrichment archetype (E1) — and any other deliberately overflowing element (full-bleed marquee, oversized headline that exceeds the viewport on small screens, a tilted figure that pushes past a column) — needs a parent that *visually* shows the overflow without letting the document scroll horizontally.
|
||||
|
||||
The default is unsafe: a `width: calc(100% + 12vw)` figure inside a section with `overflow: visible` makes the document scroll horizontally on every viewport. The page feels broken on touch devices. Slop-test gate 36 fails on this.
|
||||
|
||||
**Always pair clipped-edge with a global clip.** At the top of the stylesheet:
|
||||
|
||||
```css
|
||||
html { overflow-x: clip; }
|
||||
body { overflow-x: clip; } /* fallback for older Safari */
|
||||
```
|
||||
|
||||
Use `overflow-x: clip` rather than `overflow-x: hidden`:
|
||||
|
||||
- `clip` preserves `position: sticky` and `position: fixed` on descendants.
|
||||
- `hidden` creates a new scroll container, which breaks sticky and can trap focus on overflowing inputs.
|
||||
|
||||
The clipped-edge mockup keeps its visual extension; the page no longer scrolls horizontally. Same pattern works for full-bleed marquees, oversized headlines, and any deliberately-decorative overflow.
|
||||
|
||||
The hero or section that *contains* the overflowing element keeps `overflow: visible` (so the figure renders past the parent edge); the global clip on `html` and `body` is the only safety net needed.
|
||||
|
||||
## When in doubt
|
||||
|
||||
If the layout looks fine but flat, do one of these before shipping:
|
||||
|
||||
@@ -613,6 +613,34 @@ Reference: shadcn/ui, Tailwind UI, Once UI, MUI demos, Framer Motion examples.
|
||||
|
||||
---
|
||||
|
||||
## SaaS page sequence
|
||||
|
||||
When the macrostructure is **Bento Grid · Stat-Led · Workbench · Marquee Hero** and the brief is a B2B SaaS marketing page, ship these sections in roughly this order. None are mandatory — but skipping more than two reads as "the page is incomplete":
|
||||
|
||||
1. **Hero** — macrostructure-specific (Bento, Stat, Workbench, Marquee). Two CTAs (primary action + secondary "Talk to sales").
|
||||
2. **Social proof / logo wall** — 6–8 customer logos in monochrome. (See [`assets.md` § Logo walls](assets.md).)
|
||||
3. **Features** — 3–6 feature cards, varies by macrostructure (Bento has them inline; Stat-Led usually puts them after the supporting-stats grid).
|
||||
4. **Testimonials** — 2–4 quote cards. Pull-quote + name + role + company. Photo optional. Avoid "We use [product] every day" language; the quote should be specific to a use case ("Foundry got us SOC2 in five weeks. We wrote zero policies ourselves.").
|
||||
5. **Pricing** — 2–3 tiers in a comparison table. Feature checklist per tier. Recommended-tier badge on the middle tier. Show the actual price; "Contact sales for pricing" on every tier is a tell that the brand doesn't trust the buyer.
|
||||
6. **FAQ** — 5–10 questions. Conversational FAQ archetype works here (see Macrostructure 9).
|
||||
7. **Final CTA strip** — single button + one-sentence prompt.
|
||||
8. **Footer** — index-style or tabular, theme-appropriate.
|
||||
|
||||
Each section transition uses theme-appropriate vertical spacing — `--space-3xl` minimum between major sections. Don't subdivide sections into "rows" with sub-rules — the section break is the visual rhythm.
|
||||
|
||||
**Voice rules for SaaS sections:**
|
||||
|
||||
- **Pricing:** show the actual price. Sales-led pricing on every tier ("Contact us") signals the brand doesn't trust the buyer.
|
||||
- **Testimonials:** include the quoted person's role *and* company. Abstract "Engineering Manager" testimonials are slop. If the brief is a real product, use real names. If the brief is a placeholder, use plausible names — never "Jane Doe" / "John Smith" (gate 20).
|
||||
- **FAQ:** answer like a person, not a sales doc. "Yes — Stripe and Adyen are both supported out of the box" beats "Our platform integrates with leading payment providers."
|
||||
- **CTA strip:** one button. Not two. The repetition is the call to action.
|
||||
|
||||
This sequence is **not** a template you stamp out — it's a recipe of *what should be present*. The macrostructure determines *how* each section looks. A Bento Grid page interleaves features and proof inside the grid; a Stat-Led page sequences them top-to-bottom; a Marquee Hero page lets the marquee do the social-proof work.
|
||||
|
||||
For non-SaaS work (Editorial, Manifesto, Letter, Long Document, Quote-Led), this sequence does **not** apply. A bakery does not need a pricing tier comparison.
|
||||
|
||||
---
|
||||
|
||||
## How to pick
|
||||
|
||||
1. **Read the brief.** Note any words that strongly signal one macrostructure ("data heavy", "tell a story", "a list of links", "many small features", "personal note").
|
||||
|
||||
@@ -13,6 +13,45 @@ A microinteraction is one event with four parts: trigger → rules → feedback
|
||||
- **Reduced motion is a first-class state, not an afterthought.** Every interaction defines its reduced-motion behaviour explicitly. Default is: collapse spatial motion to opacity crossfade, keep duration ≤ 150ms, preserve functional state changes.
|
||||
- **Keyboard first, hover second.** Every hover affordance has a focus equivalent. No interaction is hover-only.
|
||||
|
||||
## When to ship motion by default
|
||||
|
||||
The skill biases toward motion-cut. But certain archetypes feel **broken without** motion — they're so visually busy (or so number-led) that complete stillness reads as a screenshot rather than an interface. For these archetypes, ship 2–3 small purposeful microinteractions automatically, without waiting for the user to ask.
|
||||
|
||||
**Default-on archetypes:** Bento Grid · Stat-Led · Workbench · Marquee Hero · Conversational FAQ
|
||||
|
||||
**Default-off archetypes:** Editorial · Manifesto · Letter · Quote-Led · Type Specimen · Long Document · Index-First · Letter
|
||||
|
||||
For default-on, pick **two or three** from this menu (never more than three primitives per page):
|
||||
|
||||
| Microinteraction | When to ship | Recipe |
|
||||
| --- | --- | --- |
|
||||
| **Number reveal** | Stat-Led hero, headline numbers anywhere | IntersectionObserver fires on viewport entry; `requestAnimationFrame` counts from 0 to target over 1.2–1.6 s with `--ease-out`. Reduced-motion: skip animation, render final value. |
|
||||
| **Pricing card lift** | Pricing tier cards | `translateY(-3px)` + shadow upgrade on `:hover`, 180 ms `--ease-out`. Active state: drop back to `translateY(0)` over 60 ms (the press). |
|
||||
| **CTA hover lift** | Primary CTA buttons | `translateY(-1.5px)` + background-fade. 200 ms `--ease-out`. Active state at 60 ms. |
|
||||
| **Marquee scroll** | Marquee Hero, customer-logo strip | `@keyframes marquee` `translateX(-100%)` over 40–60 s, infinite. Pauses on hover. Reduced-motion: stops the scroll, shows the first three items. |
|
||||
| **Stagger reveal** | Testimonials, feature cards, gallery | IntersectionObserver fires on each card; 100 ms stagger; opacity 0 → 1 + `translateY(8px → 0)`; `--ease-out` 400 ms. **One-shot only — never re-fires on scroll.** |
|
||||
| **Recommended-tier pulse** | The middle pricing tier | One-shot `@keyframes pulse-border` 2 s, runs once on viewport entry. Subtle: opacity 0.4 → 1 → 0.4 on the border. Don't loop. |
|
||||
| **Caret blink** | Terminal hero, code mockup | `@keyframes blink` 1 s steps(2) infinite on a 1ch-wide block. Reduced-motion: solid block, no blink. |
|
||||
| **Number tick on data update** | Dashboard live values | See *Number tick* recipe below. |
|
||||
|
||||
### Hard rules for default-on motion
|
||||
|
||||
1. Every animation respects `prefers-reduced-motion: reduce` — either skip entirely or run at 0.01 s.
|
||||
2. **No more than three distinct animation primitives per page.** A counter + a hover-lift + a marquee = three. Don't add a fourth. The temptation to layer "just one more" is the slop pull.
|
||||
3. No scroll-linked animations on viewports below 40 rem (existing mobile rule, [`component-cookbook.md` § Mobile collapse](component-cookbook.md)).
|
||||
4. No animation longer than 2 s except continuous loops (marquee, ambient breathing, caret blink).
|
||||
5. The "if I removed this animation, would anyone notice?" test still applies — but for the default-on set, the answer is "yes, the page would feel screenshot-stiff and the brand would feel thin."
|
||||
|
||||
### What never gets default motion
|
||||
|
||||
- Body text reveals on scroll. Reading is not a cinematic experience.
|
||||
- Background gradient shifts. Distracting.
|
||||
- Cursor followers. Always slop.
|
||||
- Section-by-section fade-up-stagger. Pick one orchestrated entrance, not twelve.
|
||||
- Tab content sliding sideways. Crossfade only (see Tab change recipe below).
|
||||
|
||||
When the page is default-off (Editorial, Manifesto, etc.), motion is *opt-in* — the user must ask. Stillness is the brand on those pages.
|
||||
|
||||
## The timing canon
|
||||
|
||||
Pick from these durations. Do not invent new ones.
|
||||
|
||||
@@ -45,6 +45,39 @@ Each tone gets two rows: a **free baseline** (everything Google-Fonts-or-similar
|
||||
|
||||
**The discipline.** Default to the free pairings. They're not consolation prizes; Fraunces, Geist, Bricolage Grotesque, and JetBrains Mono are first-rate faces in 2026. The paid upgrades exist for two cases: (a) the user has explicitly confirmed they're licensed, or (b) the user is asking for a specific named foundry voice (e.g., "make it look like Klim", "I want Söhne"). Reach for Tier 2 only then; otherwise the free row is the right answer. Treat the free row as canon, the paid row as a *cited* alternative.
|
||||
|
||||
## Wordmark / logo typography
|
||||
|
||||
The wordmark in the navbar and footer **may use a different display face than the body**. On tone-rich themes (Editorial, Salon, Atelier, Linen, Quiet) it **should** — collapsing the wordmark into the body family flattens the visual hierarchy and the page reads as un-branded.
|
||||
|
||||
```css
|
||||
:root {
|
||||
--display: "Geist", system-ui, sans-serif; /* body + display */
|
||||
--font-wordmark: "Fraunces", Georgia, serif; /* logo only */
|
||||
}
|
||||
.wordmark {
|
||||
font-family: var(--font-wordmark);
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.015em;
|
||||
}
|
||||
```
|
||||
|
||||
Recommended pairings (free baseline first):
|
||||
|
||||
- **Geist body → Fraunces wordmark, IBM Plex Mono wordmark, or Bricolage Grotesque (heavy) wordmark**
|
||||
- **Fraunces body → Geist Mono wordmark, Inter Tight wordmark**
|
||||
- **System-ui body → JetBrains Mono wordmark, Newsreader wordmark**
|
||||
- **Inter Tight body → Fraunces wordmark, EB Garamond wordmark**
|
||||
|
||||
When to use the same family for both:
|
||||
|
||||
- **Editorial · Letter · Manifesto · Long Document** can collapse to a single family because the body voice carries the brand. The wordmark in these contexts is small, grounded, and earns its weight by being typeset rather than decorated.
|
||||
|
||||
When to use a contrasting family:
|
||||
|
||||
- **Bento Grid · Stat-Led · Workbench · Marquee Hero** — these archetypes lean visually generic (geometric grids, big numbers, browser-frame mockups) and need the wordmark to do the typographic differentiation work the body can't.
|
||||
|
||||
**Avoid the same-family collapse on a SaaS page.** A Geist-only page where the wordmark is also Geist 600 reads as un-designed; the wordmark in Fraunces SemiBold over a Geist body costs nothing and adds the one typographic register that says *this is a brand*.
|
||||
|
||||
## Scale
|
||||
|
||||
Pick a ratio. The default for Hallmark work is **1.25** (major third). Build the scale from a 16px body, then clamp display sizes for responsive.
|
||||
|
||||