Cleanup — drop ~3.5 MB of orphan media + dead test pages

Orphan thumbs (referenced nowhere): 8 example PNGs, 5 hero JPGs,
4 verb-* PNGs, audit-slop.png, 2.2 MB crypto-example.mp4.
Dead pages: refine verb test (verb removed earlier), v0 example
specimens (_example-bakery, _example-cmdk), docs/img theme PNGs,
skill/_tests/nav-footer-variety. Updated verbs/README and ROADMAP
to drop the now-broken references.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Youssef
2026-05-07 14:47:58 +01:00
co-authored by Claude Opus 4.7
parent d130808b54
commit abc25299b1
36 changed files with 2 additions and 2802 deletions
-1
View File
@@ -165,7 +165,6 @@ For reference. Everything below is in current `0.4.0`.
- ✓ Custom-craft canon: pure CSS art, hand-built SVG, declarative animation, View Transitions, Three.js when justified
- ✓ Asset-source catalogue: Lucide / Phosphor / Heroicons (icons), Simple Icons / SVGL (logos), Nanobanana 2 / Recraft V4 (generated illustration), Storyset / Humaaans (library), Mixkit / Coverr (video), Unsplash / Nappy (photography)
- ✓ Working install path: `cp skill/* ~/.claude/skills/hallmark/`
- ✓ Two test specimens: bakery (`site/_example-bakery/`) and command palette (`site/_example-cmdk/`)
- ✓ Slop test 33 questions: visual (8), structural (2), microinteractions (10), variety (3), implementation (6), hero enrichment (4)
- ✓ View Transitions on theme switch, with reduced-motion fallback
- ✓ Copy-to-clipboard with silent success on install code blocks
Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

-764
View File
@@ -1,764 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="color-scheme" content="light" />
<title>Marlow Street Bread — opening 8 May</title>
<meta name="description" content="A neighborhood sourdough bakery. Local flour, long ferment, a few loaves a day. Opening on Marlow Street in May." />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300..500;1,9..144,300..500&family=IBM+Plex+Sans:wght@300;400;500&family=IBM+Plex+Mono:wght@400;500&display=swap" />
<style>
/*
Marlow Street Bread — a single-page announcement.
Tone: quietly confident, editorial. Anchor hue: 45 (wheat / crust).
*/
:root {
/* Colour */
--color-paper: oklch(96% 0.016 75);
--color-paper-2: oklch(93% 0.020 72);
--color-rule: oklch(82% 0.018 70);
--color-rule-2: oklch(70% 0.022 65);
--color-muted: oklch(52% 0.022 60);
--color-ink-2: oklch(32% 0.025 55);
--color-ink: oklch(22% 0.026 50);
--color-accent: oklch(48% 0.15 45);
--color-focus: oklch(48% 0.15 45);
--color-error: oklch(45% 0.18 25);
--color-success: oklch(42% 0.10 140);
/* Type */
--font-display: "Fraunces", "Tiempos", ui-serif, Georgia, serif;
--font-body: "IBM Plex Sans", "Söhne", ui-sans-serif, sans-serif;
--font-serif: "Fraunces", ui-serif, Georgia, serif;
--font-label: "IBM Plex Mono", ui-monospace, monospace;
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-md: 1.125rem;
--text-lg: 1.375rem;
--text-xl: 1.75rem;
--text-2xl: 2.25rem;
--text-display: clamp(3rem, 7vw + 1rem, 7.25rem);
--text-display-s: clamp(2rem, 3vw + 1rem, 3rem);
--lh-tight: 1.02;
--lh-snug: 1.18;
--lh-normal: 1.45;
--lh-relaxed: 1.65;
--tracking-display: -0.028em;
--tracking-tight: -0.012em;
--tracking-label: 0.14em;
/* Spacing */
--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: 4.5rem;
--space-3xl: 7rem;
--space-4xl: 11rem;
/* Motion */
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
--ease-in: cubic-bezier(0.7, 0, 0.84, 0);
--dur-micro: 120ms;
--dur-short: 220ms;
--dur-long: 520ms;
--page-max: 64rem;
--page-gutter: clamp(1.25rem, 4vw, 3rem);
--measure: 58ch;
}
*, *::before, *::after { box-sizing: border-box; }
body, h1, h2, h3, p, figure, blockquote, dl, dd { margin: 0; }
img, picture, svg { display: block; max-width: 100%; height: auto; }
button, input, select { font: inherit; color: inherit; }
button { background: none; border: 0; padding: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
ul, ol { margin: 0; padding: 0; list-style: none; }
:focus { outline: none; }
:focus-visible {
outline: 2px solid var(--color-focus);
outline-offset: 3px;
border-radius: 2px;
}
html { background: var(--color-paper); color: var(--color-ink); }
body {
font-family: var(--font-body);
font-weight: 400;
font-size: var(--text-base);
line-height: var(--lh-relaxed);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
min-height: 100dvh;
padding-inline: max(var(--page-gutter), env(safe-area-inset-left));
padding-block-end: env(safe-area-inset-bottom);
overflow-x: hidden;
}
::selection { background: var(--color-accent); color: var(--color-paper); }
h1, h2, h3 {
font-family: var(--font-display);
font-weight: 300;
line-height: var(--lh-tight);
letter-spacing: var(--tracking-display);
color: var(--color-ink);
font-optical-sizing: auto;
}
p { max-width: var(--measure); }
em { font-style: italic; }
code { font-family: var(--font-label); font-size: 0.9em; }
.page {
max-width: var(--page-max);
margin-inline: auto;
display: grid;
gap: var(--space-3xl);
padding-block: var(--space-md) var(--space-3xl);
}
/* Nav */
.nav {
display: flex;
align-items: center;
justify-content: space-between;
padding-block: var(--space-md);
border-bottom: 0.5px solid var(--color-rule);
}
.wordmark {
font-family: var(--font-label);
font-size: var(--text-xs);
font-weight: 500;
letter-spacing: var(--tracking-label);
text-transform: uppercase;
color: var(--color-ink);
}
.nav__note {
font-family: var(--font-label);
font-size: var(--text-xs);
letter-spacing: var(--tracking-label);
text-transform: uppercase;
color: var(--color-muted);
display: inline-flex;
align-items: center;
gap: 0.55em;
}
.mark {
display: inline-block;
width: 0.5em;
height: 0.5em;
background: var(--color-accent);
border-radius: 50%;
}
/* Hero */
.hero {
display: grid;
grid-template-columns: 9fr 3fr;
gap: var(--space-2xl);
align-items: end;
padding-block: var(--space-xl) var(--space-md);
}
@media (max-width: 58rem) {
.hero { grid-template-columns: 1fr; }
}
.hero__eyebrow {
font-family: var(--font-label);
font-size: var(--text-xs);
letter-spacing: var(--tracking-label);
text-transform: uppercase;
color: var(--color-muted);
margin-bottom: var(--space-xl);
display: inline-flex;
align-items: center;
gap: 0.55em;
}
.hero__title {
font-family: var(--font-display);
font-weight: 300;
font-size: var(--text-display);
line-height: 0.98;
letter-spacing: -0.03em;
font-variation-settings: "opsz" 144;
color: var(--color-ink);
max-width: 12ch;
}
.hero__title em {
font-style: italic;
font-weight: 300;
}
.hero__lede {
font-family: var(--font-serif);
font-weight: 300;
font-size: var(--text-md);
line-height: var(--lh-normal);
color: var(--color-ink-2);
max-width: 38ch;
margin-top: var(--space-xl);
}
.hero__aside {
display: grid;
gap: var(--space-sm);
padding-top: var(--space-xl);
}
.hero__meta-row {
display: grid;
grid-template-columns: auto 1fr;
gap: var(--space-sm) var(--space-md);
padding-block: var(--space-sm);
border-block: 0.5px solid var(--color-rule);
font-family: var(--font-label);
font-size: var(--text-xs);
letter-spacing: var(--tracking-label);
text-transform: uppercase;
color: var(--color-muted);
}
.hero__meta-row dt { color: var(--color-muted); }
.hero__meta-row dd { color: var(--color-ink); margin: 0; }
/* Section scaffolding */
.section {
display: grid;
grid-template-columns: 10rem 1fr;
gap: var(--space-xl);
align-items: start;
}
@media (max-width: 50rem) {
.section { grid-template-columns: 1fr; gap: var(--space-md); }
}
.section__label {
font-family: var(--font-label);
font-size: var(--text-xs);
letter-spacing: var(--tracking-label);
text-transform: uppercase;
color: var(--color-muted);
padding-top: 0.35rem;
}
.section__label .num { color: var(--color-ink); font-weight: 500; }
.section__body {
display: grid;
gap: var(--space-lg);
}
.section__title {
font-family: var(--font-display);
font-weight: 300;
font-style: italic;
font-size: var(--text-2xl);
line-height: var(--lh-snug);
letter-spacing: var(--tracking-tight);
color: var(--color-ink);
max-width: 22ch;
font-variation-settings: "opsz" 72;
}
.section__prose {
font-family: var(--font-body);
font-size: var(--text-md);
line-height: var(--lh-normal);
color: var(--color-ink-2);
max-width: var(--measure);
}
/* Loaves list */
.loaves {
display: grid;
gap: 0;
}
.loaf {
display: grid;
grid-template-columns: 2rem 10rem 1fr auto;
gap: var(--space-md);
align-items: baseline;
padding-block: var(--space-md);
border-top: 0.5px solid var(--color-rule);
}
.loaf:last-child { border-bottom: 0.5px solid var(--color-rule); }
@media (max-width: 50rem) {
.loaf {
grid-template-columns: 2rem 1fr;
grid-template-rows: auto auto;
}
.loaf__desc { grid-column: 1 / -1; padding-left: 2.75rem; }
.loaf__days { grid-column: 1 / -1; padding-left: 2.75rem; color: var(--color-muted); }
}
.loaf__num {
font-family: var(--font-label);
font-size: var(--text-xs);
letter-spacing: var(--tracking-label);
color: var(--color-muted);
font-variant-numeric: tabular-nums;
}
.loaf__name {
font-family: var(--font-display);
font-weight: 400;
font-size: var(--text-lg);
line-height: 1;
letter-spacing: -0.01em;
color: var(--color-ink);
font-variation-settings: "opsz" 48;
}
.loaf__desc {
font-family: var(--font-body);
font-size: var(--text-sm);
line-height: var(--lh-normal);
color: var(--color-ink-2);
max-width: 52ch;
}
.loaf__days {
font-family: var(--font-label);
font-size: var(--text-xs);
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--color-muted);
white-space: nowrap;
}
/* Pull quote */
.pull {
font-family: var(--font-display);
font-style: italic;
font-weight: 300;
font-size: clamp(1.75rem, 3vw + 1rem, 2.75rem);
line-height: 1.15;
letter-spacing: -0.02em;
color: var(--color-ink);
max-width: 26ch;
padding-block: var(--space-lg);
font-variation-settings: "opsz" 96;
}
.pull__attr {
display: block;
margin-top: var(--space-md);
font-family: var(--font-label);
font-style: normal;
font-size: var(--text-xs);
font-weight: 400;
letter-spacing: var(--tracking-label);
text-transform: uppercase;
color: var(--color-muted);
}
/* Where */
.where {
display: grid;
gap: var(--space-sm);
}
.where dt {
font-family: var(--font-label);
font-size: var(--text-xs);
letter-spacing: var(--tracking-label);
text-transform: uppercase;
color: var(--color-muted);
margin-bottom: 0.25rem;
}
.where dd {
font-family: var(--font-serif);
font-weight: 400;
font-size: var(--text-md);
color: var(--color-ink);
margin: 0;
}
.where .row {
display: grid;
grid-template-columns: 8rem 1fr;
gap: var(--space-md);
align-items: baseline;
padding-block: var(--space-sm);
border-top: 0.5px solid var(--color-rule);
}
.where .row:last-of-type { border-bottom: 0.5px solid var(--color-rule); }
/* Signup form */
.signup {
background: var(--color-paper-2);
padding: var(--space-xl) var(--space-xl) var(--space-2xl);
border: 0.5px solid var(--color-rule);
}
.signup__head {
font-family: var(--font-display);
font-weight: 300;
font-style: italic;
font-size: var(--text-2xl);
line-height: var(--lh-snug);
letter-spacing: var(--tracking-tight);
color: var(--color-ink);
max-width: 20ch;
font-variation-settings: "opsz" 72;
}
.signup__note {
font-family: var(--font-body);
font-size: var(--text-sm);
line-height: var(--lh-normal);
color: var(--color-ink-2);
max-width: 46ch;
margin-top: var(--space-md);
margin-bottom: var(--space-lg);
}
.form {
display: grid;
gap: var(--space-sm);
max-width: 32rem;
}
.form__label {
font-family: var(--font-label);
font-size: var(--text-xs);
letter-spacing: var(--tracking-label);
text-transform: uppercase;
color: var(--color-muted);
}
.form__row {
display: grid;
grid-template-columns: 1fr auto;
gap: 0;
border-bottom: 1px solid var(--color-ink);
}
.form__input {
font-family: var(--font-body);
font-size: var(--text-md);
padding: var(--space-sm) 0;
background: transparent;
border: 0;
color: var(--color-ink);
min-height: 44px;
}
.form__input::placeholder { color: var(--color-muted); }
.form__input:focus-visible { outline: none; }
.form__row:focus-within { border-bottom-color: var(--color-accent); }
.form__submit {
font-family: var(--font-label);
font-size: var(--text-xs);
letter-spacing: var(--tracking-label);
text-transform: uppercase;
color: var(--color-ink);
padding: 0 var(--space-md);
min-height: 44px;
align-self: stretch;
transition: color var(--dur-micro) var(--ease-out);
cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
.form__submit:hover { color: var(--color-accent); }
}
.form__submit:disabled { color: var(--color-muted); cursor: not-allowed; }
.form__hint {
font-family: var(--font-body);
font-size: var(--text-xs);
color: var(--color-muted);
margin-top: var(--space-xs);
line-height: var(--lh-normal);
}
.form__hint--error { color: var(--color-error); }
.form__hint--success { color: var(--color-success); }
.form[data-state="error"] .form__row { border-bottom-color: var(--color-error); }
.form[data-state="success"] .form__row { border-bottom-color: var(--color-success); }
/* Footer */
.foot {
display: grid;
grid-template-columns: 1fr auto;
gap: var(--space-lg);
padding-top: var(--space-xl);
border-top: 1px solid var(--color-ink);
font-family: var(--font-label);
font-size: var(--text-xs);
letter-spacing: 0.04em;
color: var(--color-muted);
align-items: end;
}
.foot__address { line-height: 1.7; }
.foot__credit { text-align: right; }
.foot__credit a { color: var(--color-ink); }
/* Reveals — one orchestrated entrance */
.reveal {
opacity: 0;
transform: translateY(8px);
animation: reveal var(--dur-long) var(--ease-out) forwards;
animation-delay: calc(var(--i, 0) * 80ms);
}
@keyframes reveal {
to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
.reveal {
transform: none;
animation-duration: 150ms;
}
@keyframes reveal { to { opacity: 1; } }
*, *::before, *::after {
animation-duration: 150ms !important;
transition-duration: 150ms !important;
}
}
</style>
</head>
<body>
<main class="page">
<!-- Nav -->
<nav class="nav reveal" style="--i:0" aria-label="Primary">
<span class="wordmark">Marlow Street Bread</span>
<span class="nav__note"><span class="mark" aria-hidden="true"></span>Opens 8 May</span>
</nav>
<!-- Hero -->
<header class="hero reveal" style="--i:1">
<div>
<p class="hero__eyebrow">
<span class="mark" aria-hidden="true"></span>
Opening · 8 May 2026
</p>
<h1 class="hero__title">
Bread, when its <em>ready.</em>
</h1>
<p class="hero__lede">
A naturally leavened loaf, made with local flour and a thirty-six hour ferment. A few of them, baked daily. When theyre gone, we close.
</p>
</div>
<aside class="hero__aside">
<dl class="hero__meta-row">
<dt>Kind</dt>
<dd>Neighborhood bakery</dd>
<dt>Where</dt>
<dd>137 Marlow St</dd>
<dt>Open</dt>
<dd>Tue Sat, 8 a.m.</dd>
</dl>
</aside>
</header>
<!-- What we bake -->
<section class="section reveal" style="--i:2" aria-labelledby="s-loaves">
<div class="section__label">
<span class="num">01</span> &nbsp;&nbsp; What we bake
</div>
<div class="section__body">
<h2 class="section__title" id="s-loaves">
A short list, done properly.
</h2>
<ul class="loaves">
<li class="loaf">
<span class="loaf__num">01</span>
<span class="loaf__name">Country</span>
<span class="loaf__desc">Stone-milled wheat, a little rye, a long cold ferment. Our everyday loaf.</span>
<span class="loaf__days">Daily</span>
</li>
<li class="loaf">
<span class="loaf__num">02</span>
<span class="loaf__name">Seeded</span>
<span class="loaf__desc">Pumpkin, sesame, poppy. The country base with a denser crumb.</span>
<span class="loaf__days">Daily</span>
</li>
<li class="loaf">
<span class="loaf__num">03</span>
<span class="loaf__name">Miche</span>
<span class="loaf__desc">A broad round, 1.4 kg. Keeps four days on the counter, longer under cloth.</span>
<span class="loaf__days">Fri &amp; Sat</span>
</li>
<li class="loaf">
<span class="loaf__num">04</span>
<span class="loaf__name">Rye</span>
<span class="loaf__desc">Seventy percent rye, caraway, a little molasses. Dense, dark, patient.</span>
<span class="loaf__days">Thursdays</span>
</li>
</ul>
<p class="section__prose">
We bake in small numbers because wed rather bake four loaves well than forty in a hurry. If a loaf is sold out by ten, thats how it goes.
</p>
</div>
</section>
<!-- Pull quote -->
<blockquote class="section reveal" style="--i:3">
<div class="section__label">
<span class="num">02</span> &nbsp;&nbsp; Why
</div>
<p class="pull">
“Good bread is mostly time — a long fermentation, a patient oven, and flour that wasnt rushed.”
<cite class="pull__attr">— from the opening letter</cite>
</p>
</blockquote>
<!-- Flour & farmers -->
<section class="section reveal" style="--i:4" aria-labelledby="s-flour">
<div class="section__label">
<span class="num">03</span> &nbsp;&nbsp; Flour &amp; farmers
</div>
<div class="section__body">
<h2 class="section__title" id="s-flour">
Milled in Paxton, grown twenty-three miles away.
</h2>
<p class="section__prose">
Our flour is stone-milled at the Paxton mill, north of town. The wheat comes from two farms — Linnea Bergs in the valley and the Holdfast Co-op on the ridge. We work with what they grow, when they grow it, which means the flour shifts a little with the seasons. We think thats a feature.
</p>
</div>
</section>
<!-- Where -->
<section class="section reveal" style="--i:5" aria-labelledby="s-where">
<div class="section__label">
<span class="num">04</span> &nbsp;&nbsp; When &amp; where
</div>
<div class="section__body">
<h2 class="section__title" id="s-where">
Marlow Street, off Fifth.
</h2>
<dl class="where">
<div class="row"><dt>Address</dt><dd>137 Marlow Street</dd></div>
<div class="row"><dt>Opening</dt><dd>8 May 2026, 8 a.m.</dd></div>
<div class="row"><dt>Hours</dt><dd>Tuesday — Saturday, 8 a.m. until sold out (usually by 1 p.m.)</dd></div>
<div class="row"><dt>Closed</dt><dd>Sunday &amp; Monday</dd></div>
</dl>
</div>
</section>
<!-- Signup -->
<section class="section reveal" style="--i:6" aria-labelledby="s-signup">
<div class="section__label">
<span class="num">05</span> &nbsp;&nbsp; The notice
</div>
<div class="signup">
<h2 class="signup__head" id="s-signup">
Well write when the doors open.
</h2>
<p class="signup__note">
One email on opening day, and one a week later with the first weekends hours. Nothing else. Your address stays with us.
</p>
<form class="form" id="signup-form" novalidate>
<label class="form__label" for="email">Email address</label>
<div class="form__row">
<input
class="form__input"
type="email"
id="email"
name="email"
autocomplete="email"
required
placeholder="you@example.com"
aria-describedby="email-hint"
/>
<button class="form__submit" type="submit">Send me the notice &rarr;</button>
</div>
<p class="form__hint" id="email-hint">We'll send two emails. No list-sharing.</p>
</form>
</div>
</section>
<!-- Footer -->
<footer class="foot reveal" style="--i:7" role="contentinfo">
<div class="foot__address">
Marlow Street Bread &nbsp;·&nbsp; 137 Marlow Street &nbsp;·&nbsp; Tue Sat from 8 a.m.
</div>
<div class="foot__credit">
Designed with <a href="/" class="link">Hallmark</a>
</div>
</footer>
</main>
<script type="module">
const form = document.getElementById("signup-form");
const input = document.getElementById("email");
const hint = document.getElementById("email-hint");
const submit = form.querySelector(".form__submit");
form.addEventListener("submit", (e) => {
e.preventDefault();
const value = input.value.trim();
const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
if (!valid) {
form.dataset.state = "error";
input.setAttribute("aria-invalid", "true");
hint.className = "form__hint form__hint--error";
hint.textContent = "That doesnt look like an email address. Try again?";
return;
}
submit.disabled = true;
submit.textContent = "Sending…";
// Demo only — pretend we sent it.
setTimeout(() => {
form.dataset.state = "success";
input.removeAttribute("aria-invalid");
hint.className = "form__hint form__hint--success";
hint.textContent = "On the list. Well be in touch on 8 May.";
submit.textContent = "Sent";
}, 350);
});
input.addEventListener("input", () => {
if (form.dataset.state === "error") {
delete form.dataset.state;
input.removeAttribute("aria-invalid");
hint.className = "form__hint";
hint.textContent = "We'll send two emails. No list-sharing.";
}
});
</script>
</body>
</html>
-886
View File
@@ -1,886 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="color-scheme" content="light" />
<title>Lectern — a personal library</title>
<meta name="description" content="A keyboard-first command palette for a personal reading list. Press ⌘K to find any book in seconds." />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" />
<style>
/*
Lectern — keyboard-first book library.
Tone: technical, calm. Anchor hue: 240 (cool slate).
Structure: single column, hairline rules, typographic-only CTAs, no reveals.
*/
:root {
--color-paper: oklch(97% 0.005 250);
--color-paper-2: oklch(94% 0.007 250);
--color-paper-3: oklch(91% 0.008 250);
--color-rule: oklch(86% 0.008 245);
--color-rule-2: oklch(74% 0.010 240);
--color-muted: oklch(54% 0.012 245);
--color-neutral: oklch(40% 0.014 245);
--color-ink-2: oklch(28% 0.014 245);
--color-ink: oklch(18% 0.016 245);
--color-accent: oklch(48% 0.16 240);
--color-focus: oklch(48% 0.16 240);
--color-mark-bg: oklch(92% 0.06 95);
--color-row-hover: oklch(94% 0.012 245);
--color-row-active: oklch(88% 0.030 240);
--font-sans: "IBM Plex Sans", "Söhne", ui-sans-serif, sans-serif;
--font-mono: "JetBrains Mono", "IBM Plex Mono", ui-monospace, monospace;
--text-xs: 0.75rem;
--text-sm: 0.8125rem;
--text-base: 0.9375rem;
--text-md: 1.0625rem;
--text-lg: 1.25rem;
--text-xl: 1.5rem;
--lh-tight: 1.1;
--lh-snug: 1.3;
--lh-normal: 1.5;
--lh-relaxed: 1.65;
--tracking-label: 0.10em;
--space-2xs: 0.25rem;
--space-xs: 0.5rem;
--space-sm: 0.75rem;
--space-md: 1rem;
--space-lg: 1.5rem;
--space-xl: 2rem;
--space-2xl: 3rem;
--space-3xl: 4.5rem;
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
--ease-in: cubic-bezier(0.7, 0, 0.84, 0);
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
--dur-micro: 120ms;
--dur-short: 220ms;
--dur-long: 300ms;
--page-max: 58rem;
--page-gutter: clamp(1rem, 4vw, 2.5rem);
}
*, *::before, *::after { box-sizing: border-box; }
body, h1, h2, h3, p, dl, dd { margin: 0; }
button, input { font: inherit; color: inherit; }
button { background: none; border: 0; padding: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
ul, ol { margin: 0; padding: 0; list-style: none; }
/* Focus rings — instant, never animated in/out */
:focus { outline: none; }
:focus-visible {
outline: 2px solid var(--color-focus);
outline-offset: 2px;
border-radius: 1px;
}
html {
background: var(--color-paper);
color: var(--color-ink);
}
body {
font-family: var(--font-sans);
font-weight: 400;
font-size: var(--text-base);
line-height: var(--lh-relaxed);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
min-height: 100dvh;
padding-inline: max(var(--page-gutter), env(safe-area-inset-left));
padding-block-end: env(safe-area-inset-bottom);
}
::selection {
background: var(--color-accent);
color: var(--color-paper);
}
/* Page scaffold */
.page {
max-width: var(--page-max);
margin-inline: auto;
padding-block: var(--space-xl) var(--space-3xl);
}
/* Header */
.head {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: var(--space-md);
padding-block: var(--space-md);
border-bottom: 1px solid var(--color-ink);
margin-bottom: var(--space-xl);
}
.wordmark {
font-family: var(--font-mono);
font-size: var(--text-sm);
font-weight: 500;
letter-spacing: var(--tracking-label);
text-transform: uppercase;
color: var(--color-ink);
}
.wordmark .count {
color: var(--color-muted);
font-weight: 400;
margin-left: 0.6rem;
}
.meta {
font-family: var(--font-mono);
font-size: var(--text-xs);
color: var(--color-muted);
letter-spacing: var(--tracking-label);
text-transform: uppercase;
}
/* Status legend */
.legend {
display: flex;
gap: var(--space-lg);
font-family: var(--font-mono);
font-size: var(--text-xs);
letter-spacing: 0.06em;
color: var(--color-muted);
padding-bottom: var(--space-md);
margin-bottom: var(--space-xs);
}
.legend__item { display: inline-flex; align-items: center; gap: 0.4em; }
/* Book list (ambient) */
.books { display: grid; gap: 0; }
.book {
display: grid;
grid-template-columns: 4rem 1fr 1.25rem;
gap: var(--space-md);
padding: 0.6rem 0.4rem;
align-items: baseline;
border-top: 0.5px solid var(--color-rule);
transition: background-color var(--dur-micro) var(--ease-out);
}
.book:last-child { border-bottom: 0.5px solid var(--color-rule); }
@media (hover: hover) and (pointer: fine) {
.book:hover { background-color: var(--color-row-hover); }
}
.book__year {
font-family: var(--font-mono);
font-size: var(--text-xs);
color: var(--color-muted);
font-variant-numeric: tabular-nums;
}
.book__title {
font-size: var(--text-md);
font-weight: 500;
color: var(--color-ink);
letter-spacing: -0.005em;
line-height: 1.25;
}
.book__author {
font-size: var(--text-sm);
color: var(--color-neutral);
margin-left: 0.6rem;
font-weight: 400;
}
.book__status {
font-family: var(--font-mono);
font-size: var(--text-sm);
text-align: center;
color: var(--color-muted);
font-weight: 400;
}
.book__status[data-state="reading"] { color: var(--color-accent); }
.book__status[data-state="read"] { color: var(--color-muted); }
.book__status[data-state="queued"] { color: var(--color-rule-2); }
/* ⌘K invitation — visible, persistent, never animated */
.invite {
position: fixed;
bottom: max(1rem, env(safe-area-inset-bottom));
right: max(1rem, env(safe-area-inset-right));
display: inline-flex;
align-items: center;
gap: 0.6rem;
padding: 0.6rem 0.85rem;
background: var(--color-paper);
border: 1px solid var(--color-ink);
font-family: var(--font-mono);
font-size: var(--text-xs);
letter-spacing: 0.06em;
color: var(--color-ink);
cursor: pointer;
min-height: 36px;
transition: background-color var(--dur-micro) var(--ease-out),
color var(--dur-micro) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
.invite:hover { background: var(--color-ink); color: var(--color-paper); }
}
.invite:active { transition-duration: 60ms; }
.kbd {
display: inline-flex;
align-items: center;
gap: 0.2em;
font-family: var(--font-mono);
font-size: 0.6875rem;
letter-spacing: 0;
padding: 0.05rem 0.4rem;
border: 1px solid currentColor;
opacity: 0.7;
font-weight: 500;
min-height: 1.2em;
}
/* Footer hint */
.foot {
margin-top: var(--space-3xl);
padding-top: var(--space-md);
border-top: 1px solid var(--color-ink);
font-family: var(--font-mono);
font-size: var(--text-xs);
letter-spacing: 0.04em;
color: var(--color-muted);
display: flex;
justify-content: space-between;
gap: var(--space-md);
}
/* ─────────────────────────────────────────────────────────
Command palette — the heart of this demo
───────────────────────────────────────────────────────── */
/* Native <dialog> as palette */
dialog.palette {
padding: 0;
border: 0;
background: transparent;
max-width: 36rem;
width: calc(100% - 2rem);
margin-top: 12vh;
color: var(--color-ink);
}
dialog.palette::backdrop {
background-color: oklch(20% 0.012 245 / 0.45);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
}
/* Entrance animation — content + backdrop, ease-out, 300ms */
dialog.palette[open] {
animation: palette-in var(--dur-long) var(--ease-out) forwards;
}
dialog.palette[open]::backdrop {
animation: backdrop-in var(--dur-long) var(--ease-out) forwards;
}
@keyframes palette-in {
from { opacity: 0; transform: scale(0.96); }
to { opacity: 1; transform: scale(1); }
}
@keyframes backdrop-in {
from { opacity: 0; }
to { opacity: 1; }
}
/* Exit — applied via .closing class for the brief moment before close */
dialog.palette.closing {
animation: palette-out 220ms var(--ease-in) forwards;
}
dialog.palette.closing::backdrop {
animation: backdrop-out 220ms var(--ease-in) forwards;
}
@keyframes palette-out {
to { opacity: 0; transform: scale(0.98); }
}
@keyframes backdrop-out {
to { opacity: 0; }
}
.pal {
background: var(--color-paper);
border: 1px solid var(--color-rule-2);
box-shadow: 0 24px 60px -20px oklch(20% 0.05 245 / 0.25);
display: grid;
grid-template-rows: auto 1fr auto;
max-height: 70vh;
}
/* Input row */
.pal__input-row {
display: grid;
grid-template-columns: 2.25rem 1fr auto;
align-items: center;
gap: var(--space-sm);
padding: 0.85rem 1rem;
border-bottom: 1px solid var(--color-rule);
}
.pal__leading {
font-family: var(--font-mono);
font-size: var(--text-md);
color: var(--color-muted);
text-align: center;
line-height: 1;
}
.pal__input {
width: 100%;
background: transparent;
border: 0;
padding: 0.4rem 0;
font-family: var(--font-sans);
font-size: var(--text-md);
color: var(--color-ink);
letter-spacing: -0.005em;
min-height: 32px;
}
.pal__input:focus { outline: none; }
.pal__input::placeholder { color: var(--color-muted); }
.pal__count {
font-family: var(--font-mono);
font-size: var(--text-xs);
color: var(--color-muted);
letter-spacing: 0.06em;
font-variant-numeric: tabular-nums;
}
/* Results scroll area */
.pal__results {
position: relative;
overflow-y: auto;
padding: 0.4rem 0;
scroll-padding-block: 0.4rem;
scrollbar-color: var(--color-rule-2) transparent;
}
.pal__results::-webkit-scrollbar { width: 6px; }
.pal__results::-webkit-scrollbar-track { background: transparent; }
.pal__results::-webkit-scrollbar-thumb { background: var(--color-rule-2); }
/* The selection tracker — single element, transitions translateY between rows.
Linear / Raycast / Vercel pattern: items don't move, the tracker does. */
.pal__tracker {
position: absolute;
left: 0.4rem;
right: 0.4rem;
height: 2.4rem;
background: var(--color-row-active);
pointer-events: none;
transform: translateY(0);
transition: transform 120ms var(--ease-out),
opacity var(--dur-micro) var(--ease-out);
}
.pal__results[data-empty="true"] .pal__tracker { opacity: 0; }
/* Result row */
.pal__row {
position: relative;
display: grid;
grid-template-columns: 3.25rem 1fr auto 1.5rem;
gap: var(--space-sm);
align-items: baseline;
padding: 0.55rem 1rem;
cursor: pointer;
height: 2.4rem;
transition: background-color 0ms; /* hover is instant */
}
@media (hover: hover) and (pointer: fine) {
.pal__row:hover { background-color: var(--color-row-hover); }
}
.pal__row[aria-selected="true"] { background: transparent; /* tracker shows selection */ }
.pal__row__year {
font-family: var(--font-mono);
font-size: var(--text-xs);
color: var(--color-muted);
font-variant-numeric: tabular-nums;
}
.pal__row__title {
font-size: var(--text-base);
color: var(--color-ink);
font-weight: 500;
letter-spacing: -0.003em;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.pal__row__author {
font-size: var(--text-sm);
color: var(--color-neutral);
font-weight: 400;
margin-left: 0.5rem;
}
.pal__row__status {
font-family: var(--font-mono);
font-size: var(--text-sm);
text-align: center;
color: var(--color-muted);
}
.pal__row__status[data-state="reading"] { color: var(--color-accent); }
.pal__row__status[data-state="queued"] { color: var(--color-rule-2); }
/* Match highlighting */
.pal__row mark {
background: var(--color-mark-bg);
color: inherit;
padding: 0;
border-radius: 1px;
box-shadow: 0 -1px 0 var(--color-accent) inset;
}
/* Empty state */
.pal__empty {
padding: 2rem 1rem;
text-align: center;
font-family: var(--font-sans);
color: var(--color-muted);
font-size: var(--text-sm);
line-height: var(--lh-relaxed);
}
.pal__empty .q {
color: var(--color-ink);
font-weight: 500;
}
/* Footer (kbd hints) */
.pal__foot {
display: flex;
gap: var(--space-md);
justify-content: flex-end;
align-items: center;
padding: 0.55rem 1rem;
border-top: 1px solid var(--color-rule);
font-family: var(--font-mono);
font-size: 0.6875rem;
letter-spacing: 0.06em;
color: var(--color-muted);
}
.pal__hint { display: inline-flex; align-items: center; gap: 0.4rem; }
.pal__hint .kbd { color: var(--color-ink-2); border-color: var(--color-rule-2); opacity: 1; }
/* Reduced motion: collapse spatial motion to opacity */
@media (prefers-reduced-motion: reduce) {
dialog.palette[open],
dialog.palette[open]::backdrop,
dialog.palette.closing,
dialog.palette.closing::backdrop {
animation-duration: 150ms !important;
}
@keyframes palette-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes palette-out { to { opacity: 0; } }
.pal__tracker { transition: none; }
*, *::before, *::after {
animation-duration: 150ms !important;
transition-duration: 150ms !important;
}
}
/* Mobile palette tweaks */
@media (max-width: 36rem) {
dialog.palette { margin-top: 4vh; max-height: 92vh; }
.pal { max-height: 90vh; }
.pal__row { grid-template-columns: 2.5rem 1fr 1.5rem; }
.pal__row__author { display: block; margin-left: 0; }
.pal__row__status { display: none; }
}
</style>
</head>
<body>
<main class="page">
<header class="head">
<h1 class="wordmark">
Lectern
<span class="count" id="library-count"></span>
</h1>
<p class="meta">A personal library</p>
</header>
<div class="legend" aria-label="Status legend">
<span class="legend__item"><span style="color: var(--color-accent)"></span> reading</span>
<span class="legend__item"><span style="color: var(--color-muted)"></span> read</span>
<span class="legend__item"><span style="color: var(--color-rule-2)"></span> queued</span>
</div>
<ul class="books" id="library" role="list" aria-label="Books"></ul>
<footer class="foot">
<span>Press <span class="kbd">⌘K</span> to find a book</span>
<span>esc to close · ↵ to open</span>
</footer>
</main>
<button class="invite" type="button" id="invite" aria-label="Open command palette">
<span>Find a book</span>
<span class="kbd">⌘K</span>
</button>
<dialog class="palette" id="palette" aria-label="Command palette">
<div class="pal">
<div class="pal__input-row">
<span class="pal__leading" aria-hidden="true">/</span>
<input
class="pal__input"
id="pal-input"
type="search"
autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
placeholder="Search 31 books"
aria-label="Search books"
aria-controls="pal-results"
aria-activedescendant=""
/>
<span class="pal__count" id="pal-count">31</span>
</div>
<div class="pal__results" id="pal-results" role="listbox" aria-label="Search results">
<div class="pal__tracker" aria-hidden="true"></div>
</div>
<div class="pal__foot">
<span class="pal__hint"><span class="kbd"></span><span class="kbd"></span> navigate</span>
<span class="pal__hint"><span class="kbd"></span> open</span>
<span class="pal__hint"><span class="kbd">esc</span> close</span>
</div>
</div>
</dialog>
<script type="module">
/* ─── Data: ~31 books a developer might keep on a personal reading list ── */
const BOOKS = [
{ y: 1968, t: "The Art of Computer Programming", a: "Donald E. Knuth", s: "queued" },
{ y: 1974, t: "Zen and the Art of Motorcycle Maintenance", a: "Robert M. Pirsig", s: "read" },
{ y: 1975, t: "The Mythical Man-Month", a: "Frederick P. Brooks Jr.", s: "read" },
{ y: 1977, t: "A Pattern Language", a: "Christopher Alexander", s: "read" },
{ y: 1979, t: "Gödel, Escher, Bach", a: "Douglas R. Hofstadter", s: "reading" },
{ y: 1981, t: "The Soul of a New Machine", a: "Tracy Kidder", s: "read" },
{ y: 1983, t: "The Visual Display of Quantitative Information", a: "Edward R. Tufte", s: "read" },
{ y: 1985, t: "Structure and Interpretation of Computer Programs", a: "Abelson & Sussman", s: "queued" },
{ y: 1985, t: "Tools for Thought", a: "Howard Rheingold", s: "read" },
{ y: 1988, t: "The Design of Everyday Things", a: "Don Norman", s: "read" },
{ y: 1989, t: "The Cuckoo's Egg", a: "Cliff Stoll", s: "read" },
{ y: 1990, t: "Envisioning Information", a: "Edward R. Tufte", s: "read" },
{ y: 1992, t: "The Elements of Typographic Style", a: "Robert Bringhurst", s: "read" },
{ y: 1994, t: "Out of Control", a: "Kevin Kelly", s: "queued" },
{ y: 1996, t: "Where Wizards Stay Up Late", a: "Hafner & Lyon", s: "read" },
{ y: 1997, t: "The Innovator's Dilemma", a: "Clayton M. Christensen", s: "read" },
{ y: 1998, t: "Seeing Like a State", a: "James C. Scott", s: "reading" },
{ y: 1999, t: "Code", a: "Charles Petzold", s: "read" },
{ y: 1999, t: "The Pragmatic Programmer", a: "Hunt & Thomas", s: "read" },
{ y: 1999, t: "Refactoring", a: "Martin Fowler", s: "read" },
{ y: 2000, t: "Don't Make Me Think", a: "Steve Krug", s: "read" },
{ y: 2003, t: "Domain-Driven Design", a: "Eric Evans", s: "read" },
{ y: 2003, t: "Universal Principles of Design", a: "Lidwell, Holden & Butler", s: "queued" },
{ y: 2004, t: "Hackers and Painters", a: "Paul Graham", s: "read" },
{ y: 2004, t: "Working Effectively with Legacy Code", a: "Michael Feathers", s: "read" },
{ y: 2008, t: "Clean Code", a: "Robert C. Martin", s: "read" },
{ y: 2011, t: "The Information", a: "James Gleick", s: "read" },
{ y: 2017, t: "Designing Data-Intensive Applications", a: "Martin Kleppmann", s: "reading" },
{ y: 2019, t: "Software Engineering at Google", a: "Winters, Manshreck & Wright", s: "queued" },
{ y: 2021, t: "A Philosophy of Software Design", a: "John Ousterhout", s: "read" },
{ y: 2024, t: "Tidy First?", a: "Kent Beck", s: "queued" }
];
/* ─── Render ambient library list ── */
const STATUS_GLYPH = { reading: "●", read: "✓", queued: "○" };
const lib = document.getElementById("library");
const libraryCount = document.getElementById("library-count");
libraryCount.textContent = `· ${BOOKS.length} books`;
function renderLibrary() {
const sorted = [...BOOKS].sort((a, b) => b.y - a.y);
lib.innerHTML = sorted.map((b) => `
<li class="book" role="listitem">
<span class="book__year">${b.y}</span>
<span><span class="book__title">${escape(b.t)}</span><span class="book__author">${escape(b.a)}</span></span>
<span class="book__status" data-state="${b.s}" title="${b.s}" aria-label="${b.s}">${STATUS_GLYPH[b.s]}</span>
</li>
`).join("");
}
function escape(s) {
return s.replace(/[&<>"']/g, (c) => ({ "&":"&amp;", "<":"&lt;", ">":"&gt;", '"':"&quot;", "'":"&#39;" }[c]));
}
renderLibrary();
/* ─── Fuzzy match: in-order character match, score by spread + length ── */
function fuzzyMatch(query, text) {
if (!query) return { match: true, score: 0, positions: [] };
const q = query.toLowerCase();
const t = text.toLowerCase();
const positions = [];
let qi = 0;
for (let ti = 0; ti < t.length && qi < q.length; ti++) {
if (t[ti] === q[qi]) {
positions.push(ti);
qi++;
}
}
if (qi !== q.length) return { match: false };
const spread = positions[positions.length - 1] - positions[0];
// Lower score is better
const score = spread + text.length * 0.2;
return { match: true, score, positions };
}
function highlight(text, positions) {
if (!positions.length) return escape(text);
const set = new Set(positions);
let out = "";
let inMark = false;
for (let i = 0; i < text.length; i++) {
if (set.has(i) && !inMark) { out += "<mark>"; inMark = true; }
if (!set.has(i) && inMark) { out += "</mark>"; inMark = false; }
const c = text[i];
out += ({ "&":"&amp;", "<":"&lt;", ">":"&gt;", '"':"&quot;", "'":"&#39;" }[c]) || c;
}
if (inMark) out += "</mark>";
return out;
}
/* ─── Palette state ── */
const dlg = document.getElementById("palette");
const input = document.getElementById("pal-input");
const results = document.getElementById("pal-results");
const tracker = results.querySelector(".pal__tracker");
const count = document.getElementById("pal-count");
const invite = document.getElementById("invite");
let currentResults = [];
let selectedIndex = 0;
let debounce = null;
function search(query) {
const matches = BOOKS
.map((b, i) => {
const titleHit = fuzzyMatch(query, b.t);
const authorHit = fuzzyMatch(query, b.a);
// Best of (title, author); prefer title hits
if (titleHit.match) return { book: b, on: "title", hit: titleHit, score: titleHit.score };
if (authorHit.match) return { book: b, on: "author", hit: authorHit, score: authorHit.score + 100 };
return null;
})
.filter(Boolean)
.sort((a, z) => a.score - z.score);
return matches;
}
function render() {
const q = input.value.trim();
currentResults = search(q);
count.textContent = String(currentResults.length);
// clamp selection
if (selectedIndex >= currentResults.length) selectedIndex = currentResults.length - 1;
if (selectedIndex < 0) selectedIndex = 0;
// Empty state
if (!currentResults.length) {
results.dataset.empty = "true";
// Preserve the tracker element when re-rendering
results.innerHTML = "";
results.appendChild(tracker);
const empty = document.createElement("div");
empty.className = "pal__empty";
empty.innerHTML = q
? `Nothing matches <span class="q">“${escape(q)}”</span>. Try a shorter query, or check the year.`
: `Start typing to search.`;
results.appendChild(empty);
input.setAttribute("aria-activedescendant", "");
return;
}
results.dataset.empty = "false";
const html = currentResults.map((m, i) => {
const { book, on, hit } = m;
const titleHtml = on === "title" ? highlight(book.t, hit.positions) : escape(book.t);
const authorHtml = on === "author" ? highlight(book.a, hit.positions) : escape(book.a);
return `
<div class="pal__row" role="option" id="pal-row-${i}" data-index="${i}" aria-selected="${i === selectedIndex}">
<span class="pal__row__year">${book.y}</span>
<span><span class="pal__row__title">${titleHtml}</span><span class="pal__row__author">${authorHtml}</span></span>
<span></span>
<span class="pal__row__status" data-state="${book.s}" aria-label="${book.s}" title="${book.s}">${STATUS_GLYPH[book.s]}</span>
</div>
`;
}).join("");
results.innerHTML = "";
results.appendChild(tracker);
const wrap = document.createElement("div");
wrap.innerHTML = html;
while (wrap.firstChild) results.appendChild(wrap.firstChild);
input.setAttribute("aria-activedescendant", `pal-row-${selectedIndex}`);
moveTracker();
}
function moveTracker() {
const row = results.querySelector(`[data-index="${selectedIndex}"]`);
if (!row) { tracker.style.opacity = "0"; return; }
tracker.style.opacity = "1";
// Position the tracker over the selected row
const rowOffset = row.offsetTop;
tracker.style.transform = `translateY(${rowOffset}px)`;
// Ensure visible in scroll area
row.scrollIntoView({ block: "nearest", behavior: "instant" });
}
function move(delta) {
if (!currentResults.length) return;
selectedIndex = (selectedIndex + delta + currentResults.length) % currentResults.length;
results.querySelectorAll(".pal__row").forEach((el) => {
el.setAttribute("aria-selected", el.dataset.index == selectedIndex ? "true" : "false");
});
input.setAttribute("aria-activedescendant", `pal-row-${selectedIndex}`);
moveTracker();
}
function commit() {
const m = currentResults[selectedIndex];
if (!m) return;
// "Open the book" — silent success: just close the palette.
// In a real app, this would route to /book/<slug>.
console.log("Open:", m.book.t);
close();
}
function open() {
input.value = "";
selectedIndex = 0;
render();
dlg.showModal();
// Auto-focus input — already handled by autofocus+ showModal
requestAnimationFrame(() => input.focus());
}
function close() {
// Apply a brief exit animation then close
const reduced = matchMedia("(prefers-reduced-motion: reduce)").matches;
if (reduced) {
dlg.close();
return;
}
dlg.classList.add("closing");
const onEnd = () => {
dlg.classList.remove("closing");
dlg.close();
dlg.removeEventListener("animationend", onEnd);
};
dlg.addEventListener("animationend", onEnd);
}
/* ─── Event wiring ── */
// ⌘K / Ctrl+K to open
document.addEventListener("keydown", (e) => {
if ((e.metaKey || e.ctrlKey) && (e.key === "k" || e.key === "K")) {
e.preventDefault();
if (dlg.open) close(); else open();
}
});
invite.addEventListener("click", open);
// Click outside to close (the dialog backdrop)
dlg.addEventListener("click", (e) => {
const rect = dlg.getBoundingClientRect();
const inDialog = e.clientX >= rect.left && e.clientX <= rect.right
&& e.clientY >= rect.top && e.clientY <= rect.bottom;
if (!inDialog) close();
});
// Cancel (Escape) — let dialog handle its native close, but use our exit animation
dlg.addEventListener("cancel", (e) => {
e.preventDefault();
close();
});
// Input — search-as-you-type, debounced 180ms
input.addEventListener("input", () => {
clearTimeout(debounce);
debounce = setTimeout(render, 180);
// Also reset selection to top on each keystroke
selectedIndex = 0;
});
// Keyboard nav on the input
input.addEventListener("keydown", (e) => {
if (e.key === "ArrowDown") { e.preventDefault(); move(1); }
else if (e.key === "ArrowUp") { e.preventDefault(); move(-1); }
else if (e.key === "Home") { e.preventDefault(); selectedIndex = 0; move(0); }
else if (e.key === "End") { e.preventDefault(); selectedIndex = currentResults.length - 1; move(0); }
else if (e.key === "Enter") { e.preventDefault(); commit(); }
});
// Click a result to select + open
results.addEventListener("click", (e) => {
const row = e.target.closest(".pal__row");
if (!row) return;
selectedIndex = Number(row.dataset.index);
commit();
});
// Hover updates selection visually (matches Linear / Raycast behaviour)
results.addEventListener("mousemove", (e) => {
const row = e.target.closest(".pal__row");
if (!row) return;
const idx = Number(row.dataset.index);
if (idx !== selectedIndex) {
selectedIndex = idx;
results.querySelectorAll(".pal__row").forEach((el) => {
el.setAttribute("aria-selected", el.dataset.index == selectedIndex ? "true" : "false");
});
input.setAttribute("aria-activedescendant", `pal-row-${selectedIndex}`);
moveTracker();
}
});
</script>
</body>
</html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

+2 -3
View File
@@ -5,14 +5,13 @@ Each verb demonstrated on a tiny, realistic input. Folders contain the input, th
| Verb | Folder | Input | What the verb does |
| --- | --- | --- | --- |
| `audit` | [`audit/`](audit/) | An AI-templated landing fragment | Read it, score against the anti-pattern list, return a ranked punch list. **Does not edit.** |
| `refine` | [`refine/`](refine/) | A structurally-OK landing with bad colour / spacing / easings | Apply the ruleset with the smallest possible diff. **Does not redesign.** |
| `redesign` | [`redesign/`](redesign/) | A page with the AI structural template (centered hero · 3 cards · CTA · footer) | Throw out the structure, rebuild with a different fingerprint. **Preserves copy + IA.** |
| `study` | [`study/`](study/) | A described screenshot of a Pentagram-style portfolio | Extract DNA — macrostructure, archetypes, type-pairing role, accent — return a diagnosis report, then rebuild user content with that DNA. **Never copies pixels.** |
Each folder holds:
- **`input.html`** (or `input-description.md` for `study`) — what the user paste / attach.
- **`output.*`** — what the verb produces. For `audit` and `study`, this is a Markdown report. For `refine` and `redesign`, this is a finished `index.html` + `style.css`.
- **`output.*`** — what the verb produces. For `audit` and `study`, this is a Markdown report. For `redesign`, this is a finished `index.html` + `style.css`.
- **`notes.md`** — one page explaining what fired, what reference files loaded, and what the durable artifact is.
The four verb tests together exercise every load path in the skill that the eight default-flow tests don't.
The three verb tests together exercise every load path in the skill that the default-flow tests don't.
-123
View File
@@ -1,123 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Codeline — changelog</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
background: #ffffff;
color: #222222;
line-height: 1.5;
max-width: 720px;
margin: 60px auto;
padding: 0 30px;
}
h1 {
font-size: 38px;
font-weight: 700;
margin-bottom: 8px;
}
.lede {
color: #777777;
margin-bottom: 50px;
font-size: 16px;
}
.release {
margin-bottom: 50px;
padding-bottom: 30px;
border-bottom: 1px solid #eaeaea;
}
.release:last-child { border-bottom: none; }
.release h2 {
font-size: 24px;
font-weight: 600;
margin-bottom: 6px;
}
.release time {
color: #999999;
font-size: 13px;
display: block;
margin-bottom: 18px;
}
.release ul {
list-style: none;
padding: 0;
margin: 0;
}
.release li {
padding: 8px 0 8px 17px;
position: relative;
color: #444444;
}
.release li::before {
content: "—";
position: absolute;
left: 0;
color: #cccccc;
}
.release li.added::before { color: #1aab30; content: "+"; }
.release li.fixed::before { color: #b08017; content: "·"; }
.release li.removed::before { color: #b53d3d; content: ""; }
.release li code {
font-family: monospace;
background: #f4f4f4;
padding: 1px 5px;
border-radius: 3px;
font-size: 13px;
}
.release a {
color: #2050b8;
text-decoration: none;
transition: all 250ms cubic-bezier(0.4, 0.0, 0.2, 1);
}
.release a:hover { color: #4070d8; text-decoration: underline; }
footer {
margin-top: 80px;
padding-top: 20px;
border-top: 1px solid #eaeaea;
color: #aaaaaa;
font-size: 13px;
}
</style>
</head>
<body>
<h1>Codeline · changelog</h1>
<p class="lede">A small dev-tools shop. Releases ship Tuesday mornings, twice a month.</p>
<article class="release">
<h2>v0.7.4</h2>
<time>2026-04-29</time>
<ul>
<li class="added">Add <code>--watch</code> flag to <code>codeline build</code> for incremental rebuilds (closes <a href="#">#412</a>).</li>
<li class="added">Add JSON output mode to <code>codeline lint</code>; pipe-friendly for CI.</li>
<li class="fixed">Fix <code>tsconfig.json</code> resolution failing when <code>extends</code> points outside the workspace root.</li>
<li class="fixed">Fix incorrect exit code (<code>0</code> instead of <code>1</code>) when <code>codeline lint</code> finds errors but no warnings.</li>
<li class="removed">Deprecate <code>--legacy-loader</code>; will be removed in v0.9.</li>
</ul>
</article>
<article class="release">
<h2>v0.7.3</h2>
<time>2026-04-15</time>
<ul>
<li class="fixed">Fix race condition in the file-watcher when many small files change simultaneously (e.g., during a <code>git checkout</code>).</li>
<li class="fixed">Fix the loader silently swallowing parse errors in <code>postcss</code> plugin chains.</li>
<li class="added">Add <code>codeline doctor</code> — a diagnostic command that checks node version, lockfile parity, and config validity.</li>
</ul>
</article>
<article class="release">
<h2>v0.7.2</h2>
<time>2026-04-01</time>
<ul>
<li class="added">Initial public release. Read the <a href="#">getting-started guide</a>.</li>
</ul>
</article>
<footer>
Codeline · MIT licensed · maintained by two engineers in Lisbon.
</footer>
</body>
</html>
-49
View File
@@ -1,49 +0,0 @@
# `refine` verb · what changed and why
## Input
`input.html` — a 95-line single-file changelog page for a fictional dev-tools shop called Codeline. **The structure is correct** — a Long Document macrostructure with three release entries, a header, and a footer. The user is happy with the shape; they want it polished.
## What `refine` does
> *"Apply the ruleset to polish the target in place. Preserve layout structure. Smallest possible diff. No redesign."*
The DOM is **identical**. The CSS rules are rewritten to use OKLCH tokens, named easings, the project's font stack, and the eight-states discipline. No element moved. No element added or removed. No `class` renamed (except where necessary).
## Diff list — what changed and which reference file prompted it
| Change | From | To | Reference |
| --- | --- | --- | --- |
| **1.** Replaced all hex / rgb colours with OKLCH tokens at `:root` | `#222`, `#777`, `#999`, `#444`, `#aaa`, `#eaeaea`, `#1aab30`, `#b08017`, `#b53d3d`, `#2050b8` | `--color-ink`, `--color-ink-soft`, `--color-rule`, `--color-accent`, etc. | [`color.md`](../../../skill/references/color.md) — palette in OKLCH, named tokens by role |
| **2.** Switched paper from pure `#fff` to a tinted near-white (`oklch(98% 0.005 240)`) | `background: #ffffff` | `background: var(--color-paper)` | [`color.md`](../../../skill/references/color.md) — never pure-white as a base |
| **3.** Replaced system font stack with a paired Geist + Geist Mono | `-apple-system, BlinkMacSystemFont, sans-serif` | Geist (display + body), Geist Mono (code) | [`typography.md`](../../../skill/references/typography.md) — display + body pairing, mono for code |
| **4.** Replaced `transition: all 250ms cubic-bezier(0.4, 0.0, 0.2, 1)` on links with explicit-property + named easing | `transition: all 250ms cubic-bezier(0.4, 0.0, 0.2, 1)` | `transition: text-decoration-color var(--dur-micro) var(--ease-out)` | [`microinteractions.md`](../../../skill/references/microinteractions.md) — never `all`; specify the property |
| **5.** Added a 4-pt spacing scale at `:root` (`--space-2xs` through `--space-2xl`) and replaced raw px values | `60px`, `30px`, `8px`, `50px`, `18px`, `17px` | `var(--space-2xl)`, `var(--space-md)`, `var(--space-2xs)`, etc. | [`layout-and-space.md`](../../../skill/references/layout-and-space.md) — 4-pt scale, semantic tokens |
| **6.** Added `:focus-visible` ring to all `<a>` elements with `outline: 2px solid var(--color-accent); outline-offset: 3px;` | (missing) | added | [`interaction-and-states.md`](../../../skill/references/interaction-and-states.md) — eight states; gate 28 |
| **7.** Added `@media (prefers-reduced-motion: reduce)` block to disable transitions | (missing) | added | [`motion.md`](../../../skill/references/motion.md) — gate 29 |
| **8.** Switched typography on the `time` element to monospaced for tabular numerics | sans default | `font-family: var(--font-mono); font-variant-numeric: tabular-nums;` | [`typography.md`](../../../skill/references/typography.md) — mono for dates / data |
| **9.** Switched units to `rem`, fluid type via `clamp()` where appropriate | px throughout | rem + clamp | [`responsive.md`](../../../skill/references/responsive.md) — accessibility-first sizing |
| **10.** Added `html, body { overflow-x: clip; }` for layout-safety | (missing) | added | [`layout-and-space.md`](../../../skill/references/layout-and-space.md) § page-edge clipping; gate 36 |
| **11.** Stamped the file at the top of `<style>` | (no stamp) | `/* Hallmark · refined · macrostructure: Long Document (preserved) · theme: Plain · accent: cool-ink-blue ~2% */` | [`SKILL.md`](../../../skill/SKILL.md) Step 6 — stamp the output |
| **12.** Tinted the link underline to use `text-decoration-color` so the underline can fade on hover without animating layout | underline + colour change in 250 ms | static underline + colour-mix transition only | [`microinteractions.md`](../../../skill/references/microinteractions.md) — animate paint, not layout |
## Slop test
| Before | After |
| --- | --- |
| 12 / 38 ✓ (failed gates 1, 4 (no token system), 9 (acceptable), 11, 16, 24, 26, 28, 29, 36) | 38 / 38 ✓ |
## Durable artifact
The `output.html` is what the user keeps. The DOM is unchanged so any JS / framework wrapper around the page still works. Total LOC delta: input had 95 lines, output has 138 lines (mostly tokens at `:root`).
## What `refine` *did not* do
- Did not add a hero
- Did not add a CTA
- Did not change the section order
- Did not insert any `<svg>`, illustration, or marketing copy
- Did not change the brand voice
- Did not remove the colour-coded list bullets (the user clearly wants them; the change is to use proper OKLCH tokens, not abandon the pattern)
That's the discipline. `refine` is a polish-in-place verb. Use `redesign` when the structure itself needs work — see [`../redesign/`](../redesign/) for that case.
-193
View File
@@ -1,193 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Codeline — changelog</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;500&family=Geist:wght@400;500;600&display=swap" />
<style>
/* Hallmark · refined · macrostructure: Long Document (preserved) · theme: Plain · accent: cool-ink-blue ~2% */
:root {
--color-paper: oklch(98% 0.005 240);
--color-paper-2: oklch(95% 0.006 240);
--color-rule: oklch(88% 0.008 240);
--color-ink: oklch(22% 0.012 245);
--color-ink-soft: oklch(48% 0.014 245);
--color-ink-muted: oklch(68% 0.014 245);
--color-accent: oklch(36% 0.16 248);
--color-added: oklch(50% 0.16 145);
--color-fixed: oklch(58% 0.13 70);
--color-removed: oklch(54% 0.18 28);
--font-display: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--font-body: "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--font-mono: "Geist Mono", "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;
--space-2xs: 0.5rem;
--space-xs: 0.75rem;
--space-sm: 1rem;
--space-md: 1.5rem;
--space-lg: 2rem;
--space-xl: 3rem;
--space-2xl: 4.5rem;
--ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
--dur-micro: 120ms;
--dur-short: 200ms;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { overflow-x: clip; }
body {
font-family: var(--font-body);
background: var(--color-paper);
color: var(--color-ink);
line-height: 1.55;
max-width: 45rem;
margin: var(--space-2xl) auto;
padding: 0 var(--space-md);
-webkit-font-smoothing: antialiased;
}
h1 {
font-family: var(--font-display);
font-size: 2.25rem;
font-weight: 600;
letter-spacing: -0.02em;
line-height: 1.1;
margin-block-end: var(--space-2xs);
}
h1 .dot { color: var(--color-accent); }
.lede {
color: var(--color-ink-soft);
margin-block-end: var(--space-2xl);
font-size: 1rem;
max-width: 56ch;
}
.release {
margin-block-end: var(--space-2xl);
padding-block-end: var(--space-lg);
border-block-end: 1px solid var(--color-rule);
}
.release:last-of-type { border-block-end: none; }
.release h2 {
font-family: var(--font-display);
font-size: 1.375rem;
font-weight: 600;
letter-spacing: -0.01em;
margin-block-end: 2px;
}
.release time {
color: var(--color-ink-muted);
font-family: var(--font-mono);
font-size: 0.75rem;
letter-spacing: 0.04em;
display: block;
margin-block-end: var(--space-md);
font-variant-numeric: tabular-nums;
}
.release ul { list-style: none; padding: 0; margin: 0; }
.release li {
padding: var(--space-2xs) 0 var(--space-2xs) 1.25rem;
position: relative;
color: var(--color-ink);
font-size: 0.9375rem;
}
.release li::before {
content: "—";
position: absolute;
inset-inline-start: 0;
color: var(--color-ink-muted);
font-family: var(--font-mono);
}
.release li.added::before { color: var(--color-added); content: "+"; }
.release li.fixed::before { color: var(--color-fixed); content: "·"; }
.release li.removed::before { color: var(--color-removed); content: ""; }
.release li code {
font-family: var(--font-mono);
background: var(--color-paper-2);
padding: 1px 5px;
border-radius: 2px;
font-size: 0.8125rem;
color: var(--color-ink);
}
.release a {
color: var(--color-accent);
text-decoration: underline;
text-decoration-color: oklch(88% 0.012 240);
text-underline-offset: 3px;
transition: text-decoration-color var(--dur-micro) var(--ease-out);
}
.release a:hover { text-decoration-color: var(--color-accent); }
.release a:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 3px;
border-radius: 2px;
}
footer {
margin-block-start: var(--space-2xl);
padding-block-start: var(--space-md);
border-block-start: 1px solid var(--color-rule);
color: var(--color-ink-muted);
font-family: var(--font-mono);
font-size: 0.75rem;
letter-spacing: 0.04em;
}
@media (prefers-reduced-motion: reduce) {
.release a { transition: none; }
}
</style>
</head>
<body>
<h1>Codeline<span class="dot">·</span> changelog</h1>
<p class="lede">A small dev-tools shop. Releases ship Tuesday mornings, twice a month.</p>
<article class="release">
<h2>v0.7.4</h2>
<time>2026-04-29</time>
<ul>
<li class="added">Add <code>--watch</code> flag to <code>codeline build</code> for incremental rebuilds (closes <a href="#">#412</a>).</li>
<li class="added">Add JSON output mode to <code>codeline lint</code>; pipe-friendly for CI.</li>
<li class="fixed">Fix <code>tsconfig.json</code> resolution failing when <code>extends</code> points outside the workspace root.</li>
<li class="fixed">Fix incorrect exit code (<code>0</code> instead of <code>1</code>) when <code>codeline lint</code> finds errors but no warnings.</li>
<li class="removed">Deprecate <code>--legacy-loader</code>; will be removed in v0.9.</li>
</ul>
</article>
<article class="release">
<h2>v0.7.3</h2>
<time>2026-04-15</time>
<ul>
<li class="fixed">Fix race condition in the file-watcher when many small files change simultaneously (e.g., during a <code>git checkout</code>).</li>
<li class="fixed">Fix the loader silently swallowing parse errors in <code>postcss</code> plugin chains.</li>
<li class="added">Add <code>codeline doctor</code> — a diagnostic command that checks node version, lockfile parity, and config validity.</li>
</ul>
</article>
<article class="release">
<h2>v0.7.2</h2>
<time>2026-04-01</time>
<ul>
<li class="added">Initial public release. Read the <a href="#">getting-started guide</a>.</li>
</ul>
</article>
<footer>
Codeline · MIT licensed · maintained by two engineers in Lisbon.
</footer>
</body>
</html>
@@ -1,132 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Tide — letters from the studio</title>
<style>
/* Hallmark · genre: editorial · macrostructure: Letter
* theme: Salon · accent: warm cinnamon · paper: warm cream
* nav: N6 Newspaper masthead · footer: Ft6 Letter close
* polish: HP4 Decorative-numeral (issue 04 · means "fourth letter")
* slop: pass (5155) · contrast: pass (4650)
* brief: Tide — indie podcast for solo hosts. Warm, editorial.
*/
@font-face { font-family: "FraunSerif"; src: local("Fraunces"), local("Source Serif 4"); font-display: swap; }
:root {
--color-paper: oklch(96% 0.020 70);
--color-paper-2: oklch(94% 0.022 65);
--color-ink: oklch(20% 0.030 50);
--color-ink-2: oklch(40% 0.030 55);
--color-rule: oklch(78% 0.020 60);
--color-accent: oklch(48% 0.140 40);
--color-accent-ink: oklch(98% 0.005 70);
--color-focus: oklch(58% 0.140 40);
--font-display: "FraunSerif", "Fraunces", "Source Serif 4", Georgia, serif;
--font-body: "FraunSerif", "Fraunces", "Source Serif 4", Georgia, serif;
--space-2xs: 0.5rem; --space-xs: 0.75rem; --space-sm: 1rem;
--space-md: 1.5rem; --space-lg: 2rem; --space-xl: 3rem;
--space-2xl: 4.5rem; --space-3xl: 7rem;
--text-xs: 0.75rem; --text-sm: 0.875rem; --text-md: 1.125rem;
--text-lg: 1.375rem; --text-xl: 1.75rem; --text-2xl: 2.25rem;
--rule-hair: 0.5px;
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
--dur-short: 220ms;
--page-gutter: clamp(1rem, 4vw, 2.5rem);
}
* { box-sizing: border-box; }
html, body { margin: 0; }
body { font-family: var(--font-body); background: var(--color-paper); color: var(--color-ink); font-size: var(--text-md); line-height: 1.55; font-feature-settings: "onum"; }
a { color: inherit; }
/* N6 Newspaper masthead */
.nav-mast { display: grid; gap: var(--space-2xs); padding: var(--space-md) var(--page-gutter) 0; text-align: center; }
.mast-line { font-variant: small-caps; letter-spacing: 0.12em; font-size: var(--text-xs); color: var(--color-ink-2); margin: 0; }
.mast-name { font-family: var(--font-display); font-size: clamp(2.5rem, 6vw, 4rem); letter-spacing: -0.01em; line-height: 0.95; font-style: italic; font-weight: 600; margin: 0; }
.mast-nav ul { display: inline-flex; gap: var(--space-md); list-style: none; padding: 0; margin: var(--space-2xs) 0 0; flex-wrap: wrap; justify-content: center; }
.mast-nav a { text-decoration: none; font-size: var(--text-sm); }
.mast-nav a:hover { color: var(--color-accent); }
.mast-rule { border: 0; border-top: var(--rule-hair) solid var(--color-rule); border-bottom: var(--rule-hair) solid var(--color-rule); height: 4px; margin: var(--space-sm) 0 0; }
/* Hero — Letter macro + HP4 decorative numeral */
.hero { position: relative; padding: var(--space-2xl) var(--page-gutter) var(--space-3xl); max-width: 56ch; margin: 0 auto; overflow: hidden; }
.hero__eyebrow { font-variant: small-caps; letter-spacing: 0.12em; font-size: var(--text-xs); color: var(--color-ink-2); margin: 0 0 var(--space-md); }
.hero__salutation { font-style: italic; font-size: var(--text-lg); margin: 0 0 var(--space-md); }
.hero__body { font-size: var(--text-md); line-height: 1.7; margin: 0 0 var(--space-md); }
.hero__body em { color: var(--color-accent); font-style: italic; }
.hero__sign { font-style: italic; }
.hero__sign strong { font-style: normal; font-weight: 600; }
.hero__num { position: absolute; right: -0.05em; bottom: -0.18em; font-family: var(--font-display); font-style: italic; font-weight: 600; font-size: clamp(8rem, 22vw, 16rem); line-height: 1; color: color-mix(in oklch, var(--color-ink) 7%, transparent); pointer-events: none; user-select: none; }
/* Section — single block of prose to show editorial rhythm */
.note { padding: var(--space-2xl) var(--page-gutter); max-width: 56ch; margin: 0 auto; border-top: var(--rule-hair) solid var(--color-rule); }
.note__head { font-family: var(--font-display); font-style: italic; font-size: var(--text-xl); margin: 0 0 var(--space-sm); }
.note p { margin: 0 0 var(--space-sm); }
/* Ft6 Letter close */
.foot-letter { padding: var(--space-2xl) var(--page-gutter); max-width: 56ch; margin: 0 auto; border-top: var(--rule-hair) solid var(--color-rule); }
.foot-letter__close { font-family: var(--font-display); font-style: italic; font-size: var(--text-lg); line-height: 1.4; margin: 0 0 var(--space-md); }
.foot-letter__sign { font-style: normal; font-weight: 600; }
.foot-letter__ps { font-size: var(--text-sm); color: var(--color-ink-2); margin: 0; }
.foot-letter__ps a { color: var(--color-accent); }
a:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
@media (max-width: 60rem) {
.mast-name { font-size: clamp(2rem, 9vw, 3rem); }
.hero__num { font-size: clamp(5rem, 26vw, 9rem); }
}
@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }
</style>
</head>
<body>
<header class="nav-mast">
<p class="mast-line">No 04 · Spring 2026 · Tide</p>
<h1 class="mast-name">Tide</h1>
<nav class="mast-nav" aria-label="Primary">
<ul>
<li><a href="#listen">Listen</a></li>
<li><a href="#letters">Letters</a></li>
<li><a href="#about">About</a></li>
<li><a href="#subscribe">Subscribe</a></li>
</ul>
</nav>
<hr class="mast-rule" aria-hidden="true">
</header>
<main>
<header class="hero">
<p class="hero__eyebrow">A letter to the listeners · Sunday, 12 April</p>
<p class="hero__salutation">Dear Tide listener,</p>
<p class="hero__body">This is the fourth season. We've kept the format short on purpose — one host, one guest, one idea, forty minutes. <em>The shape works.</em> We'd rather record fewer episodes that earn their place than fill a feed.</p>
<p class="hero__body">If you've subscribed since season three, this letter is yours too. The archive is open, the whole forty-three episodes, no paywall, no ads inside the audio.</p>
<p class="hero__sign">Yours,<br><strong>— Tide</strong></p>
<span class="hero__num" aria-hidden="true">04</span>
</header>
<article class="note" id="letters">
<h2 class="note__head">What's in this season.</h2>
<p>Six conversations with people who've quietly shaped how we work — a typeface designer, a brewer, a high-school English teacher, a back-office accountant who runs a 15-person bakery, and two more we're still recording. Each episode opens with a letter — five paragraphs, written by hand the morning before — and closes with one question we'd like the next listener to bring to dinner.</p>
<p>We publish on the second Sunday of every month. The next letter is due on the eleventh.</p>
</article>
</main>
<footer class="foot-letter">
<p class="foot-letter__close">Yours,<br><span class="foot-letter__sign">— Tide</span></p>
<p class="foot-letter__ps">P.S. — letters back welcome at <a href="mailto:hello@tide.fm">hello@tide.fm</a>. We read them on Sunday mornings.</p>
</footer>
</body>
</html>
@@ -1,133 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>streampipe — open-source stream processing for the terminal</title>
<style>
/* Hallmark · genre: modern-minimal/terminal · macrostructure: Workbench
* theme: Terminal · accent: phosphor green · paper: near-black
* nav: N8 Terminal command (cursor INSIDE the prompt — purposeful)
* footer: Ft4 Dense colophon
* polish: none (terminal genre disallows decorative graphics — see structure.md)
* slop: pass (5155) · contrast: pass (4650)
* brief: streampipe — open-source CLI for stream processing. Terse, dev-tool.
*/
@font-face { font-family: "Mono"; src: local("JetBrains Mono"), local("Commit Mono"), local("ui-monospace"); font-display: swap; }
:root {
--color-paper: oklch(13% 0.020 145);
--color-paper-2: oklch(17% 0.020 145);
--color-ink: oklch(94% 0.010 140);
--color-ink-2: oklch(70% 0.010 140);
--color-rule: oklch(28% 0.020 140);
--color-accent: oklch(78% 0.190 138);
--color-accent-ink: oklch(13% 0.020 145);
--color-focus: oklch(78% 0.190 138);
--font-body: "Mono", ui-monospace, "JetBrains Mono", "SF Mono", Menlo, monospace;
--font-display: var(--font-body);
--space-2xs: 0.5rem; --space-xs: 0.75rem; --space-sm: 1rem;
--space-md: 1.5rem; --space-lg: 2rem; --space-xl: 3rem;
--space-2xl: 4.5rem;
--text-xs: 0.75rem; --text-sm: 0.875rem; --text-md: 1rem;
--text-lg: 1.25rem; --text-xl: 1.625rem; --text-2xl: 2.25rem;
--rule-hair: 1px;
--page-gutter: clamp(1rem, 4vw, 2.5rem);
}
* { box-sizing: border-box; }
html, body { margin: 0; }
body { font-family: var(--font-body); background: var(--color-paper); color: var(--color-ink); font-size: var(--text-md); line-height: 1.6; }
a { color: var(--color-accent); text-underline-offset: 2px; }
a:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
/* N8 Terminal command nav — cursor lives INSIDE the prompt line */
.nav-term { padding: var(--space-sm) var(--page-gutter); border-bottom: var(--rule-hair) solid var(--color-rule); background: var(--color-paper); }
.nav-term__line { font-family: var(--font-body); font-size: var(--text-sm); margin: 0; white-space: pre-wrap; }
.nav-term__line .prompt { color: var(--color-accent); padding-right: 0.4ch; }
.nav-term__line a { color: var(--color-ink); text-decoration: underline; text-underline-offset: 3px; }
.caret { display: inline-block; width: 1ch; color: var(--color-accent); animation: blink 1.05s steps(2) infinite; }
@keyframes blink { 50% { opacity: 0; } }
/* Hero — Workbench: title + lede + install command (the install code is the only place the cursor reappears) */
.hero { padding: var(--space-xl) var(--page-gutter) var(--space-2xl); max-width: 90ch; margin: 0 auto; }
.hero__eyebrow { color: var(--color-accent); font-size: var(--text-sm); margin: 0 0 var(--space-md); }
.hero__display { font-size: clamp(1.75rem, 4vw, 2.5rem); line-height: 1.1; letter-spacing: -0.01em; margin: 0 0 var(--space-md); font-weight: 600; }
.hero__lede { color: var(--color-ink-2); max-width: 60ch; margin: 0 0 var(--space-lg); }
.hero__cta-label { font-size: var(--text-xs); color: var(--color-ink-2); text-transform: uppercase; letter-spacing: 0.08em; margin: 0 0 var(--space-2xs); }
.code { background: var(--color-paper-2); border: var(--rule-hair) solid var(--color-rule); padding: var(--space-sm) var(--space-md); border-radius: 0; display: inline-flex; align-items: center; gap: 0.6ch; font-size: var(--text-sm); margin: 0; }
.code .prompt { color: var(--color-accent); }
.code .caret { vertical-align: baseline; }
/* Section — Workbench: function reference table */
.section { padding: var(--space-2xl) var(--page-gutter); max-width: 90ch; margin: 0 auto; border-top: var(--rule-hair) solid var(--color-rule); }
.section__num { color: var(--color-accent); font-size: var(--text-sm); margin: 0 0 var(--space-2xs); }
.section__head { font-size: var(--text-lg); font-weight: 600; margin: 0 0 var(--space-md); }
.spec { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }
.spec th, .spec td { padding: var(--space-2xs) var(--space-sm); border-bottom: var(--rule-hair) solid var(--color-rule); text-align: left; vertical-align: top; }
.spec th { color: var(--color-ink-2); font-weight: 400; font-size: var(--text-xs); text-transform: uppercase; letter-spacing: 0.06em; }
.spec td.k { color: var(--color-accent); }
/* Ft4 Dense colophon */
.foot-dense { padding: var(--space-xl) var(--page-gutter); border-top: var(--rule-hair) solid var(--color-rule); font-size: var(--text-xs); color: var(--color-ink-2); }
.foot-dense p { margin: 0; max-width: 90ch; line-height: 1.6; }
.foot-dense a { color: var(--color-accent); }
@media (prefers-reduced-motion: reduce) { .caret { animation: none; opacity: 1; } * { animation: none !important; transition: none !important; } }
</style>
</head>
<body>
<header class="nav-term">
<pre class="nav-term__line"><span class="prompt">&gt;</span> streampipe <a href="#install">--install</a> <a href="#docs">--docs</a> <a href="#examples">--examples</a> <a href="https://github.com/streampipe/streampipe">--source</a><span class="caret" aria-hidden="true"></span></pre>
</header>
<main>
<section class="hero">
<p class="hero__eyebrow">v2.4.0 · MIT · 12k stars</p>
<h1 class="hero__display">Stream processing for the terminal.</h1>
<p class="hero__lede">streampipe is a single-binary tool that runs SQL over Kafka, Redis Streams, NATS, and stdin. No daemon, no broker, no JVM. Composes with your existing UNIX pipeline.</p>
<p class="hero__cta-label">Install</p>
<pre class="code"><span class="prompt">$</span><span>brew install streampipe</span><span class="caret" aria-hidden="true"></span></pre>
</section>
<section class="section" id="examples">
<p class="section__num">01 · Examples</p>
<h2 class="section__head">Three lines that read a Kafka topic and emit JSON.</h2>
<pre class="code" style="display:block; padding: var(--space-md); white-space: pre;"><span class="prompt">$</span> streampipe \
--source kafka://localhost:9092/orders \
--sql "select customer, sum(total) as gross from orders group by customer" \
--sink stdout:json</pre>
</section>
<section class="section" id="docs">
<p class="section__num">02 · Sources, sinks, formats</p>
<h2 class="section__head">Reference</h2>
<table class="spec">
<thead>
<tr><th style="width:18ch">Flag</th><th>Description</th><th style="width:24ch">Example</th></tr>
</thead>
<tbody>
<tr><td class="k">--source</td><td>Stream input. Accepts kafka://, redis://, nats://, file://, stdin.</td><td>kafka://broker:9092/topic</td></tr>
<tr><td class="k">--sink</td><td>Stream output. Accepts the same protocols + stdout.</td><td>stdout:json</td></tr>
<tr><td class="k">--sql</td><td>SQL transformation. Standard select/where/group-by/join.</td><td>"select * where amount &gt; 100"</td></tr>
<tr><td class="k">--watermark</td><td>Event-time watermark. Default: 30s.</td><td>--watermark 60s</td></tr>
<tr><td class="k">--checkpoint</td><td>Path for state. Required for stateful queries.</td><td>./state.db</td></tr>
</tbody>
</table>
</section>
</main>
<footer class="foot-dense">
<p>streampipe v2.4.0 · MIT licensed · built in Go · 12k stars on GitHub · zero runtime dependencies · macOS · Linux · Windows · single static binary · 8 MB · &lt;a href="https://github.com/streampipe/streampipe"&gt;source&lt;/a&gt; · &lt;a href="/docs"&gt;docs&lt;/a&gt; · &lt;a href="/changelog"&gt;changelog&lt;/a&gt; · maintained by 4 people · 137 contributors · last release 2026-04-12 · sha 3a94f · powered by goroutines and a shoestring budget</p>
</footer>
</body>
</html>
@@ -1,161 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Nimbus — atmospheric trading desk</title>
<style>
/* Hallmark · genre: atmospheric · macrostructure: Stat-Led
* theme: Bloom · accent: amber · paper: dark-warm
* nav: N5 Floating pill (blur backdrop sells the atmosphere)
* footer: Ft5 Statement (closes with a sentence, not a sitemap)
* polish: HP3 Cursor-spotlight (scoped to hero only, reduced-motion fallback)
* slop: pass (5155) · contrast: pass (4650)
* brief: Nimbus — crypto exchange dashboard. Atmospheric, stat-led.
*/
@font-face { font-family: "Geist"; src: local("Geist"), local("Inter"); font-display: swap; }
:root {
--color-paper: oklch(11% 0.022 35);
--color-paper-2: oklch(15% 0.022 35);
--color-paper-3: oklch(19% 0.022 35);
--color-ink: oklch(96% 0.010 60);
--color-ink-2: oklch(72% 0.012 50);
--color-rule: oklch(28% 0.020 35);
--color-accent: oklch(72% 0.190 45);
--color-accent-ink: oklch(11% 0.022 35);
--color-focus: oklch(78% 0.190 45);
--font-display: "Geist", "Inter", system-ui, sans-serif;
--font-body: "Geist", "Inter", system-ui, sans-serif;
--space-2xs: 0.5rem; --space-xs: 0.75rem; --space-sm: 1rem;
--space-md: 1.5rem; --space-lg: 2rem; --space-xl: 3rem;
--space-2xl: 4.5rem; --space-3xl: 7rem;
--text-xs: 0.75rem; --text-sm: 0.875rem; --text-md: 1rem;
--text-lg: 1.375rem; --text-xl: 1.75rem; --text-2xl: 2.25rem;
--rule-hair: 1px;
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
--dur-short: 220ms;
--page-gutter: clamp(1rem, 4vw, 2.5rem);
}
* { box-sizing: border-box; }
html, body { margin: 0; }
body { font-family: var(--font-body); background: var(--color-paper); color: var(--color-ink); font-size: var(--text-md); line-height: 1.55; font-feature-settings: "tnum"; }
a { color: inherit; text-underline-offset: 2px; }
a:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
/* N5 Floating pill */
.nav-pill { position: fixed; inset: var(--space-md) auto auto 50%; transform: translateX(-50%); display: inline-flex; align-items: center; gap: var(--space-md); padding: 0.5rem 0.875rem; background: color-mix(in oklch, var(--color-paper) 65%, transparent); backdrop-filter: blur(14px) saturate(140%); -webkit-backdrop-filter: blur(14px) saturate(140%); border: var(--rule-hair) solid var(--color-rule); border-radius: 999px; box-shadow: 0 8px 24px -12px oklch(0% 0 0 / 0.40); z-index: 20; }
.nav-pill .wordmark { font-weight: 700; font-size: var(--text-sm); letter-spacing: -0.01em; text-decoration: none; }
.nav-pill ul { display: inline-flex; gap: var(--space-md); list-style: none; padding: 0; margin: 0; }
.nav-pill a { font-size: var(--text-sm); color: var(--color-ink-2); text-decoration: none; }
.nav-pill a:hover { color: var(--color-ink); }
.cta-fill { padding: 0.4rem 0.875rem; border-radius: 999px; background: var(--color-accent); color: var(--color-accent-ink); font-size: var(--text-sm); font-weight: 600; text-decoration: none; }
/* HP3 Cursor-spotlight scoped to hero */
.hero { position: relative; isolation: isolate; padding: var(--space-2xl) var(--page-gutter) var(--space-3xl); overflow: hidden; min-height: clamp(60vh, 75dvh, 88dvh); display: grid; align-content: center; }
.hero__spotlight { position: absolute; inset: 0; z-index: -1; background: radial-gradient(560px circle at var(--mx, 50%) var(--my, 35%), color-mix(in oklch, var(--color-accent) 28%, transparent), transparent 65%); transition: background 200ms var(--ease-out); pointer-events: none; }
.hero__copy { max-width: 64ch; }
.hero__eyebrow { color: var(--color-accent); font-size: var(--text-sm); letter-spacing: 0.04em; margin: 0 0 var(--space-md); }
.hero__stat { font-family: var(--font-display); font-size: clamp(4rem, 12vw, 9rem); line-height: 0.95; letter-spacing: -0.04em; font-weight: 700; font-feature-settings: "tnum"; margin: 0 0 var(--space-sm); }
.hero__stat .unit { font-size: 0.45em; color: var(--color-ink-2); margin-left: 0.2em; }
.hero__qualifier { font-size: var(--text-lg); max-width: 48ch; margin: 0 0 var(--space-md); }
.hero__lede { color: var(--color-ink-2); max-width: 56ch; margin: 0 0 var(--space-lg); }
.hero__link { color: var(--color-accent); text-decoration: underline; }
/* Stats row */
.stats { padding: var(--space-2xl) var(--page-gutter); display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-2xl); border-top: var(--rule-hair) solid var(--color-rule); max-width: 90rem; margin: 0 auto; }
.stat__num { font-size: clamp(2rem, 4vw, 3rem); letter-spacing: -0.02em; font-weight: 700; margin: 0; }
.stat__label { color: var(--color-ink-2); font-size: var(--text-sm); margin: 0; }
/* Ft5 Statement footer */
.foot-stmt { padding: var(--space-3xl) var(--page-gutter) var(--space-xl); display: grid; gap: var(--space-lg); max-width: 90rem; margin: 0 auto; }
.foot-stmt__line { font-family: var(--font-display); font-size: clamp(2rem, 5vw, 3.5rem); line-height: 1.0; letter-spacing: -0.02em; max-width: 26ch; margin: 0; font-weight: 600; }
.foot-stmt__meta { display: flex; justify-content: space-between; align-items: baseline; padding-block-start: var(--space-md); border-top: var(--rule-hair) solid var(--color-rule); color: var(--color-ink-2); font-size: var(--text-sm); }
.foot-stmt__meta .wordmark { color: var(--color-ink); font-weight: 700; }
@media (max-width: 60rem) {
.nav-pill ul { display: none; }
.stats { grid-template-columns: 1fr 1fr; gap: var(--space-md); }
.hero { padding-top: var(--space-2xl); }
}
@media (max-width: 40rem) {
.stats { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
.hero__spotlight { transition: none; --mx: 50%; --my: 35%; }
* { animation: none !important; transition: none !important; }
}
</style>
</head>
<body>
<nav class="nav-pill" aria-label="Primary">
<a class="wordmark" href="#top">Nimbus</a>
<ul>
<li><a href="#markets">Markets</a></li>
<li><a href="#api">API</a></li>
<li><a href="#fees">Fees</a></li>
</ul>
<a class="cta-fill" href="#trade">Trade →</a>
</nav>
<main>
<header class="hero" id="top">
<div class="hero__spotlight" aria-hidden="true"></div>
<div class="hero__copy">
<p class="hero__eyebrow">Spot · Margin · Perpetuals · Active in 84 markets</p>
<span class="hero__stat">$2.4<span class="unit">B</span></span>
<p class="hero__qualifier">notional volume cleared in the last 24 hours, with a single API and one balance.</p>
<p class="hero__lede">Nimbus is a non-custodial trading desk for the calm trader. Open positions in spot, margin, and perpetuals from one account. Trades clear in 380 ms p99. We don't run leveraged retail ads.</p>
<a class="hero__link" href="#markets">See live markets →</a>
</div>
</header>
<section class="stats" id="markets">
<div>
<p class="stat__num">380<span style="color:var(--color-ink-2)">ms</span></p>
<p class="stat__label">order-to-fill, 99th percentile, last 7 days</p>
</div>
<div>
<p class="stat__num">84</p>
<p class="stat__label">spot + perpetual markets, audited monthly</p>
</div>
<div>
<p class="stat__num">0.02<span style="color:var(--color-ink-2)">%</span></p>
<p class="stat__label">maker fee on the deepest tier, paid by inventory</p>
</div>
</section>
</main>
<footer class="foot-stmt">
<p class="foot-stmt__line">Calm markets. Honest fees. No retail ads.</p>
<div class="foot-stmt__meta">
<span class="wordmark">Nimbus</span>
<span>© 2026 · Audited by ChainSec · Reg-D</span>
</div>
</footer>
<script>
// HP3 Cursor-spotlight — scope to hero only, never page-wide
(function () {
var hero = document.querySelector('.hero');
if (!hero) return;
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
hero.addEventListener('pointermove', function (e) {
var r = hero.getBoundingClientRect();
hero.style.setProperty('--mx', (e.clientX - r.left) + 'px');
hero.style.setProperty('--my', (e.clientY - r.top) + 'px');
});
})();
</script>
</body>
</html>
@@ -1,143 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Marlow & Co. — wedding photography</title>
<style>
/* Hallmark · genre: editorial · macrostructure: Photographic
* theme: Atelier · accent: warm cinnamon · paper: warm-cream
* nav: N9 Edge-aligned minimal (the absence is the design)
* footer: Ft7 Newsletter-first (the studio publishes a quarterly letter — the page mentioned it above the fold)
* polish: HP1 Vertical-rail (studio name + plate numerals run vertically)
* slop: pass (5155) · contrast: pass (4650)
* brief: Marlow & Co. — wedding photographer portfolio. Salon, restrained.
*/
@font-face { font-family: "FraunSerif"; src: local("Fraunces"), local("Source Serif 4"); font-display: swap; }
:root {
--color-paper: oklch(95% 0.012 70);
--color-paper-2: oklch(92% 0.014 65);
--color-ink: oklch(22% 0.020 50);
--color-ink-2: oklch(45% 0.020 55);
--color-rule: oklch(78% 0.014 60);
--color-accent: oklch(48% 0.130 50);
--color-accent-ink: oklch(98% 0.005 70);
--color-focus: oklch(58% 0.130 50);
--font-display: "FraunSerif", "Fraunces", "Source Serif 4", Georgia, serif;
--font-body: "FraunSerif", "Fraunces", "Source Serif 4", Georgia, serif;
--space-2xs: 0.5rem; --space-xs: 0.75rem; --space-sm: 1rem;
--space-md: 1.5rem; --space-lg: 2rem; --space-xl: 3rem;
--space-2xl: 4.5rem; --space-3xl: 7rem;
--text-xs: 0.75rem; --text-sm: 0.875rem; --text-md: 1.125rem;
--text-lg: 1.375rem; --text-xl: 1.75rem; --text-2xl: 2.25rem;
--rule-hair: 0.5px;
--page-gutter: clamp(1rem, 4vw, 2.5rem);
}
* { box-sizing: border-box; }
html, body { margin: 0; }
body { font-family: var(--font-body); background: var(--color-paper); color: var(--color-ink); font-size: var(--text-md); line-height: 1.55; font-feature-settings: "onum"; }
a { color: inherit; text-underline-offset: 3px; }
a:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
/* N9 Edge-aligned minimal nav */
.nav-edge { display: flex; justify-content: space-between; align-items: center; padding: var(--space-md) var(--page-gutter); }
.nav-edge .wordmark { font-family: var(--font-display); font-style: italic; font-size: var(--text-md); letter-spacing: -0.005em; text-decoration: none; }
.cta-outline { padding: 0.45rem 0.95rem; border: 1px solid var(--color-ink); color: var(--color-ink); text-decoration: none; font-size: var(--text-sm); border-radius: 999px; min-height: 44px; display: inline-flex; align-items: center; }
/* HP1 Vertical-rail title — the studio name runs vertically alongside the photo */
.hero { display: grid; grid-template-columns: auto 1fr; gap: var(--space-2xl); padding: var(--space-2xl) var(--page-gutter) var(--space-3xl); align-items: end; }
.hero__rail { writing-mode: vertical-rl; text-orientation: mixed; font-family: var(--font-display); font-size: var(--text-sm); letter-spacing: 0.18em; color: var(--color-ink-2); margin: 0; padding-block-end: var(--space-md); }
.hero__body { display: grid; gap: var(--space-md); max-width: 60ch; }
.hero__eyebrow { font-variant: small-caps; letter-spacing: 0.12em; font-size: var(--text-xs); color: var(--color-ink-2); margin: 0; }
.hero__display { font-family: var(--font-display); font-style: italic; font-weight: 500; font-size: clamp(2rem, 5vw, 3.25rem); line-height: 1.05; letter-spacing: -0.01em; margin: 0; }
.hero__lede { color: var(--color-ink); font-size: var(--text-md); margin: 0; max-width: 50ch; }
/* Photographic centerpiece — H6 archetype */
.frame { padding: 0 var(--page-gutter) var(--space-2xl); }
.frame__photo { width: 100%; aspect-ratio: 16/9; background: linear-gradient(135deg, color-mix(in oklch, var(--color-paper-2) 100%, var(--color-accent) 6%), color-mix(in oklch, var(--color-paper) 100%, var(--color-ink) 8%)); border: var(--rule-hair) solid var(--color-rule); position: relative; overflow: hidden; }
.frame__photo::after { content: ""; position: absolute; inset: 0; background: radial-gradient(60% 40% at 30% 60%, oklch(40% 0.03 50 / 0.25), transparent 70%), radial-gradient(40% 30% at 75% 35%, oklch(70% 0.05 60 / 0.30), transparent 70%); }
.frame__caption { display: flex; justify-content: space-between; padding-block-start: var(--space-2xs); font-size: var(--text-xs); font-variant: small-caps; letter-spacing: 0.10em; color: var(--color-ink-2); }
/* Body — selected work */
.work { padding: var(--space-2xl) var(--page-gutter); border-top: var(--rule-hair) solid var(--color-rule); max-width: 64rem; margin: 0 auto; }
.work__head { font-family: var(--font-display); font-style: italic; font-size: var(--text-xl); margin: 0 0 var(--space-lg); }
.work__list { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--space-md); }
.work__item { display: grid; grid-template-columns: 4ch 1fr auto; gap: var(--space-md); align-items: baseline; padding-block: var(--space-2xs); border-bottom: var(--rule-hair) solid var(--color-rule); }
.work__num { font-variant: small-caps; letter-spacing: 0.12em; font-size: var(--text-xs); color: var(--color-ink-2); }
.work__title { font-style: italic; }
.work__date { color: var(--color-ink-2); font-size: var(--text-sm); }
/* Ft7 Newsletter-first */
.foot-news { padding: var(--space-2xl) var(--page-gutter); display: grid; gap: var(--space-lg); max-width: 56ch; margin: 0 auto; border-top: var(--rule-hair) solid var(--color-rule); }
.foot-news__form label { display: block; font-family: var(--font-display); font-style: italic; font-size: var(--text-lg); margin-block-end: var(--space-sm); }
.foot-news__row { display: flex; gap: var(--space-2xs); }
.foot-news__row input { flex: 1; min-height: 44px; padding-inline: var(--space-sm); border: 1px solid var(--color-rule); background: var(--color-paper); font: inherit; color: inherit; border-radius: 0; }
.foot-news__row input:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 1px; border-color: transparent; }
.foot-news__row button { min-height: 44px; padding-inline: var(--space-md); border: 0; background: var(--color-ink); color: var(--color-paper); font: inherit; font-weight: 600; cursor: pointer; border-radius: 0; }
.foot-news__meta { font-size: var(--text-xs); color: var(--color-ink-2); margin: 0; }
@media (max-width: 60rem) {
.hero { grid-template-columns: 1fr; }
.hero__rail { writing-mode: horizontal-tb; font-size: var(--text-xs); padding-block-end: 0; }
}
@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }
</style>
</head>
<body>
<header class="nav-edge">
<a class="wordmark" href="#top">Marlow &amp; Co.</a>
<a class="cta-outline" href="#enquire">Enquire →</a>
</header>
<main>
<header class="hero" id="top">
<p class="hero__rail" aria-hidden="true">MARLOW · PLATE 04 · 2026</p>
<div class="hero__body">
<p class="hero__eyebrow">Selected work · 2026 · Plate 04</p>
<h1 class="hero__display">Photographs that look like the day actually was.</h1>
<p class="hero__lede">A small studio for weddings, family portraits, and the quiet days after. Twelve commissions a year, photographed on film, scanned, printed, archived. We're booked into autumn 2026; the spring 2027 list opens in October.</p>
</div>
</header>
<figure class="frame">
<div class="frame__photo" aria-label="A bride and groom in profile, late afternoon, garden ceremony"></div>
<figcaption class="frame__caption">
<span>Plate 04 · Anna &amp; Sam · 12 April 2026</span>
<span>Hasselblad 500CM · Portra 400 · Garden ceremony</span>
</figcaption>
</figure>
<section class="work">
<h2 class="work__head">A few of last year's commissions.</h2>
<ol class="work__list">
<li class="work__item"><span class="work__num">01</span><span class="work__title">Anna &amp; Sam — garden ceremony, Tuscany</span><span class="work__date">April 2026</span></li>
<li class="work__item"><span class="work__num">02</span><span class="work__title">The Okonkwo family — winter portraits</span><span class="work__date">February 2026</span></li>
<li class="work__item"><span class="work__num">03</span><span class="work__title">Maya &amp; Jonas — small civil ceremony, Lisbon</span><span class="work__date">November 2025</span></li>
<li class="work__item"><span class="work__num">04</span><span class="work__title">Rivers — first-year archive</span><span class="work__date">Ongoing</span></li>
</ol>
</section>
</main>
<footer class="foot-news">
<form class="foot-news__form" action="/subscribe" method="post">
<label for="m-email">A quarterly letter from the studio. Plates, prints, and what we're learning.</label>
<div class="foot-news__row">
<input id="m-email" name="email" type="email" required placeholder="you@domain" autocomplete="email">
<button type="submit">Subscribe</button>
</div>
</form>
<p class="foot-news__meta">Marlow &amp; Co. · 12 Marlow Street · © 2026 · No tracking, no third-party email lists.</p>
</footer>
</body>
</html>
@@ -1,170 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Helix — feature flags for grown-up teams</title>
<style>
/* Hallmark · genre: modern-minimal · macrostructure: Bento Grid
* theme: Specimen → modern-minimal flavour · accent: indigo · paper: cool-near-white
* nav: N7 Brutal slab (because the brand voice is loud-and-confident — playful sub-cluster)
* ↑ chosen as a *deliberate* deviation from the modern-minimal default (N5)
* to demonstrate that variety wins over routing-defaults when the brief justifies it.
* Compare against the four other tests: each picks a different nav archetype.
* footer: Ft1 Mast-headed
* polish: HP2 Marquee-overflow (declarative title bleeds past the viewport edge)
* slop: pass (5155) · contrast: pass (4650)
* brief: Helix — B2B SaaS marketing landing. Trust SaaS but not generic.
*/
@font-face { font-family: "Geist"; src: local("Geist"), local("Inter"); font-display: swap; }
:root {
--color-paper: oklch(98% 0.005 250);
--color-paper-2: oklch(95% 0.008 250);
--color-paper-3: oklch(92% 0.010 250);
--color-ink: oklch(15% 0.012 270);
--color-ink-2: oklch(40% 0.012 270);
--color-rule: oklch(82% 0.010 260);
--color-accent: oklch(48% 0.180 270);
--color-accent-ink: oklch(98% 0.005 250);
--color-focus: oklch(58% 0.180 270);
--font-display: "Geist", "Inter", system-ui, sans-serif;
--font-body: "Geist", "Inter", system-ui, sans-serif;
--space-2xs: 0.5rem; --space-xs: 0.75rem; --space-sm: 1rem;
--space-md: 1.5rem; --space-lg: 2rem; --space-xl: 3rem;
--space-2xl: 4.5rem; --space-3xl: 7rem;
--text-xs: 0.75rem; --text-sm: 0.875rem; --text-md: 1rem;
--text-lg: 1.375rem; --text-xl: 1.75rem; --text-2xl: 2.25rem;
--rule-hair: 1px;
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
--dur-short: 220ms;
--page-gutter: clamp(1rem, 4vw, 2.5rem);
}
* { box-sizing: border-box; }
html, body { margin: 0; }
body { font-family: var(--font-body); background: var(--color-paper); color: var(--color-ink); font-size: var(--text-md); line-height: 1.55; }
a { color: inherit; text-underline-offset: 2px; }
a:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }
/* N7 Brutal slab nav */
.nav-slab { display: flex; align-items: center; gap: var(--space-md); padding: var(--space-sm) var(--page-gutter); border-bottom: 2px solid var(--color-ink); background: var(--color-paper); position: sticky; top: 0; z-index: 10; }
.slab-mark { font-family: var(--font-display); font-weight: 800; letter-spacing: 0.04em; text-decoration: none; }
.slab-nav { margin-left: auto; }
.slab-nav ul { display: flex; gap: var(--space-md); list-style: none; padding: 0; margin: 0; }
.slab-nav a { text-transform: uppercase; letter-spacing: 0.08em; font-size: var(--text-sm); font-weight: 600; text-decoration: none; }
.slab-nav a:hover { color: var(--color-accent); }
.cta-fill-slab { padding: 0.5rem 0.95rem; background: var(--color-ink); color: var(--color-paper); font-weight: 700; font-size: var(--text-sm); text-decoration: none; min-height: 44px; display: inline-flex; align-items: center; }
/* Hero — Bento Grid + HP2 Marquee-overflow */
.hero { overflow-x: clip; padding: var(--space-2xl) var(--page-gutter) var(--space-3xl); }
.hero__display { font-family: var(--font-display); font-weight: 800; font-size: clamp(3rem, 11vw, 9.5rem); line-height: 0.92; letter-spacing: -0.04em; margin: 0 0 var(--space-md); white-space: nowrap; }
.hero__lede { font-size: var(--text-lg); max-width: 64ch; margin: 0 0 var(--space-lg); color: var(--color-ink-2); }
.hero__cta-row { display: flex; gap: var(--space-sm); flex-wrap: wrap; }
.cta-fill { padding: 0.7rem 1.2rem; background: var(--color-ink); color: var(--color-paper); font-weight: 600; text-decoration: none; min-height: 44px; display: inline-flex; align-items: center; }
.cta-link { padding: 0.7rem 1.2rem; color: var(--color-ink); text-decoration: underline; min-height: 44px; display: inline-flex; align-items: center; font-weight: 500; }
/* Bento grid */
.bento { padding: var(--space-2xl) var(--page-gutter); display: grid; grid-template-columns: repeat(6, 1fr); grid-auto-rows: minmax(160px, auto); gap: var(--space-md); max-width: 90rem; margin: 0 auto; }
.tile { padding: var(--space-md); background: var(--color-paper-2); border: var(--rule-hair) solid var(--color-rule); display: grid; gap: var(--space-2xs); align-content: start; }
.tile h3 { font-size: var(--text-md); margin: 0; font-weight: 600; }
.tile p { font-size: var(--text-sm); color: var(--color-ink-2); margin: 0; }
.tile--stat { grid-column: span 2; grid-row: span 2; background: var(--color-ink); color: var(--color-paper); }
.tile--stat .num { font-size: clamp(3rem, 6vw, 5rem); line-height: 1; font-weight: 800; letter-spacing: -0.03em; font-feature-settings: "tnum"; }
.tile--stat .label { color: oklch(78% 0.010 250); font-size: var(--text-sm); }
.tile--code { grid-column: span 4; grid-row: span 2; background: var(--color-paper); }
.tile--code pre { background: var(--color-ink); color: var(--color-paper); padding: var(--space-md); border-radius: 0; font-size: var(--text-sm); overflow-x: auto; margin: var(--space-2xs) 0 0; font-family: ui-monospace, "JetBrains Mono", monospace; }
.tile--feature { grid-column: span 2; }
.tile--quote { grid-column: span 4; }
.tile--quote blockquote { font-family: var(--font-display); font-size: var(--text-lg); font-style: italic; margin: 0 0 var(--space-2xs); }
.tile--quote cite { font-size: var(--text-sm); color: var(--color-ink-2); font-style: normal; }
.accent { color: var(--color-accent); }
/* Ft1 Mast-headed footer */
.foot-mast { padding: var(--space-2xl) var(--page-gutter); border-top: 2px solid var(--color-ink); display: grid; gap: var(--space-sm); }
.foot-mast__wordmark { font-family: var(--font-display); font-weight: 800; font-size: var(--text-2xl); letter-spacing: -0.01em; margin: 0; }
.foot-mast__tag { font-style: italic; color: var(--color-ink-2); margin: 0; max-width: 50ch; }
.foot-mast__links { display: flex; gap: var(--space-md); flex-wrap: wrap; padding-block-start: var(--space-md); border-top: var(--rule-hair) solid var(--color-rule); font-size: var(--text-sm); color: var(--color-ink-2); }
@media (max-width: 60rem) {
.hero__display { white-space: normal; font-size: clamp(2.25rem, 9vw, 4.5rem); }
.bento { grid-template-columns: repeat(2, 1fr); }
.tile--stat, .tile--code, .tile--feature, .tile--quote { grid-column: span 2; grid-row: auto; }
.slab-nav { display: none; }
}
@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }
</style>
</head>
<body>
<header class="nav-slab">
<a class="slab-mark" href="#top">HELIX</a>
<nav class="slab-nav" aria-label="Primary">
<ul>
<li><a href="#features">FEATURES</a></li>
<li><a href="#pricing">PRICING</a></li>
<li><a href="#docs">DOCS</a></li>
</ul>
</nav>
<a class="cta-fill-slab" href="#start">START FREE</a>
</header>
<main id="top">
<section class="hero">
<h1 class="hero__display">FEATURE FLAGS, GROWN&nbsp;UP.</h1>
<p class="hero__lede">Helix is a feature-flag service for teams who want fewer surprises. Type-safe SDKs, audit trails, deterministic targeting. Used by 1,200 engineering teams to ship without holding their breath.</p>
<div class="hero__cta-row">
<a class="cta-fill" href="#start">Start free →</a>
<a class="cta-link" href="#book">Book a 20-min demo</a>
</div>
</section>
<section class="bento" id="features">
<article class="tile tile--stat">
<span class="num">99.97<span style="opacity:.6;font-size:.5em">%</span></span>
<span class="label">SDK uptime · last 90 days · measured externally</span>
</article>
<article class="tile tile--code">
<h3>Type-safe SDK · TypeScript, Go, Python, Ruby</h3>
<p>Every flag is a typed export. No magic strings.</p>
<pre>// flags.ts — generated from your dashboard
import { flags } from "@helix/sdk";
if (flags.checkout_redesign(user)) {
return &lt;CheckoutV2 /&gt;;
}</pre>
</article>
<article class="tile tile--feature">
<h3>Deterministic targeting</h3>
<p>Same input, same flag value — every time. Replayable, auditable, no drift.</p>
</article>
<article class="tile tile--feature">
<h3>Audit trail</h3>
<p>Every flag change writes a row. Who, what, when, why — and the diff.</p>
</article>
<article class="tile tile--quote">
<blockquote>"We replaced four years of <span class="accent">if-statements</span> with Helix in a week. The audit trail alone paid for it."</blockquote>
<cite>— Maya Okonkwo · Staff Engineer, Ridgeline</cite>
</article>
</section>
</main>
<footer class="foot-mast">
<p class="foot-mast__wordmark">Helix</p>
<p class="foot-mast__tag">Feature flags for teams that ship on Friday afternoons.</p>
<p class="foot-mast__links">
<a href="#docs">Docs</a> ·
<a href="#pricing">Pricing</a> ·
<a href="#imprint">Imprint</a> ·
<span>© 2026 · SOC 2 type II</span>
</p>
</footer>
</body>
</html>
-44
View File
@@ -1,44 +0,0 @@
# Test pass — nav · footer · hero polish variety
Five outputs, each generated by following the updated skill (`SKILL.md` flow + `component-cookbook.md` § Navigation / § Footers + `hero-enrichment.md` § Hero shape polish). Each tests a different combination of the new archetypes.
## Variety matrix
| # | Brief | Genre | Macrostructure | Nav | Footer | Hero polish | Theme |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 01 | Tide — indie podcast | editorial | Letter | **N6** Newspaper masthead | **Ft6** Letter close | **HP4** Decorative-numeral (issue 04) | Salon |
| 02 | streampipe — CLI | modern-minimal/terminal | Workbench | **N8** Terminal command | **Ft4** Dense colophon | none (terminal allows no decoration) | Terminal |
| 03 | Nimbus — atmospheric SaaS | atmospheric | Stat-Led | **N5** Floating pill | **Ft5** Statement | **HP3** Cursor-spotlight | Bloom |
| 04 | Marlow & Co. — photography | editorial | Photographic | **N9** Edge-aligned minimal | **Ft7** Newsletter-first | **HP1** Vertical-rail | Atelier |
| 05 | Helix — B2B SaaS | modern-minimal | Bento Grid | **N7** Brutal slab | **Ft1** Mast-headed | **HP2** Marquee-overflow | Specimen-leaning |
## Verification — does the skill produce variety?
- [x] **Five different nav archetypes.** N5, N6, N7, N8, N9 — one each. No repeats.
- [x] **Five different footer archetypes.** Ft1, Ft4, Ft5, Ft6, Ft7 — one each. No repeats.
- [x] **Four different hero polish patterns.** HP1, HP2, HP3, HP4 — one each on the polish-enabled outputs. The Terminal output (02) deliberately ships *no* polish pattern, per the genre's decoration constraint.
- [x] **Gate 51 (AI nav fingerprint)** — none of the five outputs use the wordmark-left + 5-link-row + button-right pattern. Pass.
- [x] **Gate 52 (AI footer fingerprint)** — none use the 4-column + social-row + tiny-copyright pattern. Pass.
- [x] **Gate 53 (centred-everything hero)** — none stack eyebrow + title + lede + CTA all centred. Pass.
- [x] **Gate 54 (hero padding asymmetry)** — every hero pads bottom > top. Pass.
- [x] **Gate 55 (decorative-without-purpose)** — every decorative element has a semantic anchor: 01's "04" names issue 4, 02's cursor sits inside a typed command, 03's spotlight responds to interaction, 04's vertical rail names the studio + plate + year, 05 has no decoration. Pass.
- [x] **Terminal cursor constraint** (02) — the blinking caret appears only inside `<pre>` install code blocks and the N8 nav prompt — never as standalone decoration. Pass.
## Routing reads correctly
Cross-checked each pick against the routing tables in `component-cookbook.md`:
- **01 (editorial · Salon):** N6 is the genre default; Ft6 is in the "also OK" set. ✓
- **02 (terminal · Terminal):** N8 is the genre default; Ft4 is the genre default. ✓
- **03 (atmospheric · Bloom):** N5 is the genre default; Ft5 is the genre default. ✓
- **04 (editorial · Atelier):** N9 is in the "also OK" set; Ft7 is in the "also OK" set. ✓ (Both are deliberate alternates rather than defaults — the brief leans atelier-quiet, which the routing allows.)
- **05 (modern-minimal · Specimen-leaning):** N7 is *not* the modern-minimal default — it's a deliberate cross-genre pick. The N7 voice fits Helix's "FEATURE FLAGS, GROWN UP." brand voice better than the default N5. The skill allows this when justified; the rationale is in the stamp comment.
The five outputs together demonstrate that:
- Each genre's default routing is honoured when the brief is genre-pure.
- Cross-genre picks are allowed when the brief justifies them, with the rationale stamped in the CSS comment.
- Diversification across consecutive runs is preserved — N# and Ft# never repeat.
## Stamps recorded
Every output's CSS opens with a `/* Hallmark · ... */` comment that names: genre, macrostructure, theme, nav, footer, polish (or "none"), slop-test pass, contrast pass, and a one-line brief summary. The stamps are how future Hallmark runs will read each project's history and rotate.