diff --git a/site/_tests/05-tracejam-saas/style.css b/site/_tests/05-tracejam-saas/style.css
index 85a9747..3dd5e93 100644
--- a/site/_tests/05-tracejam-saas/style.css
+++ b/site/_tests/05-tracejam-saas/style.css
@@ -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 {
diff --git a/site/_tests/07-foundry-compliance/brief.md b/site/_tests/07-foundry-compliance/brief.md
new file mode 100644
index 0000000..580ff39
--- /dev/null
+++ b/site/_tests/07-foundry-compliance/brief.md
@@ -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.
diff --git a/site/_tests/07-foundry-compliance/index.html b/site/_tests/07-foundry-compliance/index.html
new file mode 100644
index 0000000..6348863
--- /dev/null
+++ b/site/_tests/07-foundry-compliance/index.html
@@ -0,0 +1,378 @@
+
+
+
+
+
+
Foundry — SOC2 in 31 days, not 9 months
+
+
+
+
+
+
+ Foundry
+
+
+
+
+
+ CONTROLS · EVIDENCE · AUDIT — IN ONE PLACE
+
+ 847 .
+
+
+ companies passed audit using Foundry. Average time to compliant:
+ thirty-one days , against an industry average of nine months. We wrote
+ the controls, we collect the evidence, we sit on the audit-day call.
+
+
+
+
+
+
+
+ — TRUSTED BY 847 SaaS COMPANIES, INCLUDING —
+
+ Tariff
+ Threadline
+ Tracejam
+ Cohort
+ Maple & Co.
+ Streampipe
+ Beacon
+ Anya Labs
+
+
+
+
+
+ The numbers behind the number.
+
+
+ 98%
+ pass on first try.
+ In 2025, 819 of 836 Foundry customers passed their first SOC 2 audit without a finding requiring remediation.
+
+
+ 31
+ days, on average.
+ From signature to audit-ready, measured across all customers in 2025. The fastest was nine days; the longest, sixty-two.
+
+
+ 12,400 h
+ of engineering reclaimed.
+ Hours your team did not spend writing policies, chasing screenshots, or filling in vendor questionnaires. We did.
+
+
+
+
+
+
+
+ — THE WORK —
+ Four things Foundry does so you don't.
+
+
+
+ 01
+ Continuous monitoring.
+ 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.
+ Coverage: 142 controls across SOC 2, ISO 27001, HIPAA, GDPR, PCI. Latency: p95 alert in 4 minutes.
+
+
+ 02
+ Auto-evidence collection.
+ 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.
+ Bundles ship in: AICPA-accepted format, ISO 19011 evidence track, or ABNF for PCI-QSA review.
+
+
+ 03
+ Vendor risk, automated.
+ 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.
+ Tracks: 280,000 vendors with public SOC 2s. Updates: within 48 hours of vendor report changes.
+
+
+ 04
+ Audit-day support.
+ 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.
+ Included on: Team and Scale tiers. Auditor partners: Prescient Assurance, Insight Assurance, A-LIGN, Schellman.
+
+
+
+
+
+
+
+ — WHAT CUSTOMERS SAY —
+ Three CTOs, three audits, three short stories.
+
+
+
+
+ Foundry got us SOC 2 in five weeks. We wrote zero policies ourselves. The audit-day team answered the questions our auditor asked, and we shipped a release the same afternoon.
+
+
+ Rachel Tran
+ CEO · Tariff
+ YC W24 · 14 employees · Pre-Series-A
+
+
+
+
+
+ I have done two SOC 2 audits in my career. The first one was nine months and an outside firm. The second was Foundry. Twenty-three days. We did not hire anyone for it.
+
+
+ Mateo Andrade
+ CTO · Threadline
+ Series A · 47 employees · Engineering-led
+
+
+
+
+
+ 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. Two of them we had not even heard of.
+
+
+ Priya Sundaram
+ Head of Security · Anya Labs
+ Series B · 180 employees · Healthcare SaaS
+
+
+
+
+
+
+
+
+ — PRICING —
+ Three tiers. Real prices. No "contact sales for pricing."
+ All tiers include the platform, the controls library, and the evidence collector. The differences are in audit-day support, framework count, and seat limit.
+
+
+
+
+ Starter
+ Your first SOC 2.
+
+ $299 /month
+ $2,990 yearly · save two months
+
+ One framework (SOC 2 Type I or Type II)
+ Up to 10 employees / 8 vendors
+ Continuous monitoring (40 controls)
+ Auto-evidence collection
+ Slack + email support, business hours
+ No audit-day team
+
+ Start with Starter →
+
+
+
+
+ $899 /month
+ $8,990 yearly · save two months
+
+ Up to four frameworks (SOC 2, ISO 27001, HIPAA, GDPR)
+ Up to 50 employees / 50 vendors
+ Continuous monitoring (all 142 controls)
+ Auto-evidence collection
+ Vendor risk module
+ Audit-day team on every audit
+ Slack + email + phone, 24×5
+
+ Start with Team →
+
+
+
+
+ Custom
+ From $2,499/month · annual contract
+
+ Unlimited frameworks & entities
+ Unlimited employees / vendors
+ Custom controls library + custom mappings
+ Dedicated solutions architect
+ SAML / SCIM / Okta provisioning
+ Audit-day team on every audit
+ 24×7 priority support
+
+ Talk to sales →
+
+
+
+
+
+
+
+ — FREQUENTLY ASKED —
+ Eight questions we get every week.
+
+
+
+
Is Foundry the auditor?
+ 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.
+
+
+
How long does a SOC 2 audit take with Foundry?
+ 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.
+
+
+
What systems does Foundry integrate with?
+ 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.
+
+
+
What if our codebase is on-prem?
+ 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.
+
+
+
Can we self-host Foundry?
+ 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.
+
+
+
What does "audit-day support" actually mean?
+ 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.
+
+
+
What happens if we fail the audit?
+ 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.
+
+
+
Can we cancel?
+ 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.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/_tests/07-foundry-compliance/style.css b/site/_tests/07-foundry-compliance/style.css
new file mode 100644
index 0000000..da7d6eb
--- /dev/null
+++ b/site/_tests/07-foundry-compliance/style.css
@@ -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;
+}
diff --git a/site/_tests/08-cohort-courses/brief.md b/site/_tests/08-cohort-courses/brief.md
new file mode 100644
index 0000000..9505a97
--- /dev/null
+++ b/site/_tests/08-cohort-courses/brief.md
@@ -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.
diff --git a/site/_tests/08-cohort-courses/index.html b/site/_tests/08-cohort-courses/index.html
new file mode 100644
index 0000000..70b8e39
--- /dev/null
+++ b/site/_tests/08-cohort-courses/index.html
@@ -0,0 +1,405 @@
+
+
+
+
+
+
Cohort — run live courses with 30 to 500 students
+
+
+
+
+
+
+ Cohort.
+
+
+
+
+
+
+
+
+ Build a SaaS in 30 days · Sahil Lavingia
+ Writing Strong · David Perell
+ Notion for Operators · Marie Poulin
+ The Cold Email Course · Alex Berman
+ How to Read a Painting · Camille Walala
+ Ship a Newsletter in 14 days · Lia Haberman
+ Practical TypeScript · Matt Pocock
+ The Public Speaking Cohort · Vinh Giang
+ Founders' Finance · Lola Priego
+ Print & Production · Hassan Rahim
+
+
+ Build a SaaS in 30 days · Sahil Lavingia
+ Writing Strong · David Perell
+ Notion for Operators · Marie Poulin
+ The Cold Email Course · Alex Berman
+ How to Read a Painting · Camille Walala
+ Ship a Newsletter in 14 days · Lia Haberman
+ Practical TypeScript · Matt Pocock
+ The Public Speaking Cohort · Vinh Giang
+ Founders' Finance · Lola Priego
+ Print & Production · Hassan Rahim
+
+
+
+
+
+
— A salon room for thirty to five hundred —
+
+ Cohort runs live courses the way your favourite teacher would —
+ with a date, a roster, and a room.
+
+
+ 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.
+
+
+
+
+
+
+
+
+
+ — THREE STEPS —
+ From idea to first cohort in a Sunday afternoon.
+
+
+
+ 01
+ Set a date.
+ 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.
+
+
+ 02
+ Invite the students.
+ 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.
+
+
+ 03
+ Teach live.
+ 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.
+
+
+
+
+
+
+
+
+
+
+
I.
+
Live cohorts.
+
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.
+
Limit: 500 live attendees per session. Recording: 1080p, captioned, hosted on Cohort or your own S3. Latency: p95 under 240 ms across most regions.
+
+
+
+
+
+ Live · 287 in the room
+
+
+ Session 04 · Friday · 18:00 GMT
+
+
+
"Now — open the prompt I sent on Monday. We're going to revise it together."
+
— Sahil, instructor
+
+
+
+
+
+
+
+
II.
+
Office hours, by request.
+
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.
+
Booking: 5-minute granularity. Buffers: auto. No-shows: tracked but not penalised — the teacher decides the policy.
+
+
+
+
+
Mon
+
Tue
+
Wed
+
Thu
+
Fri
+
+ Eight booked · Four available · This week.
+
+
+
+
+
+
+
III.
+
Async chat that doesn't read like a forum.
+
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.
+
Notifications: per-cohort digest, never per-message. Search: full-text + canonical-answers-only mode. Export: Markdown.
+
+
+
+
+
Marie
+
Should the database state be in Notion or Linear?
+
+
+
Sahil canonical
+
Notion for the source of truth. Linear for what's in flight this week. The duplication is the feature, not the bug.
+
+
+
Lia
+
Confirms — three cohorts in, this is the only setup that doesn't break.
+
+
+
+
+
+
+
+
IV.
+
Cohort analytics, the honest kind.
+
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.
+
Tracks: attendance, completion, Q&A volume, NPS, retention to the next cohort. Doesn't track: click-throughs on the welcome email — nobody cares.
+
+
+
+ 87%
+ finished the cohort
+
+ Avg. attendance 92%
+ NPS (n = 47) +71
+ Retain to next 34%
+ "Strongest moment" Session 04
+
+
+
+
+
+
+
+
+
+ — FROM THE TEACHERS —
+ Four short letters from instructors who switched.
+
+
+
+
+ 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.
+
+
+ Marie Poulin
+ Instructor · Notion for Operators
+ Eight cohorts · 2,400 students · since 2024
+
+
+
+
+
+ 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.
+
+
+ Sahil Lavingia
+ Instructor · Build a SaaS in 30 days
+ Twelve cohorts · 3,800 students · since 2024
+
+
+
+
+
+ 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.
+
+
+ Lia Haberman
+ Instructor · Ship a Newsletter
+ Six cohorts · 1,100 students · since 2025
+
+
+
+
+
+ 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.
+
+
+ Hassan Rahim
+ Instructor · Print & Production
+ Three cohorts · 320 students · since 2025
+
+
+
+
+
+
+
+
+
+
+ Indie
+ For solo instructors and small cohorts.
+
+ $59
+ /month
+
+ + 10% on revenue, capped at $500/cohort
+
+ One instructor seat
+ Up to 200 students per cohort
+ Unlimited cohorts per year
+ Live sessions · Office hours · Async chat
+ Cohort analytics · Email digests
+ Stripe payouts (you keep the relationship)
+
+ Start as Indie →
+
+
+
+ RECOMMENDED FOR TEAMS
+ Team
+ For studios, schools, and creator collectives.
+
+ $199
+ /month
+
+ flat — no revenue cut
+
+ Up to 8 instructor seats
+ Up to 500 students per cohort
+ Custom-branded cohort domain
+ Live · Office hours · Async · Replay vault
+ Cohort analytics + studio-wide rollup
+ Priority support · onboarding call
+
+ Start as Team →
+
+
+
+
+
+
+
+ — ANSWERS —
+ Six questions instructors ask before they switch.
+
+
+
+
Do you take a cut of my revenue?
+
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.
+
+
+
Can I bring my own audience?
+
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.
+
+
+
What happens between cohorts?
+
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."
+
+
+
Do you provide the live video?
+
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.
+
+
+
Can I export my course?
+
Anytime. Markdown for written content, MP4 for video, JSON for the roster. Your audience is yours; we just host the room.
+
+
+
What about students who can't make the live session?
+
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.
+
+
+
+
+
+
+ Run your first cohort by next month.
+ Set the date →
+ No credit card. Two-week trial on the room itself.
+
+
+
+
+
+
+
+
+
diff --git a/site/_tests/08-cohort-courses/style.css b/site/_tests/08-cohort-courses/style.css
new file mode 100644
index 0000000..57755ef
--- /dev/null
+++ b/site/_tests/08-cohort-courses/style.css
@@ -0,0 +1,1094 @@
+/* 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
+ */
+
+@import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,600;1,400;1,500&family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;1,400;1,500&family=Plus+Jakarta+Sans:wght@400;500;600&display=swap");
+
+:root {
+ /* Salon paper band — warm creamy paper */
+ --paper: oklch(95% 0.022 65);
+ --paper-2: oklch(92% 0.028 65);
+ --paper-3: oklch(88% 0.034 60);
+ --rule: oklch(78% 0.024 60);
+ --rule-2: oklch(58% 0.030 55);
+ --muted: oklch(48% 0.030 50);
+ --neutral: oklch(34% 0.030 45);
+ --ink-2: oklch(28% 0.030 40);
+ --ink: oklch(22% 0.030 38);
+
+ /* Warm amber accent — Salon's voice */
+ --accent: oklch(58% 0.14 55);
+ --accent-soft: oklch(82% 0.08 55);
+ --accent-bg: oklch(94% 0.040 55);
+
+ /* Type system. Salon uses MONO for display — the unusual choice. */
+ --font-display: "IBM Plex Mono", "JetBrains Mono", ui-monospace, monospace;
+ --font-serif: "Cormorant Garamond", ui-serif, Georgia, serif;
+ --font-body: "Plus Jakarta Sans", "Hanken Grotesk", ui-sans-serif, sans-serif;
+ --font-label: "IBM Plex Mono", ui-monospace, monospace;
+ --font-wordmark: "Cormorant Garamond", ui-serif, Georgia, serif;
+
+ --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: 7rem;
+ --space-4xl: 10rem;
+
+ --rule-hair: 0.5px;
+ --rule-fine: 1px;
+ --rule-bold: 1.5px;
+
+ --measure: 60ch;
+ --page-max: 70rem;
+
+ --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.65;
+ color: var(--ink-2);
+ background: var(--paper);
+ -webkit-font-smoothing: antialiased;
+ text-rendering: optimizeLegibility;
+ font-feature-settings: "kern", "liga", "ss01";
+ overflow-x: clip;
+}
+
+body {
+ min-height: 100dvh;
+ overflow-x: clip;
+}
+
+a { color: inherit; }
+
+::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-fine) solid var(--rule);
+ background: color-mix(in oklch, var(--paper) 92%, transparent);
+ backdrop-filter: blur(8px);
+ position: sticky;
+ top: 0;
+ z-index: 10;
+ line-height: 1;
+}
+
+.wordmark {
+ font-family: var(--font-wordmark);
+ font-weight: 600;
+ font-size: 1.375rem;
+ letter-spacing: -0.005em;
+ color: var(--ink);
+ text-decoration: none;
+ display: inline-flex;
+ align-items: baseline;
+ line-height: 1;
+ font-style: italic;
+}
+
+.wordmark__mark {
+ color: var(--accent);
+ font-style: normal;
+}
+
+.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 160ms 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.06em;
+ 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 200ms var(--ease-out), color 200ms var(--ease-out), transform 220ms var(--ease-out-2), box-shadow 220ms 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 10px 24px -14px oklch(58% 0.14 55 / 0.50); }
+.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.06em;
+ text-transform: uppercase;
+ text-decoration: none;
+ padding: 0.95rem 1.1rem;
+ line-height: 1;
+ border: var(--rule-fine) solid var(--ink);
+ transition: background 200ms var(--ease-out), color 200ms var(--ease-out), transform 220ms 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-block: var(--space-3xl) var(--space-3xl);
+ display: grid;
+ gap: var(--space-3xl);
+ text-align: center;
+ position: relative;
+}
+
+/* MARQUEE — continuous scroll of course titles */
+
+.marquee {
+ width: 100%;
+ overflow: hidden;
+ padding-block: var(--space-md);
+ border-block: var(--rule-fine) solid var(--rule);
+ background: var(--paper-2);
+ position: relative;
+ /* Edge-fade so the cycle is implied */
+ -webkit-mask-image: linear-gradient(90deg, transparent, var(--paper) 6%, var(--paper) 94%, transparent);
+ mask-image: linear-gradient(90deg, transparent, var(--paper) 6%, var(--paper) 94%, transparent);
+}
+
+.marquee__track {
+ display: inline-flex;
+ gap: 0;
+ white-space: nowrap;
+ animation: marquee 56s linear infinite;
+ will-change: transform;
+}
+
+.marquee__track.is-paused { animation-play-state: paused; }
+.marquee__track.is-offscreen { animation-play-state: paused; }
+
+.marquee__list {
+ list-style: none;
+ display: inline-flex;
+ align-items: center;
+ gap: var(--space-2xl);
+ padding-inline-end: var(--space-2xl);
+ font-family: var(--font-label);
+ font-size: 0.95rem;
+ letter-spacing: 0.04em;
+ color: var(--ink-2);
+ line-height: 1;
+}
+
+.marquee__list li {
+ display: inline-flex;
+ align-items: baseline;
+ gap: 0.5em;
+}
+
+.marquee__list em {
+ font-family: var(--font-serif);
+ font-style: italic;
+ font-weight: 500;
+ font-size: 1.4em;
+ letter-spacing: -0.005em;
+ color: var(--ink);
+ margin-inline-end: 0.2em;
+}
+
+.marquee__list li::after {
+ content: "✦";
+ margin-inline-start: var(--space-xl);
+ color: var(--accent);
+ font-size: 0.7em;
+}
+.marquee__list li:last-child::after { display: none; }
+
+@keyframes marquee {
+ from { transform: translateX(0); }
+ to { transform: translateX(-50%); }
+}
+
+/* ─── HERO COPY ─── */
+
+.hero__copy {
+ display: grid;
+ gap: var(--space-lg);
+ padding-inline: clamp(1.5rem, 6vw, 4rem);
+ max-width: 70rem;
+ margin-inline: auto;
+}
+
+.hero__eyebrow {
+ font-family: var(--font-label);
+ font-size: 0.75rem;
+ letter-spacing: 0.18em;
+ color: var(--accent);
+ text-transform: uppercase;
+}
+
+.hero__title {
+ font-family: var(--font-serif);
+ font-weight: 500;
+ font-size: clamp(2.25rem, 4.5vw + 1rem, 5rem);
+ line-height: 1.05;
+ letter-spacing: -0.018em;
+ color: var(--ink);
+ font-style: normal;
+ max-width: 22ch;
+ margin-inline: auto;
+}
+
+.hero__title em {
+ font-style: italic;
+ color: var(--accent);
+ font-weight: 500;
+}
+
+.hero__lede {
+ font-family: var(--font-body);
+ font-size: clamp(1.0625rem, 0.6vw + 0.75rem, 1.25rem);
+ line-height: 1.65;
+ color: var(--ink-2);
+ max-width: 56ch;
+ margin-inline: auto;
+}
+
+.hero__cta {
+ display: inline-flex;
+ flex-wrap: wrap;
+ gap: var(--space-md);
+ align-items: center;
+ justify-content: center;
+ margin-block-start: var(--space-md);
+ margin-inline: auto;
+}
+
+.hero__foot {
+ font-family: var(--font-label);
+ font-size: 0.78rem;
+ letter-spacing: 0.04em;
+ color: var(--muted);
+ margin-block-start: var(--space-md);
+ text-align: center;
+}
+
+/* ─── SECTION HEAD (shared) ─── */
+
+.section__head {
+ max-width: 60rem;
+ margin-block-end: var(--space-2xl);
+}
+
+.section__head--centered { text-align: center; margin-inline: auto; }
+
+.section__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);
+}
+
+.section__h {
+ font-family: var(--font-serif);
+ font-weight: 500;
+ font-style: italic;
+ font-size: clamp(1.875rem, 2.5vw + 1rem, 3.25rem);
+ line-height: 1.15;
+ letter-spacing: -0.012em;
+ color: var(--ink);
+ max-width: 26ch;
+}
+
+.section__head--centered .section__h { margin-inline: auto; }
+
+.section__lede {
+ font-family: var(--font-body);
+ font-size: 1.0625rem;
+ line-height: 1.65;
+ color: var(--ink-2);
+ max-width: 60ch;
+ margin-block-start: var(--space-md);
+}
+
+.section__head--centered .section__lede { margin-inline: auto; }
+
+/* ─── HOW IT WORKS ─── */
+
+.how {
+ padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
+ border-block-end: var(--rule-fine) solid var(--rule);
+}
+
+.how__steps {
+ list-style: none;
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: var(--space-xl);
+ border-block-start: var(--rule-fine) solid var(--rule);
+ padding-block-start: var(--space-2xl);
+}
+
+.step { display: grid; gap: var(--space-md); }
+
+.step__num {
+ font-family: var(--font-label);
+ font-size: 0.85rem;
+ letter-spacing: 0.18em;
+ color: var(--accent);
+ font-weight: 600;
+}
+
+.step__title {
+ font-family: var(--font-serif);
+ font-weight: 500;
+ font-style: italic;
+ font-size: clamp(1.5rem, 1.4vw + 0.75rem, 2rem);
+ line-height: 1.2;
+ letter-spacing: -0.012em;
+ color: var(--ink);
+}
+
+.step p {
+ font-size: 1rem;
+ line-height: 1.65;
+ color: var(--ink-2);
+ max-width: var(--measure);
+}
+
+@media (max-width: 64rem) {
+ .how__steps { grid-template-columns: 1fr; }
+}
+
+/* ─── FEATURES — alternating split ─── */
+
+.features {
+ padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
+ border-block-end: var(--rule-fine) solid var(--rule);
+ display: grid;
+ gap: var(--space-3xl);
+}
+
+.feature-row {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: var(--space-3xl);
+ align-items: center;
+ max-width: var(--page-max);
+ margin-inline: auto;
+}
+
+.feature-row--reverse .feature-row__copy { order: 2; }
+.feature-row--reverse .feature-row__visual { order: 1; }
+
+.feature-row__copy { display: grid; gap: var(--space-md); }
+
+.feature-row__num {
+ font-family: var(--font-label);
+ font-size: 0.95rem;
+ letter-spacing: 0.18em;
+ color: var(--accent);
+ font-weight: 600;
+}
+
+.feature-row__copy h3 {
+ font-family: var(--font-serif);
+ font-weight: 500;
+ font-style: italic;
+ font-size: clamp(1.625rem, 1.6vw + 0.75rem, 2.25rem);
+ line-height: 1.15;
+ letter-spacing: -0.012em;
+ color: var(--ink);
+}
+
+.feature-row__copy p {
+ font-size: 1.0625rem;
+ line-height: 1.65;
+ color: var(--ink-2);
+ max-width: var(--measure);
+}
+
+.feature-row__detail {
+ font-family: var(--font-label);
+ font-size: 0.82rem;
+ letter-spacing: 0.04em;
+ color: var(--muted);
+ line-height: 1.7;
+ margin-block-start: var(--space-xs);
+}
+.feature-row__detail b { color: var(--ink); font-weight: 600; }
+
+.feature-row__visual {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 18rem;
+}
+
+@media (max-width: 64rem) {
+ .feature-row { grid-template-columns: 1fr; }
+ .feature-row--reverse .feature-row__copy,
+ .feature-row--reverse .feature-row__visual { order: initial; }
+}
+
+/* ─── card-stack visual ─── */
+
+.card-stack {
+ position: relative;
+ width: min(100%, 24rem);
+ font-family: var(--font-label);
+}
+
+.card-stack__layer {
+ background: var(--paper);
+ border: var(--rule-fine) solid var(--rule);
+ padding: var(--space-md) var(--space-lg);
+ border-radius: 6px;
+ box-shadow: 0 8px 24px -16px oklch(20% 0.05 60 / 0.18);
+ font-size: 0.85rem;
+ letter-spacing: 0.04em;
+ color: var(--ink-2);
+ display: flex;
+ align-items: center;
+ gap: 0.6em;
+}
+
+.card-stack__layer--3 {
+ position: relative;
+ z-index: 3;
+ transform: translateY(-12px) rotate(-1.5deg);
+ background: var(--paper);
+ font-weight: 600;
+ color: var(--ink);
+}
+
+.card-stack__layer--2 {
+ position: relative;
+ z-index: 2;
+ margin-block-start: -8px;
+ margin-inline-start: 16px;
+ transform: rotate(0.8deg);
+ background: var(--paper-2);
+ color: var(--muted);
+}
+
+.card-stack__layer--1 {
+ position: relative;
+ z-index: 1;
+ margin-block-start: -6px;
+ margin-inline-start: -10px;
+ transform: rotate(-0.4deg);
+ display: block;
+ font-family: var(--font-serif);
+ font-size: 1.1rem;
+ font-style: italic;
+ font-weight: 500;
+ line-height: 1.4;
+ color: var(--ink);
+}
+
+.card-stack__layer--1 p { letter-spacing: -0.005em; }
+
+.card-stack__attrib {
+ display: block;
+ font-family: var(--font-label);
+ font-style: normal;
+ font-size: 0.75rem;
+ letter-spacing: 0.06em;
+ color: var(--muted);
+ margin-block-start: var(--space-xs);
+}
+
+.dot {
+ width: 8px; height: 8px; border-radius: 50%;
+ display: inline-block;
+}
+.dot--green { background: oklch(70% 0.14 145); animation: pulse 2.4s var(--ease-out) infinite; }
+
+@keyframes pulse {
+ 0%, 100% { opacity: 1; transform: scale(1); }
+ 50% { opacity: 0.55; transform: scale(0.85); }
+}
+
+/* ─── hours visual ─── */
+
+.hours {
+ width: min(100%, 26rem);
+ background: var(--paper);
+ border: var(--rule-fine) solid var(--rule);
+ padding: var(--space-lg);
+ border-radius: 6px;
+ display: grid;
+ gap: var(--space-md);
+}
+
+.hours__week {
+ display: grid;
+ grid-template-columns: repeat(5, 1fr);
+ gap: var(--space-sm);
+}
+
+.hours__day {
+ display: grid;
+ gap: var(--space-2xs);
+ align-content: start;
+ text-align: center;
+ font-family: var(--font-label);
+}
+
+.hours__day span {
+ font-size: 0.7rem;
+ letter-spacing: 0.16em;
+ color: var(--muted);
+ text-transform: uppercase;
+ margin-block-end: var(--space-xs);
+}
+
+.hours__slot {
+ height: 18px;
+ display: block;
+ background: var(--paper-3);
+ border-radius: 2px;
+ border: var(--rule-fine) solid var(--rule);
+}
+
+.hours__slot.is-booked {
+ background: var(--accent);
+ border-color: var(--accent);
+}
+
+.hours__caption {
+ font-family: var(--font-label);
+ font-size: 0.8rem;
+ letter-spacing: 0.04em;
+ color: var(--muted);
+ text-align: center;
+ border-block-start: var(--rule-fine) solid var(--rule);
+ padding-block-start: var(--space-sm);
+}
+
+/* ─── thread visual ─── */
+
+.thread {
+ width: min(100%, 26rem);
+ display: grid;
+ gap: var(--space-sm);
+}
+
+.thread__msg {
+ background: var(--paper);
+ border: var(--rule-fine) solid var(--rule);
+ border-radius: 6px;
+ padding: var(--space-md);
+ font-size: 0.9375rem;
+ line-height: 1.55;
+ color: var(--ink-2);
+}
+
+.thread__msg--reply { margin-inline-start: var(--space-xl); }
+
+.thread__author {
+ display: block;
+ font-family: var(--font-label);
+ font-size: 0.78rem;
+ letter-spacing: 0.06em;
+ color: var(--accent);
+ font-weight: 600;
+ margin-block-end: var(--space-xs);
+ text-transform: uppercase;
+}
+
+.thread__badge {
+ display: inline-block;
+ margin-inline-start: 0.4em;
+ padding: 0.1em 0.5em;
+ background: var(--accent);
+ color: var(--paper);
+ font-size: 0.65rem;
+ letter-spacing: 0.08em;
+ border-radius: 2px;
+}
+
+/* ─── report visual ─── */
+
+.report {
+ width: min(100%, 24rem);
+ background: var(--paper);
+ border: var(--rule-fine) solid var(--rule);
+ padding: var(--space-xl);
+ border-radius: 6px;
+ text-align: center;
+ display: grid;
+ gap: var(--space-md);
+}
+
+.report__num {
+ font-family: var(--font-serif);
+ font-style: italic;
+ font-weight: 500;
+ font-size: 5.5rem;
+ line-height: 0.9;
+ color: var(--accent);
+ letter-spacing: -0.03em;
+}
+
+.report__label {
+ font-family: var(--font-label);
+ font-size: 0.78rem;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: var(--muted);
+}
+
+.report__list {
+ list-style: none;
+ display: grid;
+ gap: var(--space-sm);
+ border-block-start: var(--rule-fine) solid var(--rule);
+ padding-block-start: var(--space-md);
+}
+
+.report__list li {
+ display: flex;
+ justify-content: space-between;
+ font-family: var(--font-label);
+ font-size: 0.85rem;
+ letter-spacing: 0.04em;
+ color: var(--muted);
+}
+
+.report__list b { color: var(--ink); font-weight: 600; }
+
+/* ─── TESTIMONIALS ─── */
+
+.voices {
+ padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
+ border-block-end: var(--rule-fine) solid var(--rule);
+ background: var(--paper-2);
+}
+
+.voices__grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: var(--space-xl);
+ max-width: var(--page-max);
+ margin-inline: auto;
+}
+
+.voice {
+ display: grid;
+ gap: var(--space-md);
+ padding: var(--space-xl);
+ background: var(--paper);
+ border: var(--rule-fine) solid var(--rule);
+ border-radius: 4px;
+ opacity: 0;
+ transform: translateY(8px);
+ transition: opacity 400ms var(--ease-out), transform 400ms var(--ease-out);
+}
+
+.voice.is-revealed {
+ opacity: 1;
+ transform: translateY(0);
+}
+
+.voice blockquote {
+ font-family: var(--font-serif);
+ font-style: italic;
+ font-weight: 400;
+ font-size: 1.25rem;
+ line-height: 1.45;
+ letter-spacing: -0.005em;
+ color: var(--ink);
+}
+
+.voice blockquote::before {
+ content: "“";
+ font-family: var(--font-serif);
+ font-style: italic;
+ font-weight: 500;
+ font-size: 3.4rem;
+ line-height: 0.7;
+ display: block;
+ color: var(--accent);
+ margin-block-end: var(--space-xs);
+}
+
+.voice figcaption {
+ display: grid;
+ gap: var(--space-3xs);
+ border-block-start: var(--rule-fine) solid var(--rule);
+ padding-block-start: var(--space-md);
+}
+
+.voice__name {
+ font-family: var(--font-serif);
+ font-weight: 600;
+ font-size: 1.0625rem;
+ color: var(--ink);
+ letter-spacing: -0.005em;
+}
+
+.voice__role {
+ font-family: var(--font-label);
+ font-size: 0.78rem;
+ letter-spacing: 0.06em;
+ color: var(--accent);
+ text-transform: uppercase;
+}
+
+.voice__meta {
+ font-family: var(--font-label);
+ font-size: 0.72rem;
+ letter-spacing: 0.04em;
+ color: var(--muted);
+}
+
+@media (max-width: 64rem) {
+ .voices__grid { grid-template-columns: 1fr; }
+}
+
+/* ─── PRICING ─── */
+
+.pricing {
+ padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
+ border-block-end: var(--rule-fine) solid var(--rule);
+}
+
+.pricing__grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: var(--space-xl);
+ max-width: 56rem;
+ margin-inline: auto;
+}
+
+.tier {
+ display: grid;
+ gap: var(--space-md);
+ padding: var(--space-2xl);
+ background: var(--paper);
+ border: var(--rule-fine) solid var(--rule);
+ border-radius: 6px;
+ position: relative;
+ text-align: center;
+ transition: transform 200ms var(--ease-out-2), box-shadow 200ms var(--ease-out-2), border-color 200ms var(--ease-out);
+}
+
+.tier:hover {
+ transform: translateY(-3px);
+ box-shadow: 0 16px 36px -22px oklch(22% 0.05 50 / 0.22);
+ border-color: var(--accent);
+}
+
+.tier:active {
+ transform: translateY(0);
+ transition-duration: 60ms;
+}
+
+.tier--recommended {
+ border-color: var(--accent);
+ border-width: 1.5px;
+}
+
+.tier__badge {
+ font-family: var(--font-label);
+ font-size: 0.7rem;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ color: var(--paper);
+ background: var(--accent);
+ padding: 0.3em 0.8em;
+ border-radius: 999px;
+ width: max-content;
+ margin-inline: auto;
+}
+
+.tier__name {
+ font-family: var(--font-serif);
+ font-weight: 500;
+ font-size: clamp(1.875rem, 1.5vw + 0.75rem, 2.5rem);
+ letter-spacing: -0.012em;
+ color: var(--ink);
+}
+.tier__name em { font-style: italic; }
+
+.tier__pitch {
+ font-family: var(--font-label);
+ font-size: 0.8rem;
+ letter-spacing: 0.06em;
+ color: var(--muted);
+ text-transform: uppercase;
+}
+
+.tier__price {
+ display: flex;
+ align-items: baseline;
+ justify-content: center;
+ gap: var(--space-xs);
+ font-family: var(--font-serif);
+ border-block-start: var(--rule-fine) solid var(--rule);
+ padding-block-start: var(--space-md);
+}
+
+.tier__amount {
+ font-weight: 500;
+ font-style: italic;
+ font-size: clamp(2.5rem, 2vw + 1rem, 3.5rem);
+ letter-spacing: -0.018em;
+ color: var(--ink);
+ font-variant-numeric: lining-nums tabular-nums;
+ line-height: 1;
+}
+
+.tier__period {
+ font-family: var(--font-label);
+ font-size: 0.95rem;
+ letter-spacing: 0.04em;
+ color: var(--muted);
+ text-transform: lowercase;
+ font-style: normal;
+}
+
+.tier__yearly {
+ font-family: var(--font-label);
+ font-size: 0.8rem;
+ 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.95rem;
+ line-height: 1.55;
+ color: var(--ink-2);
+ text-align: start;
+}
+
+.tier__list li {
+ padding-inline-start: 1.4em;
+ position: relative;
+}
+.tier__list li::before {
+ content: "✦";
+ position: absolute;
+ inset-inline-start: 0;
+ color: var(--accent);
+}
+.tier__list li strong { color: var(--ink); font-weight: 600; }
+
+@media (max-width: 56rem) {
+ .pricing__grid { grid-template-columns: 1fr; }
+}
+
+/* ─── FAQ ─── */
+
+.faq {
+ padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
+ border-block-end: var(--rule-fine) solid var(--rule);
+}
+
+.faq__grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: var(--space-2xl);
+ max-width: var(--page-max);
+ margin-inline: auto;
+}
+
+.faq__row {
+ display: grid;
+ gap: var(--space-sm);
+ border-block-start: var(--rule-fine) solid var(--rule);
+ padding-block-start: var(--space-lg);
+}
+
+.faq__row h3 {
+ font-family: var(--font-serif);
+ font-style: italic;
+ font-weight: 500;
+ font-size: clamp(1.125rem, 0.6vw + 0.75rem, 1.4375rem);
+ line-height: 1.25;
+ letter-spacing: -0.005em;
+ color: var(--ink);
+}
+
+.faq__row p {
+ font-family: var(--font-body);
+ font-size: 1rem;
+ line-height: 1.65;
+ color: var(--ink-2);
+ max-width: 60ch;
+}
+
+@media (max-width: 64rem) {
+ .faq__grid { grid-template-columns: 1fr; }
+}
+
+/* ─── FINAL CTA ─── */
+
+.final-cta {
+ padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
+ background: var(--paper-2);
+ display: grid;
+ gap: var(--space-lg);
+ text-align: center;
+ justify-items: center;
+ border-block-end: var(--rule-fine) solid var(--rule);
+}
+
+.final-cta__line {
+ font-family: var(--font-serif);
+ font-weight: 400;
+ font-size: clamp(2rem, 3vw + 0.5rem, 3.75rem);
+ line-height: 1.1;
+ letter-spacing: -0.012em;
+ color: var(--ink);
+ max-width: 24ch;
+}
+.final-cta__line em { font-style: italic; }
+
+.final-cta__btn { font-size: 0.85rem; padding: 1rem 1.6rem; }
+
+.final-cta__sub {
+ font-family: var(--font-label);
+ font-size: 0.78rem;
+ letter-spacing: 0.04em;
+ color: var(--muted);
+}
+
+/* ─── FOOTER ─── */
+
+.footer {
+ padding: var(--space-2xl) clamp(1.5rem, 6vw, 4rem);
+ background: var(--paper);
+ display: grid;
+ gap: var(--space-lg);
+ text-align: center;
+}
+
+.footer__top {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-md);
+ align-items: center;
+}
+
+.footer__brand {
+ font-family: var(--font-wordmark);
+ font-style: italic;
+ font-weight: 600;
+ font-size: 1.5rem;
+ color: var(--ink);
+ letter-spacing: -0.005em;
+ text-decoration: none;
+ display: inline-flex;
+ align-items: baseline;
+ line-height: 1;
+}
+
+.footer__nav {
+ list-style: none;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+ gap: var(--space-lg);
+ font-family: var(--font-label);
+ font-size: 0.78rem;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+}
+
+.footer__nav a {
+ color: var(--ink-2);
+ text-decoration: none;
+ transition: color 160ms var(--ease-out);
+}
+.footer__nav a:hover { color: var(--accent); }
+
+.footer__colophon {
+ font-family: var(--font-label);
+ font-size: 0.78rem;
+ letter-spacing: 0.04em;
+ color: var(--muted);
+ max-width: 60ch;
+ margin-inline: auto;
+ line-height: 1.6;
+}
+
+/* ─── REDUCED MOTION ─── */
+
+@media (prefers-reduced-motion: reduce) {
+ * {
+ animation: none !important;
+ transition-duration: 0.01ms !important;
+ }
+ .marquee__track { transform: none; }
+ .voice { opacity: 1 !important; transform: none !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 ─── */
+
+a:focus-visible, button:focus-visible {
+ outline: 2px solid var(--accent);
+ outline-offset: 3px;
+ border-radius: 2px;
+}
diff --git a/site/_tests/README.md b/site/_tests/README.md
index 4d0cf01..7359851 100644
--- a/site/_tests/README.md
+++ b/site/_tests/README.md
@@ -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.
diff --git a/site/_tests/_thumbs/01-tide.png b/site/_tests/_thumbs/01-tide.png
new file mode 100644
index 0000000..ea92fcf
Binary files /dev/null and b/site/_tests/_thumbs/01-tide.png differ
diff --git a/site/_tests/_thumbs/02-streampipe.png b/site/_tests/_thumbs/02-streampipe.png
new file mode 100644
index 0000000..6475fc7
Binary files /dev/null and b/site/_tests/_thumbs/02-streampipe.png differ
diff --git a/site/_tests/_thumbs/03-maple.png b/site/_tests/_thumbs/03-maple.png
new file mode 100644
index 0000000..59ce06b
Binary files /dev/null and b/site/_tests/_thumbs/03-maple.png differ
diff --git a/site/_tests/_thumbs/04-meridian.png b/site/_tests/_thumbs/04-meridian.png
new file mode 100644
index 0000000..7abaf6c
Binary files /dev/null and b/site/_tests/_thumbs/04-meridian.png differ
diff --git a/site/_tests/_thumbs/05-tracejam.png b/site/_tests/_thumbs/05-tracejam.png
new file mode 100644
index 0000000..05e56a2
Binary files /dev/null and b/site/_tests/_thumbs/05-tracejam.png differ
diff --git a/site/_tests/_thumbs/06-anya.png b/site/_tests/_thumbs/06-anya.png
new file mode 100644
index 0000000..87b34ab
Binary files /dev/null and b/site/_tests/_thumbs/06-anya.png differ
diff --git a/site/_tests/_thumbs/07-foundry.png b/site/_tests/_thumbs/07-foundry.png
new file mode 100644
index 0000000..b0de437
Binary files /dev/null and b/site/_tests/_thumbs/07-foundry.png differ
diff --git a/site/_tests/_thumbs/08-cohort.png b/site/_tests/_thumbs/08-cohort.png
new file mode 100644
index 0000000..e0ba443
Binary files /dev/null and b/site/_tests/_thumbs/08-cohort.png differ
diff --git a/site/css/components.css b/site/css/components.css
index f5a1df4..9dbaa6e 100644
--- a/site/css/components.css
+++ b/site/css/components.css
@@ -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);
diff --git a/site/index.html b/site/index.html
index 65f582c..c2331c8 100644
--- a/site/index.html
+++ b/site/index.html
@@ -410,15 +410,18 @@
07 ⁄ Examples
- Six pages, six different shapes.
+ Eight pages, eight different shapes.
-
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 /* Hallmark · macrostructure: … */ stamp at the top of its CSS.
+
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 /* Hallmark · macrostructure: … */ stamp at the top of its CSS. Hover any thumbnail to see the page open in a new tab.
+
+
+
01
Tide
Quote-Led · Atelier · no enrichment
@@ -427,6 +430,9 @@
+
+
+
02
Streampipe
Workbench · Terminal · Tier-A CSS-art mockup
@@ -435,6 +441,9 @@
+
+
+
03
Maple Street Bread
Long Document · Linen · Tier-B hand-built SVG loaf
@@ -443,14 +452,20 @@
+
+
+
04
Meridian
- Manifesto · Manifesto · no enrichment
- Studio manifesto. Pure black, oversized white display, single red bleed band.
+ Manifesto · Manifesto · principles + practice + reading
+ Studio manifesto. Pure black, oversized white display, eleven sections.
View ↗
+
+
+
05
Tracejam
Bento Grid · Pastel · clipped-edge dashboard
@@ -459,6 +474,9 @@
+
+
+
06
Anya
Long Document · Studio · no enrichment
@@ -466,6 +484,28 @@
View ↗
+
+
+
+
+ 07
+ Foundry
+ Stat-Led · Newsprint · animated counter + pricing pulse
+ SOC2 compliance SaaS. Hero is one giant number. Testimonials, pricing, FAQ — full sequence.
+ View ↗
+
+
+
+
+
+
+ 08
+ Cohort
+ Marquee Hero · Salon · continuous-scroll + stagger reveal
+ Cohort-based courses platform. Marquee of live courses, alternating features, salon tone.
+ View ↗
+
+
Findings + improvement list at site/_tests/ .
diff --git a/skill/SKILL.md b/skill/SKILL.md
index 6dee6ca..4dab595 100644
--- a/skill/SKILL.md
+++ b/skill/SKILL.md
@@ -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 `
`, custom-art ``, ``, 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 `` / `` 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.
---
diff --git a/skill/references/layout-and-space.md b/skill/references/layout-and-space.md
index da14bba..5a0d7e8 100644
--- a/skill/references/layout-and-space.md
+++ b/skill/references/layout-and-space.md
@@ -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:
diff --git a/skill/references/macrostructures.md b/skill/references/macrostructures.md
index 82b42ee..ca9ae5e 100644
--- a/skill/references/macrostructures.md
+++ b/skill/references/macrostructures.md
@@ -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").
diff --git a/skill/references/microinteractions.md b/skill/references/microinteractions.md
index 236172e..61d4f72 100644
--- a/skill/references/microinteractions.md
+++ b/skill/references/microinteractions.md
@@ -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.
diff --git a/skill/references/typography.md b/skill/references/typography.md
index 5f1440f..b93673b 100644
--- a/skill/references/typography.md
+++ b/skill/references/typography.md
@@ -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.