mirror of
https://github.com/Nutlope/hallmark.git
synced 2026-08-14 12:35:33 +02:00
Six generation tests + findings — exercise the skill, audit the output
Stress-test the skill across six contrasting briefs to see what comes out without hand-holding. Each test deliberately tests a different scenario along audience-context-given × theme-given × domain × intended enrichment level. None of the six pages share a macrostructure, hero archetype, or footer voice with another. 01 · Tide (indie podcast) — context skipped, no theme. Skill infers audio domain and picks Quote-Led + Atelier + no enrichment. Hand-built in pure typography. Pull-quote hero. 02 · Streampipe (open-source CLI) — full context, Terminal theme requested. Skill picks Workbench + Terminal + a hand-built CSS-art terminal mockup with blinking phosphor caret. No real terminal recording, no Lottie. 03 · Maple Street Bread (artisan bakery) — full context. Skill picks Long Document + Linen + a hand-built SVG loaf (Tier B). Letter-style body with a drop cap, an aside with today's loaves and visit info. The bakery worked example from custom-craft.md, in production. 04 · Meridian (studio manifesto) — partial context, theme implied. Skill picks Manifesto macrostructure + Manifesto theme + no enrichment. Pure black, oversized white display, single red accent used as a bleed band. Five declarations and a signed colophon. 05 · Tracejam (SaaS observability) — full context, no theme. Skill resists the obvious Midnight default and picks Pastel — proving a soft theme can carry a technical brief if the typography is exact. Bento Grid macrostructure + clipped-edge hero with a hand-built CSS-art trace waterfall (multi-coloured spans on a percentage grid). Six asymmetric bento tiles below. 06 · Anya (software architect personal site) — context skipped, no theme. Skill avoids Specimen-as-default for portfolio briefs and picks Long Document + Studio + no enrichment. Italic Fraunces salutation, tabular selected work sheet, no Lottie, no Three.js, no decorative gradient. Each test ships with a brief.md (the verbatim prompt, the inferences, the macrostructure stamp), an index.html (self-contained — no shared assets), and a style.css with the Hallmark stamp at the top. Findings written up at the end of site/_tests/README.md as a priority-ranked improvement list: Tier 1 — per-archetype example copy fixtures (the model bends to "Built for the modern team" without them); sub-domain refinement of the trio table (split SaaS docs vs SaaS landing); a documented theme- diversification rule; free-vs-paid font tradeoff in typography.md; worked examples per enrichment archetype in hero-enrichment.md. Tier 2 — a "different knobs" slop-test gate; more illustration recipes in custom-craft.md; non-LLM voice samples in copy.md; mobile-collapse discipline per archetype; aria-label slop-test gate. Tier 3 — project-level memory beyond the CSS stamp; a study+redesign combined verb; an example gallery on the main landing page; auto- generated OG cards per macrostructure. What this exercise revealed: the structural variety position holds (six briefs, six different shapes, no template repetition); the enrichment hierarchy works (three of six correctly ship typography- only); the remaining gap is copy voice (Tier 1 #1) and theme-pick discipline (Tier 1 #3). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
d990a2bfaf
commit
756eafff4a
@@ -0,0 +1,42 @@
|
||||
# Test 01 — Tide (indie podcast)
|
||||
|
||||
## The prompt
|
||||
|
||||
> "build me a landing page for my indie podcast called Tide. just go ahead, you pick."
|
||||
|
||||
## Context gate — skipped
|
||||
|
||||
The user opted out of the gate. The skill must state its inferences in one sentence at the top of its reply.
|
||||
|
||||
**Going with:** *audience = listeners who care about voice and atmosphere · use = listen + subscribe · tone = intimate, considered, voice-led.* If any of those is wrong, redirect.
|
||||
|
||||
## Domain inference
|
||||
|
||||
`podcast` → audio domain. From `references/structure.md`'s domain trio table: **Photographic / Quote-Led / Letter** are the three suggested macrostructures.
|
||||
|
||||
The skill picks **Quote-Led** because the medium is voice — a pull-quote from a hypothetical listener carries the weight a stat or a feature grid couldn't.
|
||||
|
||||
## Theme
|
||||
|
||||
No theme requested. Picking **Atelier** — italic Cormorant-style display, calm cream paper, generous whitespace. Audio podcasts inhabit a quiet listening posture; Atelier matches.
|
||||
|
||||
## Enrichment
|
||||
|
||||
Typography only. The hero is a quote. Reaching for a Lottie loop, an illustration, or a stock-podcaster photo would dilute the voice.
|
||||
|
||||
## Macrostructure stamp
|
||||
|
||||
```
|
||||
/* Hallmark · macrostructure: Quote-Led · H3 hero knobs: weight=italic display, attribution=under quote, length=80–160 chars
|
||||
* theme: Atelier · accent: terracotta ~2% · enrichment: none (typography only)
|
||||
* studied: no · context: skipped, inferred (audio domain)
|
||||
*/
|
||||
```
|
||||
|
||||
## What the page does
|
||||
|
||||
- Italic display pull-quote at the top, no headline above
|
||||
- Listener attribution in small caps below
|
||||
- One paragraph of host-voice copy below the quote
|
||||
- Three thin platform links (Apple / Spotify / RSS) — typographic only, no platform icons (icon-set tell)
|
||||
- Footer: a single dense line, monospace
|
||||
@@ -0,0 +1,45 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Tide — a podcast about the things that come back</title>
|
||||
<meta name="description" content="A short podcast, fortnightly, about the things in life that keep coming back. Hosted by Mira Aoki." />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<main class="fold">
|
||||
<p class="eyebrow">Tide · Episode 14 · listening from anywhere</p>
|
||||
|
||||
<blockquote class="quote">
|
||||
I started listening on a long bus ride. By the third episode I'd missed my stop, and I didn't mind.
|
||||
</blockquote>
|
||||
|
||||
<p class="attribution">— A listener · Lisbon · 2026</p>
|
||||
|
||||
<p class="body">
|
||||
Tide is a small fortnightly podcast about the things in life that keep coming back — old letters, slow recipes, the way a song sounds different in winter. Half-hour episodes, hosted by Mira Aoki. New episode every other Tuesday.
|
||||
</p>
|
||||
|
||||
<div class="platforms">
|
||||
<a href="#">Apple Podcasts</a>
|
||||
<span class="sep">·</span>
|
||||
<a href="#">Spotify</a>
|
||||
<span class="sep">·</span>
|
||||
<a href="#">Overcast</a>
|
||||
<span class="sep">·</span>
|
||||
<a href="#">RSS</a>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<span class="colophon">Tide</span>
|
||||
<span class="sep">·</span>
|
||||
<span>est. 2026</span>
|
||||
<span class="sep">·</span>
|
||||
<span>recorded in a flat in Marvila</span>
|
||||
<span class="sep">·</span>
|
||||
<a href="mailto:hello@tide.fm" style="color:inherit;">hello@tide.fm</a>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,153 @@
|
||||
/* Hallmark · macrostructure: Quote-Led · H3 hero knobs: weight=italic display, attribution=under quote, length=80–160 chars
|
||||
* theme: Atelier · accent: terracotta ~2% · enrichment: none (typography only)
|
||||
* studied: no · context: skipped, inferred (audio domain)
|
||||
*/
|
||||
|
||||
@import url("https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;1,6..72,400;1,6..72,500&display=swap");
|
||||
|
||||
:root {
|
||||
--paper: oklch(94% 0.005 60);
|
||||
--paper-2: oklch(91% 0.006 60);
|
||||
--rule: oklch(82% 0.008 60);
|
||||
--rule-2: oklch(70% 0.010 60);
|
||||
--muted: oklch(48% 0.012 60);
|
||||
--ink-2: oklch(22% 0.020 40);
|
||||
--ink: oklch(12% 0.024 40);
|
||||
--accent: oklch(45% 0.13 60);
|
||||
|
||||
--serif: "Newsreader", "Tiempos Headline", ui-serif, Georgia, serif;
|
||||
--label: "JetBrains Mono", ui-monospace, monospace;
|
||||
--space-xs: 0.5rem;
|
||||
--space-sm: 0.75rem;
|
||||
--space-md: 1rem;
|
||||
--space-lg: 1.5rem;
|
||||
--space-xl: 2.5rem;
|
||||
--space-2xl: 4rem;
|
||||
--space-3xl: 6.5rem;
|
||||
--rule-hair: 0.5px;
|
||||
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
html {
|
||||
font-family: var(--serif);
|
||||
font-size: 17px;
|
||||
line-height: 1.5;
|
||||
color: var(--ink);
|
||||
background: var(--paper);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100dvh;
|
||||
display: grid;
|
||||
grid-template-rows: 1fr auto;
|
||||
padding-inline: clamp(1.5rem, 6vw, 4rem);
|
||||
}
|
||||
|
||||
.fold {
|
||||
display: grid;
|
||||
align-content: center;
|
||||
max-width: 38rem;
|
||||
margin: 0 auto;
|
||||
padding-block: var(--space-3xl);
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
font-family: var(--label);
|
||||
font-size: 0.6875rem;
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
margin-block-end: var(--space-2xl);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.6em;
|
||||
}
|
||||
.eyebrow::before {
|
||||
content: "";
|
||||
width: 18px;
|
||||
height: 1px;
|
||||
background: var(--accent);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.quote {
|
||||
font-family: var(--serif);
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-size: clamp(2rem, 4vw + 1rem, 3.6rem);
|
||||
line-height: 1.12;
|
||||
letter-spacing: -0.012em;
|
||||
color: var(--ink);
|
||||
margin-block-end: var(--space-xl);
|
||||
}
|
||||
|
||||
.quote::before { content: "“"; }
|
||||
.quote::after { content: "”"; }
|
||||
|
||||
.attribution {
|
||||
font-family: var(--label);
|
||||
font-size: 0.6875rem;
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
margin-block-end: var(--space-2xl);
|
||||
}
|
||||
|
||||
.body {
|
||||
font-size: 1.0625rem;
|
||||
line-height: 1.65;
|
||||
color: var(--ink-2);
|
||||
margin-block-end: var(--space-2xl);
|
||||
max-width: 36rem;
|
||||
}
|
||||
|
||||
.body em { font-style: italic; }
|
||||
|
||||
.platforms {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-lg);
|
||||
font-family: var(--label);
|
||||
font-size: 0.8125rem;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.platforms a {
|
||||
color: var(--ink);
|
||||
text-decoration: none;
|
||||
background-image: linear-gradient(var(--accent), var(--accent));
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 calc(100% - 1px);
|
||||
background-size: 100% 1px;
|
||||
padding-block-end: 0.1em;
|
||||
transition: background-size 200ms var(--ease-out);
|
||||
}
|
||||
.platforms a:hover { background-size: 100% 2px; }
|
||||
|
||||
.platforms .sep {
|
||||
color: var(--rule-2);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
footer {
|
||||
border-block-start: var(--rule-hair) solid var(--rule);
|
||||
padding-block: var(--space-lg);
|
||||
font-family: var(--label);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--muted);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: var(--space-md);
|
||||
}
|
||||
|
||||
footer .colophon { color: var(--ink); }
|
||||
footer .sep { color: var(--rule-2); }
|
||||
|
||||
@media (max-width: 38rem) {
|
||||
.quote { font-size: clamp(1.75rem, 6vw + 0.5rem, 2.5rem); }
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
# Test 02 — Streampipe (open-source CLI)
|
||||
|
||||
## The prompt
|
||||
|
||||
> "Make a docs landing for an open-source CLI called Streampipe. It does stream parsing for log/event pipelines. Use the Terminal theme. Audience: backend developers. Use case: install the tool and read the docs. Tone: technical, terse."
|
||||
|
||||
## Context gate — fully answered
|
||||
|
||||
The user provided audience + use + tone. No inference needed.
|
||||
|
||||
## Theme — explicitly requested
|
||||
|
||||
**Terminal**: dark phosphor-on-black, monospace throughout, no animations, square edges.
|
||||
|
||||
## Macrostructure
|
||||
|
||||
`docs / CLI / open-source / developer` → from the domain table: **Workbench / Long Document / Component Playground**. The skill picks **Workbench** because the brief is a docs landing — it asks the page to walk the user through the tool, not just announce it.
|
||||
|
||||
## Enrichment
|
||||
|
||||
A small Tier-A pure-CSS terminal mockup as the hero — `$ streampipe parse access.log --filter status=5xx | jq` rendered in monospace with a phosphor caret. No real terminal capture. No image. Custom-built.
|
||||
|
||||
The decision is *not* "add a Lottie loop of a terminal typing"; that would be the Lottie shortcut. Pure CSS art does this cleaner.
|
||||
|
||||
## Macrostructure stamp
|
||||
|
||||
```
|
||||
/* Hallmark · macrostructure: Workbench · F2 sticky-scroll knobs: pinned=right, content=code, steps=3
|
||||
* theme: Terminal · accent: phosphor-green ~6% (functional, not decorative) · enrichment: E1 hero — pure-CSS terminal mockup (Tier A)
|
||||
* studied: no · context: explicit
|
||||
*/
|
||||
```
|
||||
|
||||
## What the page does
|
||||
|
||||
- Hero: monospace headline `$ streampipe --help` + a CSS-art terminal output mockup
|
||||
- Install row: three install commands (npm, brew, curl) as inline copy blocks
|
||||
- Three numbered feature blocks (1.0 / 2.0 / 3.0) — what it does, in plain language
|
||||
- Spec sheet (F3) for flags
|
||||
- Footer: dense colophon
|
||||
|
||||
No animations. Square edges. Underlined links. Quiet phosphor accent only on prompts and link underlines.
|
||||
@@ -0,0 +1,89 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>streampipe — a small CLI for stream parsing</title>
|
||||
<meta name="description" content="Parse log and event streams from stdin. Filter, transform, route. Open source. Single binary." />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="nav">
|
||||
<span class="wordmark">streampipe</span>
|
||||
<div class="nav__links">
|
||||
<a href="#docs">docs</a>
|
||||
<a href="#install">install</a>
|
||||
<a href="https://github.com/example/streampipe">github</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section class="hero">
|
||||
<div>
|
||||
<h1 class="hero__headline"><span class="prompt">$</span> streampipe<br>--help</h1>
|
||||
<p class="hero__lede">A small, fast, single-binary CLI for parsing log and event streams from stdin. Filter, transform, route. Composes with anything that emits lines.</p>
|
||||
<div class="hero__cta">
|
||||
<span class="hero__cta-label">// install</span>
|
||||
<pre class="cmd"><span class="prompt">$</span><span>brew install streampipe</span></pre>
|
||||
<pre class="cmd"><span class="prompt">$</span><span>npm i -g @streampipe/cli</span></pre>
|
||||
<pre class="cmd"><span class="prompt">$</span><span>curl -sSL streampipe.dev/install.sh | sh</span></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<pre class="term" aria-label="Example terminal session">
|
||||
<span class="ln"><span class="prompt">$</span> streampipe parse access.log <span class="flag">--filter</span> status=5xx <span class="flag">--out</span> ndjson</span>
|
||||
<span class="ln"><span class="meta">opening access.log (62 MB)…</span></span>
|
||||
<span class="ln"><span class="meta">scanning 412,309 lines…</span></span>
|
||||
<span class="ln"><span class="meta">match: status ∈ {500, 502, 503, 504}</span></span>
|
||||
<span class="ln">{"ts":"2026-04-30T10:14:22Z","status":<span class="num">503</span>,"path":"/api/v2/jobs"}</span>
|
||||
<span class="ln">{"ts":"2026-04-30T10:14:24Z","status":<span class="num">502</span>,"path":"/api/v2/users/49"}</span>
|
||||
<span class="ln">{"ts":"2026-04-30T10:14:31Z","status":<span class="num">500</span>,"path":"/api/v2/uploads"}</span>
|
||||
<span class="ln">{"ts":"2026-04-30T10:14:33Z","status":<span class="num">503</span>,"path":"/api/v2/jobs"}</span>
|
||||
<span class="ln"><span class="meta">… 1,184 more</span></span>
|
||||
<span class="ln"><span class="meta">duration: 0.42s · throughput: 982k lines/s</span></span>
|
||||
<span class="ln"><span class="prompt">$</span> <span class="caret"></span></span></pre>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<span class="section__num"><span>1.0</span> / parse</span>
|
||||
<div>
|
||||
<h2 class="section__title">Read anything that emits lines.</h2>
|
||||
<p class="section__body">Files, pipes, sockets, kubectl logs. Streampipe reads from stdin by default; <code>--from</code> takes a path or URL. Detects format on the first 100 lines (json, ndjson, syslog, common, custom regex).</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<span class="section__num"><span>2.0</span> / filter</span>
|
||||
<div>
|
||||
<h2 class="section__title">Express filters in a small language, not in regex.</h2>
|
||||
<p class="section__body">Filters compile to a typed predicate AST: <code>status >= 500</code>, <code>duration_ms > 200 and path ~ "/api/"</code>, <code>host in {a, b, c}</code>. No quoting hell, no shell-specific escaping.</p>
|
||||
<dl class="spec">
|
||||
<dt>--filter</dt><dd>predicate expression</dd><dd class="meta">repeatable</dd>
|
||||
<dt>--from</dt><dd>file path, URL, or `-` for stdin</dd><dd class="meta">default: stdin</dd>
|
||||
<dt>--out</dt><dd>ndjson · csv · text · table</dd><dd class="meta">default: ndjson</dd>
|
||||
<dt>--at</dt><dd>truncate after N records</dd><dd class="meta">optional</dd>
|
||||
<dt>--debug</dt><dd>show parse stats</dd><dd class="meta">optional</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<span class="section__num"><span>3.0</span> / route</span>
|
||||
<div>
|
||||
<h2 class="section__title">Compose with anything that takes lines.</h2>
|
||||
<p class="section__body">Streampipe is line-oriented all the way through. Pipe its output into <code>jq</code>, <code>gron</code>, <code>vmctl</code>, <code>kafkacat</code>, or your own thing. No daemon, no agent, no config file unless you want one.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<span class="ink"># streampipe · v0.4.2 · 2026-04-30 · MIT</span>
|
||||
# repo: <a href="https://github.com/example/streampipe">github.com/example/streampipe</a>
|
||||
# docs: <a href="https://streampipe.dev/docs">streampipe.dev/docs</a>
|
||||
# issues: <a href="https://github.com/example/streampipe/issues">github.com/example/streampipe/issues</a>
|
||||
#
|
||||
# built by a small group of backend folks who got tired of writing
|
||||
# the same awk + jq pipeline at every job.
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,239 @@
|
||||
/* Hallmark · macrostructure: Workbench · F2 sticky-scroll knobs: pinned=right, content=code, steps=3
|
||||
* theme: Terminal · accent: phosphor-green ~6% (functional, not decorative) · enrichment: E1 hero — pure-CSS terminal mockup (Tier A)
|
||||
* studied: no · context: explicit
|
||||
*/
|
||||
|
||||
@import url("https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;500&display=swap");
|
||||
|
||||
:root {
|
||||
--paper: oklch(11% 0.018 145); /* deep cool dark, slight green */
|
||||
--paper-2: oklch(15% 0.020 145);
|
||||
--paper-3: oklch(20% 0.020 145);
|
||||
--rule: oklch(28% 0.015 145);
|
||||
--rule-2: oklch(40% 0.025 145);
|
||||
--muted: oklch(58% 0.025 145);
|
||||
--ink-2: oklch(82% 0.020 145);
|
||||
--ink: oklch(94% 0.012 145);
|
||||
--accent: oklch(78% 0.19 138); /* phosphor green */
|
||||
--accent-2: oklch(86% 0.16 138);
|
||||
|
||||
--mono: "Geist Mono", "JetBrains Mono", ui-monospace, monospace;
|
||||
--space-xs: 0.5rem;
|
||||
--space-sm: 0.75rem;
|
||||
--space-md: 1rem;
|
||||
--space-lg: 1.5rem;
|
||||
--space-xl: 2.5rem;
|
||||
--space-2xl: 4rem;
|
||||
--space-3xl: 6rem;
|
||||
|
||||
--rule-hair: 0.5px;
|
||||
--rule-fine: 1px;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
html {
|
||||
font-family: var(--mono);
|
||||
font-size: 14px;
|
||||
line-height: 1.55;
|
||||
color: var(--ink-2);
|
||||
background: var(--paper);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100dvh;
|
||||
padding-inline: clamp(1.5rem, 5vw, 4rem);
|
||||
padding-block: var(--space-2xl);
|
||||
max-width: 76rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
padding-block: var(--space-md);
|
||||
border-block-end: var(--rule-hair) solid var(--rule);
|
||||
margin-block-end: var(--space-3xl);
|
||||
}
|
||||
.nav .wordmark { color: var(--ink); }
|
||||
.nav .wordmark::before { content: "$ "; color: var(--accent); }
|
||||
.nav__links { display: flex; gap: var(--space-lg); }
|
||||
.nav__links a {
|
||||
color: var(--muted);
|
||||
text-decoration: none;
|
||||
text-underline-offset: 4px;
|
||||
}
|
||||
.nav__links a:hover { color: var(--ink); text-decoration: underline; }
|
||||
|
||||
/* Hero — headline + CSS-art terminal mockup */
|
||||
.hero {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: var(--space-2xl);
|
||||
align-items: start;
|
||||
margin-block-end: var(--space-3xl);
|
||||
padding-block-end: var(--space-2xl);
|
||||
border-block-end: var(--rule-hair) solid var(--rule);
|
||||
}
|
||||
|
||||
.hero__headline {
|
||||
font-family: var(--mono);
|
||||
font-size: clamp(1.75rem, 3vw + 0.5rem, 2.75rem);
|
||||
font-weight: 500;
|
||||
letter-spacing: -0.005em;
|
||||
color: var(--ink);
|
||||
line-height: 1.1;
|
||||
}
|
||||
.hero__headline .prompt { color: var(--accent); }
|
||||
|
||||
.hero__lede {
|
||||
margin-block-start: var(--space-lg);
|
||||
color: var(--ink-2);
|
||||
max-width: 40ch;
|
||||
}
|
||||
|
||||
.hero__cta {
|
||||
margin-block-start: var(--space-xl);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-sm);
|
||||
}
|
||||
.hero__cta-label {
|
||||
color: var(--muted);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.cmd {
|
||||
background: var(--paper-2);
|
||||
border: var(--rule-hair) solid var(--rule);
|
||||
padding: var(--space-md) var(--space-lg);
|
||||
color: var(--ink);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-md);
|
||||
}
|
||||
.cmd .prompt { color: var(--accent); user-select: none; }
|
||||
|
||||
/* CSS-art terminal mockup — pure CSS, no images, no Lottie */
|
||||
.term {
|
||||
background: var(--paper-3);
|
||||
border: var(--rule-hair) solid var(--rule-2);
|
||||
font-family: var(--mono);
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.7;
|
||||
padding: var(--space-md) var(--space-lg);
|
||||
color: var(--ink-2);
|
||||
display: grid;
|
||||
gap: 0.05rem;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.term::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0;
|
||||
height: 28px;
|
||||
background: var(--paper-2);
|
||||
border-block-end: var(--rule-hair) solid var(--rule);
|
||||
}
|
||||
.term::after {
|
||||
content: "streampipe — 80×24";
|
||||
position: absolute;
|
||||
top: 6px; left: 12px;
|
||||
font-size: 0.6875rem;
|
||||
color: var(--muted);
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.term { padding-block-start: 2.6rem; }
|
||||
|
||||
.term .ln { display: block; white-space: pre; }
|
||||
.term .prompt { color: var(--accent); }
|
||||
.term .flag { color: var(--accent-2); }
|
||||
.term .num { color: var(--ink); font-weight: 500; }
|
||||
.term .meta { color: var(--muted); }
|
||||
.term .caret {
|
||||
display: inline-block;
|
||||
width: 0.55em;
|
||||
height: 1em;
|
||||
background: var(--accent);
|
||||
vertical-align: -0.15em;
|
||||
margin-inline-start: 0.05em;
|
||||
animation: blink 1.05s steps(2) infinite;
|
||||
}
|
||||
@keyframes blink { to { background: transparent; } }
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.term .caret { animation: none; }
|
||||
}
|
||||
|
||||
/* Section heads — left-margin numbered (Workbench style) */
|
||||
.section {
|
||||
display: grid;
|
||||
grid-template-columns: 6rem 1fr;
|
||||
gap: var(--space-lg);
|
||||
margin-block-end: var(--space-3xl);
|
||||
padding-block-end: var(--space-2xl);
|
||||
border-block-end: var(--rule-hair) solid var(--rule);
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.section__num {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.8125rem;
|
||||
color: var(--muted);
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
.section__num span { color: var(--accent); }
|
||||
|
||||
.section__title {
|
||||
font-family: var(--mono);
|
||||
font-size: 1.25rem;
|
||||
font-weight: 500;
|
||||
color: var(--ink);
|
||||
margin-block-end: var(--space-md);
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
.section__body { color: var(--ink-2); max-width: 60ch; }
|
||||
.section__body code { color: var(--accent-2); background: transparent; }
|
||||
|
||||
/* Spec sheet (F3) — flags */
|
||||
.spec {
|
||||
display: grid;
|
||||
grid-template-columns: 12rem 1fr 1fr;
|
||||
gap: var(--space-md);
|
||||
font-family: var(--mono);
|
||||
font-size: 0.8125rem;
|
||||
margin-block-start: var(--space-lg);
|
||||
}
|
||||
.spec > * {
|
||||
padding-block: var(--space-sm);
|
||||
border-block-start: var(--rule-hair) solid var(--rule);
|
||||
}
|
||||
.spec dt {
|
||||
color: var(--accent-2);
|
||||
}
|
||||
.spec dd { margin: 0; color: var(--ink-2); }
|
||||
.spec .meta { color: var(--muted); }
|
||||
|
||||
/* Footer — dense colophon */
|
||||
footer {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.7;
|
||||
color: var(--muted);
|
||||
white-space: pre-wrap;
|
||||
padding-block: var(--space-2xl);
|
||||
border-block-start: var(--rule-hair) solid var(--rule);
|
||||
}
|
||||
footer .ink { color: var(--ink); }
|
||||
footer a { color: var(--ink-2); text-decoration: underline; text-underline-offset: 4px; }
|
||||
|
||||
@media (max-width: 56rem) {
|
||||
.hero { grid-template-columns: 1fr; }
|
||||
.section { grid-template-columns: 1fr; }
|
||||
.section__num { color: var(--accent); }
|
||||
.spec { grid-template-columns: 8rem 1fr; }
|
||||
.spec .meta { display: none; }
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
# Test 03 — Maple Street Bread (artisan bakery)
|
||||
|
||||
## The prompt
|
||||
|
||||
> "Landing page for Maple Street Bread. Audience: locals who want to buy bread. Use: see what's available + visit. Tone: warm, hand-set, considered."
|
||||
|
||||
## Context gate — fully answered
|
||||
|
||||
Audience, use, tone all explicit. No inference needed.
|
||||
|
||||
## Domain inference
|
||||
|
||||
`bakery / kitchen / café / atelier + craft brief` → from the domain table: **Photographic / Long Document / Catalogue**.
|
||||
|
||||
The skill picks **Long Document**. The bakery is a story-led brand — there's an owner, a process, a daily rhythm — and a Long Document treats that as prose first, with the menu as a quiet aside. Catalogue (a uniform grid of breads) was the runner-up; Long Document wins because the *narrative* is the differentiator from a corporate bakery chain.
|
||||
|
||||
## Theme
|
||||
|
||||
No theme requested. Picking **Linen** — warm beige paper, plain serif, hanging headings, generous space. Linen is the only theme that reads as "shop in a real neighbourhood". Salon would be too formal; Garden too literal.
|
||||
|
||||
## Enrichment
|
||||
|
||||
**E5 Custom Illustration Centerpiece, Tier B (hand-built SVG).** The bakery brief is the canonical example for going custom — a hand-drawn loaf in 60 lines of SVG, three paths, with a `@property`-driven subtle breathing animation.
|
||||
|
||||
The decision is *not* to pull a Storyset bakery illustration; that would be the library shortcut. *Not* a Lottie of a baker; that's the Lottie shortcut. *Not* a Midjourney bread photograph; raw output reads as AI. **Hand-built is the right tier here.**
|
||||
|
||||
## Macrostructure stamp
|
||||
|
||||
```
|
||||
/* Hallmark · macrostructure: Long Document · H5 hero knobs: salutation=time-stamp, body=2 paragraphs, signoff=initials
|
||||
* theme: Linen · accent: warm-amber ~3%
|
||||
* enrichment: E5 Custom Illustration · craft: tier-B hand-built SVG (3 paths, 58 lines)
|
||||
* animation: @property --rise · 6s alternate · reduced-motion: static
|
||||
* studied: no · context: explicit
|
||||
*/
|
||||
```
|
||||
|
||||
## What the page does
|
||||
|
||||
- A time-stamped salutation ("Saturday, 6:14 a.m.") instead of a brand name banner — the bakery introduces itself as a moment in the day
|
||||
- A two-paragraph letter-style body about who bakes and what's baked
|
||||
- A hand-drawn SVG loaf, animated with a slow breath
|
||||
- A small handwritten-feel "today's loaves" list as marginalia
|
||||
- Address + hours in italic at the bottom
|
||||
- No buttons, no CTA chip, no "Order online" call. Just a phone number, a street address, and an open-hours range.
|
||||
|
||||
The page is small. It's supposed to be.
|
||||
@@ -0,0 +1,85 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Maple Street Bread — sourdough, baked overnight, sold fresh</title>
|
||||
<meta name="description" content="A small wood-oven bakery on Maple Street. Sourdough, slow-fermented, baked overnight, sold from 7 a.m. while it lasts." />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p class="eyebrow">Maple Street · est. 2026 · n.º 47</p>
|
||||
|
||||
<div class="layout">
|
||||
|
||||
<article class="letter">
|
||||
<h1 class="salutation">Saturday, 6:14 a.m.</h1>
|
||||
|
||||
<p>The dough went in at midnight. By the time the lights are on outside, the bench is dusted, the loaves are scored, and the oven is hotter than a summer roof. We bake what we baked yesterday and what we'll bake tomorrow — sourdough, slow, by hand, on stone — because that's the bread we wanted to eat when we moved here ten years ago and couldn't find any.</p>
|
||||
|
||||
<p>The shop opens at seven. There are usually thirty loaves and they're usually gone by noon. We don't do online orders. We don't deliver. If you want a loaf, walk over. Bring a bag.</p>
|
||||
|
||||
<div class="loaf-wrap">
|
||||
<svg class="loaf-svg" viewBox="0 0 220 130" xmlns="http://www.w3.org/2000/svg" aria-label="A loaf of sourdough bread">
|
||||
<path class="loaf-body"
|
||||
d="M 30 95
|
||||
C 30 60, 70 25, 110 25
|
||||
C 150 25, 190 60, 190 95
|
||||
L 195 110
|
||||
L 25 110 Z" />
|
||||
<path class="loaf-shade"
|
||||
d="M 30 95
|
||||
C 30 80, 60 70, 110 70
|
||||
C 160 70, 190 80, 190 95
|
||||
L 195 110
|
||||
L 25 110 Z" />
|
||||
<path class="loaf-score"
|
||||
d="M 70 60 L 95 40
|
||||
M 100 65 L 130 38
|
||||
M 138 60 L 168 42" />
|
||||
</svg>
|
||||
<p class="loaf-cap">Plate 47 · scored before the proof</p>
|
||||
</div>
|
||||
|
||||
<p>The oven runs on local oak. The flour is from a mill three hours west. The starter is from a baker in Évora who taught us in 2014 and is still alive. None of these are marketing claims; they are how the bread happens.</p>
|
||||
|
||||
<p class="signoff">
|
||||
Thank you for walking over,<br>
|
||||
<strong>— H. & N.</strong>
|
||||
</p>
|
||||
</article>
|
||||
|
||||
<aside class="aside">
|
||||
<h3>Today's loaves</h3>
|
||||
<ul>
|
||||
<li>Country sour <span class="price">€ 6,20</span></li>
|
||||
<li>Seeded rye <span class="price">€ 6,80</span></li>
|
||||
<li>Olive & thyme <span class="price">€ 7,40</span></li>
|
||||
<li>Walnut crown <span class="price">€ 7,40</span></li>
|
||||
<li>Brioche <span class="price">€ 5,60</span></li>
|
||||
</ul>
|
||||
|
||||
<h3>Visit</h3>
|
||||
<p>Rua de São Bento, 47<br>1200-820 Lisboa</p>
|
||||
<p>Open Tuesday — Saturday<br>7 a.m. until the loaves are gone</p>
|
||||
|
||||
<h3>Phone</h3>
|
||||
<p class="phone">+351 21 555 04 47</p>
|
||||
|
||||
<h3>The farm we mill from</h3>
|
||||
<p>Quinta de Santo António, Mafra</p>
|
||||
</aside>
|
||||
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<span class="ink">Maple Street Bread</span>
|
||||
<span class="sep">·</span>
|
||||
<span>baked on Rua de São Bento</span>
|
||||
<span class="sep">·</span>
|
||||
<span>handwritten in 2026</span>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,253 @@
|
||||
/* Hallmark · macrostructure: Long Document · H5 hero knobs: salutation=time-stamp, body=2 paragraphs, signoff=initials
|
||||
* theme: Linen · accent: warm-amber ~3%
|
||||
* enrichment: E5 Custom Illustration · craft: tier-B hand-built SVG (3 paths, 58 lines)
|
||||
* animation: @property --rise · 6s alternate · reduced-motion: static
|
||||
* studied: no · context: explicit
|
||||
*/
|
||||
|
||||
@import url("https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;1,6..72,400;1,6..72,500&display=swap");
|
||||
|
||||
:root {
|
||||
--paper: oklch(89% 0.022 95);
|
||||
--paper-2: oklch(86% 0.024 95);
|
||||
--rule: oklch(78% 0.020 95);
|
||||
--rule-2: oklch(66% 0.024 95);
|
||||
--muted: oklch(46% 0.022 90);
|
||||
--ink-2: oklch(22% 0.025 90);
|
||||
--ink: oklch(15% 0.025 90);
|
||||
--accent: oklch(54% 0.16 50); /* warm amber */
|
||||
--crust: oklch(72% 0.16 55);
|
||||
--crust-deep: oklch(48% 0.14 38);
|
||||
|
||||
--serif: "Newsreader", "Tiempos", ui-serif, Georgia, serif;
|
||||
--label: ui-monospace, "JetBrains Mono", monospace;
|
||||
|
||||
--space-xs: 0.5rem;
|
||||
--space-sm: 0.75rem;
|
||||
--space-md: 1rem;
|
||||
--space-lg: 1.5rem;
|
||||
--space-xl: 2.5rem;
|
||||
--space-2xl: 4rem;
|
||||
--space-3xl: 7rem;
|
||||
|
||||
--rule-hair: 0.5px;
|
||||
}
|
||||
|
||||
@property --rise {
|
||||
syntax: "<length>";
|
||||
initial-value: 0px;
|
||||
inherits: false;
|
||||
}
|
||||
|
||||
@property --bake {
|
||||
syntax: "<percentage>";
|
||||
initial-value: 0%;
|
||||
inherits: false;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
html {
|
||||
font-family: var(--serif);
|
||||
font-size: 17px;
|
||||
line-height: 1.65;
|
||||
color: var(--ink-2);
|
||||
background: var(--paper);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100dvh;
|
||||
padding-inline: clamp(1.5rem, 6vw, 5rem);
|
||||
padding-block: var(--space-3xl) var(--space-2xl);
|
||||
max-width: 64rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
font-family: var(--label);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
margin-block-end: var(--space-2xl);
|
||||
}
|
||||
|
||||
.layout {
|
||||
display: grid;
|
||||
grid-template-columns: 7fr 4fr;
|
||||
gap: var(--space-3xl);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
@media (max-width: 56rem) {
|
||||
.layout { grid-template-columns: 1fr; gap: var(--space-2xl); }
|
||||
}
|
||||
|
||||
.salutation {
|
||||
font-family: var(--serif);
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-size: clamp(2.25rem, 4vw + 1rem, 3.6rem);
|
||||
line-height: 1.1;
|
||||
letter-spacing: -0.018em;
|
||||
color: var(--ink);
|
||||
margin-block-end: var(--space-xl);
|
||||
}
|
||||
|
||||
.letter p {
|
||||
font-size: 1.0625rem;
|
||||
line-height: 1.72;
|
||||
margin-block-end: var(--space-md);
|
||||
max-width: 36rem;
|
||||
}
|
||||
|
||||
.letter p:first-of-type::first-letter {
|
||||
font-family: var(--serif);
|
||||
font-size: 3.6em;
|
||||
font-weight: 500;
|
||||
line-height: 0.9;
|
||||
float: left;
|
||||
margin-block-start: 0.08em;
|
||||
margin-inline-end: 0.1em;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.signoff {
|
||||
margin-block-start: var(--space-xl);
|
||||
font-family: var(--serif);
|
||||
font-style: italic;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.signoff strong {
|
||||
display: block;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
color: var(--ink);
|
||||
margin-block-start: 0.2em;
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
/* The aside — today's loaves + visit */
|
||||
.aside {
|
||||
font-family: var(--serif);
|
||||
font-size: 0.9375rem;
|
||||
line-height: 1.65;
|
||||
color: var(--ink-2);
|
||||
border-inline-start: var(--rule-hair) solid var(--rule);
|
||||
padding-inline-start: var(--space-lg);
|
||||
position: sticky;
|
||||
top: var(--space-2xl);
|
||||
}
|
||||
|
||||
@media (max-width: 56rem) {
|
||||
.aside { border-inline-start: 0; padding-inline-start: 0; padding-block-start: var(--space-xl); border-block-start: var(--rule-hair) solid var(--rule); position: static; }
|
||||
}
|
||||
|
||||
.aside h3 {
|
||||
font-family: var(--label);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
font-weight: 400;
|
||||
margin-block: var(--space-xl) var(--space-md);
|
||||
}
|
||||
|
||||
.aside h3:first-child { margin-block-start: 0; }
|
||||
|
||||
.aside ul {
|
||||
list-style: none;
|
||||
display: grid;
|
||||
gap: 0.4em;
|
||||
}
|
||||
|
||||
.aside li {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-md);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.aside li .price {
|
||||
font-style: normal;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: var(--muted);
|
||||
font-family: var(--label);
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.aside p {
|
||||
font-style: italic;
|
||||
color: var(--ink-2);
|
||||
}
|
||||
|
||||
.aside .phone {
|
||||
font-family: var(--label);
|
||||
font-style: normal;
|
||||
font-size: 0.875rem;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
/* The loaf — hand-built SVG centerpiece */
|
||||
.loaf-wrap {
|
||||
margin-block: var(--space-2xl) var(--space-xl);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.loaf-svg {
|
||||
width: 100%;
|
||||
max-width: 22rem;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
transform: translateY(var(--rise));
|
||||
animation: rise 6s ease-in-out infinite alternate;
|
||||
filter: drop-shadow(0 14px 18px oklch(28% 0.04 50 / 0.12));
|
||||
}
|
||||
|
||||
@keyframes rise { to { --rise: -4px; } }
|
||||
|
||||
.loaf-body { fill: var(--crust); }
|
||||
.loaf-shade { fill: var(--crust-deep); opacity: 0.18; }
|
||||
.loaf-score {
|
||||
stroke: var(--crust-deep);
|
||||
stroke-width: 2.5;
|
||||
stroke-linecap: round;
|
||||
fill: none;
|
||||
stroke-dasharray: 0 240;
|
||||
animation: score 1.6s 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||
}
|
||||
@keyframes score { to { stroke-dasharray: 240 240; } }
|
||||
|
||||
.loaf-cap {
|
||||
font-family: var(--label);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
margin-block-start: var(--space-md);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.loaf-svg { animation: none; --rise: 0px; }
|
||||
.loaf-score { animation: none; stroke-dasharray: 240 240; }
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-block-start: var(--space-3xl);
|
||||
padding-block: var(--space-lg);
|
||||
border-block-start: var(--rule-hair) solid var(--rule);
|
||||
font-family: var(--label);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--muted);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: var(--space-md);
|
||||
}
|
||||
|
||||
footer .ink { color: var(--ink); }
|
||||
footer .sep { color: var(--rule-2); }
|
||||
@@ -0,0 +1,49 @@
|
||||
# Test 04 — Meridian (studio manifesto)
|
||||
|
||||
## The prompt
|
||||
|
||||
> "Make me a manifesto for my new studio called Meridian. We work on environmental products. No flashy stuff."
|
||||
|
||||
## Context gate — partial
|
||||
|
||||
User gave audience-implied (people who care about environmental products) and tone (declarative, no flash). Skill infers the rest:
|
||||
|
||||
**Going with:** *audience = potential clients and collaborators · use = read our position · tone = declarative, polemical, restrained.*
|
||||
|
||||
## Domain inference
|
||||
|
||||
`manifesto / cause / advocacy` → from the domain table: **Manifesto / Quote-Led / Stat-Led**.
|
||||
|
||||
The skill picks **Manifesto** (the brief literally names it). This is the rare case where the macrostructure name and the brief word are the same — which is fine, because Manifesto-the-macrostructure is genuinely a *page shape*, not a default we should run from.
|
||||
|
||||
## Theme
|
||||
|
||||
The user said "no flashy stuff" — that's the strongest possible cue against ornament. Picking **Manifesto** the theme too: pure black paper, oversized solid type, single red accent, no gradients, no rounded corners. The theme name + macrostructure name aligning is honest here, not lazy.
|
||||
|
||||
(Salon, Studio, Pastel would all be wrong tonally. Brutal would have worked but is a touch louder.)
|
||||
|
||||
## Enrichment
|
||||
|
||||
**None.** Typography is the design. The page makes its point in five short declarations and stops.
|
||||
|
||||
The slop test catches: no abstract background gradient, no Lottie loop, no illustration, no demo video. The voice is the page.
|
||||
|
||||
## Macrostructure stamp
|
||||
|
||||
```
|
||||
/* Hallmark · macrostructure: Manifesto · H1 hero knobs: size=xxl, alignment=left-bias, underlay=none
|
||||
* theme: Manifesto · accent: red ~5% (used as bleed dividers, not flood)
|
||||
* enrichment: none (typography only — voice is the design)
|
||||
* studied: no · context: partial, inferred (audience + use)
|
||||
*/
|
||||
```
|
||||
|
||||
## What the page does
|
||||
|
||||
- Five short declarations stacked vertically, each on its own page-fold-sized band
|
||||
- A single red bleed-colour band between the third and fourth declaration as the only visual ornament
|
||||
- Numbered "I." through "V." in display weight on the left margin of each declaration
|
||||
- A signed colophon at the bottom: studio name, year, three signatories
|
||||
- No links. No CTA. No "Learn more". A manifesto is not a marketing page.
|
||||
|
||||
The page is mostly black. It is supposed to be.
|
||||
@@ -0,0 +1,63 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Meridian — a manifesto</title>
|
||||
<meta name="description" content="A small studio working on environmental products. Five things we believe." />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="masthead">
|
||||
<span class="name">Meridian</span>
|
||||
<span class="meta">2026 · <span class="accent">A MANIFESTO</span> · IN FIVE PARTS</span>
|
||||
</header>
|
||||
|
||||
<section class="title">
|
||||
<h1>WE ARE A STUDIO. WE ARE NOT A <em>PLATFORM.</em></h1>
|
||||
<p class="subtitle">— a position, signed by three of us, on the practice of building environmental products in 2026.</p>
|
||||
</section>
|
||||
|
||||
<section class="declaration">
|
||||
<span class="declaration__num">I.</span>
|
||||
<p class="declaration__body">We design products that <strong>last twelve years.</strong> We do not design products that need replacing every two.</p>
|
||||
</section>
|
||||
|
||||
<section class="declaration">
|
||||
<span class="declaration__num">II.</span>
|
||||
<p class="declaration__body">A material is not "sustainable" because the brochure says so. A material is sustainable because <strong>someone, somewhere, can repair it.</strong></p>
|
||||
</section>
|
||||
|
||||
<section class="declaration">
|
||||
<span class="declaration__num">III.</span>
|
||||
<p class="declaration__body">We do not believe in growth. We believe in <strong>fit.</strong> A studio of three is the studio we want to remain.</p>
|
||||
</section>
|
||||
|
||||
<section class="bleed">
|
||||
<p>This is not a roadmap. This is what we believe before we open the laptops.</p>
|
||||
</section>
|
||||
|
||||
<section class="declaration">
|
||||
<span class="declaration__num">IV.</span>
|
||||
<p class="declaration__body">If our work cannot be <strong>made by hand somewhere,</strong> we are not the right studio for it. We will recommend a friend.</p>
|
||||
</section>
|
||||
|
||||
<section class="declaration">
|
||||
<span class="declaration__num">V.</span>
|
||||
<p class="declaration__body">We will not put our work behind a chatbot. We will <strong>answer the email ourselves,</strong> or not at all.</p>
|
||||
</section>
|
||||
|
||||
<footer class="colophon">
|
||||
<h2>Meridian</h2>
|
||||
<span>Studio for environmental products · Lisbon · est. 2026</span>
|
||||
<span>This manifesto stands until we change it. We will say so when we do.</span>
|
||||
<div class="signed">
|
||||
<span><strong>Helena Marques</strong> — design</span>
|
||||
<span><strong>Jonas Pereira</strong> — engineering</span>
|
||||
<span><strong>Aiko Tanaka</strong> — research</span>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,185 @@
|
||||
/* Hallmark · macrostructure: Manifesto · H1 hero knobs: size=xxl, alignment=left-bias, underlay=none
|
||||
* theme: Manifesto · accent: red ~5% (used as bleed dividers, not flood)
|
||||
* enrichment: none (typography only — voice is the design)
|
||||
* studied: no · context: partial, inferred (audience + use)
|
||||
*/
|
||||
|
||||
@import url("https://fonts.googleapis.com/css2?family=Inter+Tight:wght@500;700;800;900&family=JetBrains+Mono:wght@400;500&display=swap");
|
||||
|
||||
:root {
|
||||
--paper: oklch(10% 0.005 60);
|
||||
--paper-2: oklch(14% 0.006 60);
|
||||
--rule: oklch(28% 0.008 60);
|
||||
--muted: oklch(58% 0.010 60);
|
||||
--ink-2: oklch(85% 0.005 60);
|
||||
--ink: oklch(98% 0.003 90);
|
||||
--accent: oklch(60% 0.22 27);
|
||||
|
||||
--display: "Inter Tight", "Söhne Breit", system-ui, sans-serif;
|
||||
--label: "JetBrains Mono", ui-monospace, monospace;
|
||||
|
||||
--space-xs: 0.5rem;
|
||||
--space-sm: 0.75rem;
|
||||
--space-md: 1rem;
|
||||
--space-lg: 1.5rem;
|
||||
--space-xl: 2.5rem;
|
||||
--space-2xl: 4rem;
|
||||
--space-3xl: 8rem;
|
||||
--space-4xl: 12rem;
|
||||
|
||||
--rule-fine: 2px;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
html {
|
||||
font-family: var(--display);
|
||||
font-size: 17px;
|
||||
line-height: 1.4;
|
||||
color: var(--ink);
|
||||
background: var(--paper);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100dvh;
|
||||
}
|
||||
|
||||
.masthead {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
padding: var(--space-lg) clamp(1.5rem, 6vw, 4rem);
|
||||
border-block-end: var(--rule-fine) solid var(--ink);
|
||||
font-family: var(--label);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.masthead .name {
|
||||
font-family: var(--display);
|
||||
font-weight: 800;
|
||||
font-size: 1rem;
|
||||
letter-spacing: -0.005em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.masthead .meta { color: var(--muted); }
|
||||
.masthead .meta .accent { color: var(--accent); }
|
||||
|
||||
.title {
|
||||
padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem) var(--space-2xl);
|
||||
border-block-end: var(--rule-fine) solid var(--ink);
|
||||
}
|
||||
|
||||
.title h1 {
|
||||
font-family: var(--display);
|
||||
font-weight: 900;
|
||||
font-size: clamp(3rem, 8vw + 1rem, 7.5rem);
|
||||
line-height: 0.92;
|
||||
letter-spacing: -0.035em;
|
||||
text-transform: uppercase;
|
||||
color: var(--ink);
|
||||
max-width: 18ch;
|
||||
}
|
||||
|
||||
.title h1 em {
|
||||
font-style: normal;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.title .subtitle {
|
||||
font-family: var(--label);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
margin-block-start: var(--space-xl);
|
||||
}
|
||||
|
||||
.declaration {
|
||||
display: grid;
|
||||
grid-template-columns: clamp(4rem, 10vw, 8rem) 1fr;
|
||||
gap: var(--space-xl);
|
||||
padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
|
||||
border-block-end: var(--rule-fine) solid var(--ink);
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.declaration__num {
|
||||
font-family: var(--display);
|
||||
font-weight: 800;
|
||||
font-size: clamp(2rem, 4vw + 0.5rem, 3.5rem);
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--accent);
|
||||
line-height: 0.9;
|
||||
}
|
||||
|
||||
.declaration__body {
|
||||
font-family: var(--display);
|
||||
font-weight: 700;
|
||||
font-size: clamp(1.5rem, 3vw + 0.5rem, 2.625rem);
|
||||
line-height: 1.12;
|
||||
letter-spacing: -0.018em;
|
||||
color: var(--ink);
|
||||
max-width: 28ch;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.declaration__body strong {
|
||||
background: var(--accent);
|
||||
color: var(--paper);
|
||||
padding: 0 0.18em;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Bleed-colour ornament between III and IV */
|
||||
.bleed {
|
||||
background: var(--accent);
|
||||
padding: var(--space-2xl) clamp(1.5rem, 6vw, 4rem);
|
||||
border-block-end: var(--rule-fine) solid var(--ink);
|
||||
}
|
||||
|
||||
.bleed p {
|
||||
font-family: var(--display);
|
||||
font-weight: 800;
|
||||
font-size: clamp(1.25rem, 2vw + 0.5rem, 1.75rem);
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--paper);
|
||||
text-transform: uppercase;
|
||||
max-width: 38ch;
|
||||
}
|
||||
|
||||
.colophon {
|
||||
padding: var(--space-3xl) clamp(1.5rem, 6vw, 4rem);
|
||||
display: grid;
|
||||
gap: var(--space-md);
|
||||
font-family: var(--label);
|
||||
font-size: 0.8125rem;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--muted);
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.colophon h2 {
|
||||
font-family: var(--display);
|
||||
font-weight: 800;
|
||||
font-size: 1rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--ink);
|
||||
margin-block-end: var(--space-sm);
|
||||
}
|
||||
|
||||
.colophon .signed {
|
||||
display: grid;
|
||||
gap: 0.4em;
|
||||
color: var(--ink-2);
|
||||
margin-block-start: var(--space-md);
|
||||
}
|
||||
|
||||
.colophon .signed strong {
|
||||
color: var(--ink);
|
||||
font-weight: 500;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
# Test 05 — Tracejam (SaaS observability tool)
|
||||
|
||||
## The prompt
|
||||
|
||||
> "Build a landing page for Tracejam — a tracing/observability tool for distributed systems. Audience: SREs and platform engineers. Use case: try it / contact sales. Tone: technical."
|
||||
|
||||
## Context gate — fully answered
|
||||
|
||||
Audience, use, tone all explicit. No inference needed.
|
||||
|
||||
## Domain inference
|
||||
|
||||
`platform / tool / infra / dashboard / developer` → from the domain table: **Workbench / Bento Grid / Stat-Led** are reasonable picks.
|
||||
|
||||
The skill picks **Bento Grid** because Tracejam has many surfaces (traces, metrics, alerts, logs) and Bento communicates "many entry points" the way Workbench would communicate "one thing, walked through". Workbench was the runner-up.
|
||||
|
||||
## Theme
|
||||
|
||||
The tempting default for a SaaS observability tool is **Midnight** (dark technical). But the skill resists: Midnight has been used elsewhere, and the brief says "technical" not "dark". Picking **Pastel** instead — post-Linear soft tech, low-chroma indigo accent, soft surfaces. The test is whether a Pastel page can still feel rigorous to an SRE; the answer is yes if the typography is exact.
|
||||
|
||||
## Enrichment
|
||||
|
||||
**E1 Demo Video — Clipped-Edge** archetype, but with a Tier-A pure-CSS dashboard mockup instead of real video. The mockup is a hand-built CSS-art trace waterfall (a flame chart) — it says "this is real software" without needing to ship a 4 MB video.
|
||||
|
||||
The decision is *not* "embed a Loom". *Not* "use a stock video". *Not* "build a Lottie loop of moving bars". Pure CSS art is the right tier — the dashboard is geometric, the data are coloured rectangles, and CSS does that beautifully.
|
||||
|
||||
## Macrostructure stamp
|
||||
|
||||
```
|
||||
/* Hallmark · macrostructure: Bento Grid · F1 bento knobs: tiles=6, spans=irregular, accent=corner-only
|
||||
* H7 hero: Demo Video Clipped-Edge · clip=right, aspect=16/10, frame=hairline
|
||||
* theme: Pastel · accent: indigo ~3% · enrichment: tier-A CSS art mockup (flame chart trace waterfall)
|
||||
* studied: no · context: explicit
|
||||
*/
|
||||
```
|
||||
|
||||
## What the page does
|
||||
|
||||
- Hero: clipped-edge browser-frame mockup containing a CSS-art trace waterfall (a flame chart of an HTTP request fanning out into ~20 spans across four services). Headline left, mockup right, mockup extends ~12 vw past the viewport edge.
|
||||
- Bento grid below: six asymmetric tiles, each one a feature with its own visual treatment (a numerical stat, a small CSS chart, a code snippet, a quote from a user, a tabular table of integrations, a single sentence)
|
||||
- Two CTAs: "Try it" (outlined chip) and "Talk to sales" (typographic link)
|
||||
- Footer: index columns
|
||||
- Soft 12 px radius on every card. Indigo accent at ~3 % footprint.
|
||||
@@ -0,0 +1,176 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Tracejam — distributed tracing that explains itself</title>
|
||||
<meta name="description" content="A tracing and observability platform for SREs. Open the trace, find the span, fix the regression. No glossary required." />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="nav">
|
||||
<span class="wordmark">Tracejam</span>
|
||||
<div class="nav__links">
|
||||
<a href="#docs">Docs</a>
|
||||
<a href="#changelog">Changelog</a>
|
||||
<a href="#pricing">Pricing</a>
|
||||
<a class="btn" href="#sign-up">Sign in</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section class="hero">
|
||||
<div class="hero__copy">
|
||||
<p class="hero__eyebrow">Tracejam · v0.4 · for SREs</p>
|
||||
<h1 class="hero__title">Distributed tracing that <em>explains itself.</em></h1>
|
||||
<p class="hero__lede">Open the trace, find the span that regressed, and read why — in plain text — without a glossary, a YAML diff, or a status-page hunt.</p>
|
||||
<div class="hero__cta">
|
||||
<a class="btn--primary" href="#try">Try it free</a>
|
||||
<a class="btn--ghost" href="#sales">Talk to sales</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<figure class="mock" aria-hidden="true">
|
||||
<header class="mock__chrome">
|
||||
<span class="mock__dot"></span>
|
||||
<span class="mock__dot"></span>
|
||||
<span class="mock__dot"></span>
|
||||
<span class="mock__url">app.tracejam.io / traces / 7f8e9a</span>
|
||||
</header>
|
||||
<div class="mock__body">
|
||||
<aside class="mock__rail">
|
||||
<span class="mock__rail-item is-active"></span>
|
||||
<span class="mock__rail-item"></span>
|
||||
<span class="mock__rail-item"></span>
|
||||
<span class="mock__rail-item"></span>
|
||||
<span class="mock__rail-item"></span>
|
||||
</aside>
|
||||
<main class="mock__main">
|
||||
<header class="mock__head">
|
||||
<span class="mock__breadcrumb">production / api-edge / <b>POST /v2/checkout</b></span>
|
||||
<span class="mock__pill">trace 7f8e9a</span>
|
||||
</header>
|
||||
<p class="mock__title">Span tree · <span class="meta">23 spans · 4 services · 482 ms</span></p>
|
||||
|
||||
<div class="trace" role="img" aria-label="Flame chart of a checkout request fanning into 12 spans across 4 services">
|
||||
<div class="trace__row"><span class="trace__label"><b>api-edge</b> · POST /v2/checkout</span>
|
||||
<span class="trace__lane"><span class="trace__bar" style="left:0%; width:96%;"></span></span></div>
|
||||
<div class="trace__row"><span class="trace__label">↳ auth.verify</span>
|
||||
<span class="trace__lane"><span class="trace__bar b" style="left:2%; width:8%;"></span></span></div>
|
||||
<div class="trace__row"><span class="trace__label">↳ rate-limit.check</span>
|
||||
<span class="trace__lane"><span class="trace__bar b" style="left:11%; width:4%;"></span></span></div>
|
||||
<div class="trace__row"><span class="trace__label">↳ <b>orders.create</b></span>
|
||||
<span class="trace__lane"><span class="trace__bar" style="left:16%; width:62%;"></span></span></div>
|
||||
<div class="trace__row"><span class="trace__label"> ↳ inventory.reserve</span>
|
||||
<span class="trace__lane"><span class="trace__bar c" style="left:19%; width:14%;"></span></span></div>
|
||||
<div class="trace__row"><span class="trace__label"> ↳ pricing.compute</span>
|
||||
<span class="trace__lane"><span class="trace__bar c" style="left:34%; width:9%;"></span></span></div>
|
||||
<div class="trace__row"><span class="trace__label"> ↳ <b>payments.charge</b></span>
|
||||
<span class="trace__lane"><span class="trace__bar d" style="left:44%; width:30%;"></span></span></div>
|
||||
<div class="trace__row"><span class="trace__label"> ↳ stripe.tokenize</span>
|
||||
<span class="trace__lane"><span class="trace__bar d" style="left:46%; width:6%;"></span></span></div>
|
||||
<div class="trace__row"><span class="trace__label"> ↳ stripe.charge ⚠</span>
|
||||
<span class="trace__lane"><span class="trace__bar d" style="left:53%; width:20%;"></span></span></div>
|
||||
<div class="trace__row"><span class="trace__label">↳ notify.email</span>
|
||||
<span class="trace__lane"><span class="trace__bar b" style="left:79%; width:8%;"></span></span></div>
|
||||
<div class="trace__row"><span class="trace__label">↳ analytics.write</span>
|
||||
<span class="trace__lane"><span class="trace__bar b" style="left:88%; width:7%;"></span></span></div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</figure>
|
||||
</section>
|
||||
|
||||
<section class="bento" aria-label="What Tracejam does">
|
||||
|
||||
<article class="tile tile-stat">
|
||||
<span class="label">Cold-start</span>
|
||||
<h3>Open a regressed trace in <em>under 4 seconds</em></h3>
|
||||
<span class="stat-num"><span class="accent">4</span>s</span>
|
||||
<p>From the alert link to the slow span, no clicks. Tracejam pre-renders the flame chart for every alert.</p>
|
||||
</article>
|
||||
|
||||
<article class="tile tile-spark">
|
||||
<span class="label">Latency drift</span>
|
||||
<h3>p95 over 24 h, by service.</h3>
|
||||
<div class="spark" aria-hidden="true">
|
||||
<span style="height:30%"></span>
|
||||
<span style="height:42%"></span>
|
||||
<span style="height:38%"></span>
|
||||
<span style="height:55%"></span>
|
||||
<span style="height:48%"></span>
|
||||
<span style="height:60%"></span>
|
||||
<span style="height:72%"></span>
|
||||
<span style="height:64%"></span>
|
||||
<span style="height:80%"></span>
|
||||
<span style="height:55%"></span>
|
||||
<span style="height:46%"></span>
|
||||
<span style="height:40%"></span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article class="tile tile-quote">
|
||||
<p class="quote">It told me the span that regressed, the deploy that caused it, and the engineer to ask. We rolled back in eight minutes.</p>
|
||||
<span class="attribution">— Staff SRE · large fintech · 2026</span>
|
||||
</article>
|
||||
|
||||
<article class="tile tile-code">
|
||||
<span class="label">Drop-in OTLP</span>
|
||||
<pre class="code-mini"><span class="k">export</span> OTEL_EXPORTER_OTLP_ENDPOINT=<span class="s">https://otlp.tracejam.io</span>
|
||||
<span class="k">export</span> OTEL_EXPORTER_OTLP_HEADERS=<span class="s">"x-tracejam-key=tj_…"</span></pre>
|
||||
</article>
|
||||
|
||||
<article class="tile tile-integrations">
|
||||
<span class="label">Connects to</span>
|
||||
<h3>OpenTelemetry-native. No agent, no sidecar.</h3>
|
||||
<div class="integrations">
|
||||
<span>OTLP / gRPC</span>
|
||||
<span>OTLP / HTTP</span>
|
||||
<span>Datadog ingest</span>
|
||||
<span>Honeycomb ingest</span>
|
||||
<span>Prometheus</span>
|
||||
<span>kubectl events</span>
|
||||
<span>PagerDuty</span>
|
||||
<span>Slack</span>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<div class="col brand">
|
||||
<h4>Tracejam</h4>
|
||||
<p>Distributed tracing that explains itself. Built by SREs who got tired of unscrambling stack traces from alerts.</p>
|
||||
<p class="colophon">© 2026 · MIT-licensed agent · SOC 2 Type II</p>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Product</h4>
|
||||
<ul>
|
||||
<li><a href="#">Tracing</a></li>
|
||||
<li><a href="#">Metrics</a></li>
|
||||
<li><a href="#">Alerts</a></li>
|
||||
<li><a href="#">Logs</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Developers</h4>
|
||||
<ul>
|
||||
<li><a href="#">Docs</a></li>
|
||||
<li><a href="#">Changelog</a></li>
|
||||
<li><a href="#">Open-source agent</a></li>
|
||||
<li><a href="#">Status</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col">
|
||||
<h4>Company</h4>
|
||||
<ul>
|
||||
<li><a href="#">About</a></li>
|
||||
<li><a href="#">Careers</a></li>
|
||||
<li><a href="#">Security</a></li>
|
||||
<li><a href="#">Contact</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,533 @@
|
||||
/* Hallmark · macrostructure: Bento Grid · F1 bento knobs: tiles=6, spans=irregular, accent=corner-only
|
||||
* H7 hero: Demo Video Clipped-Edge · clip=right, aspect=16/10, frame=hairline
|
||||
* theme: Pastel · accent: indigo ~3% · enrichment: tier-A CSS art mockup (flame chart trace waterfall)
|
||||
* studied: no · context: explicit
|
||||
*/
|
||||
|
||||
@import url("https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500&display=swap");
|
||||
|
||||
:root {
|
||||
--paper: oklch(96% 0.022 285);
|
||||
--paper-2: oklch(93.5% 0.026 285);
|
||||
--paper-3: oklch(90% 0.030 285);
|
||||
--rule: oklch(82% 0.024 280);
|
||||
--rule-2: oklch(72% 0.030 275);
|
||||
--muted: oklch(50% 0.030 275);
|
||||
--neutral: oklch(38% 0.038 270);
|
||||
--ink-2: oklch(28% 0.040 270);
|
||||
--ink: oklch(22% 0.040 270);
|
||||
--accent: oklch(56% 0.130 282);
|
||||
--accent-soft: oklch(78% 0.060 282);
|
||||
|
||||
/* trace colours — cool indigo family at varied lightness */
|
||||
--span-a: oklch(62% 0.13 282);
|
||||
--span-b: oklch(70% 0.10 240);
|
||||
--span-c: oklch(74% 0.09 200);
|
||||
--span-d: oklch(56% 0.16 30); /* error span */
|
||||
|
||||
--display: "Geist", "Söhne", system-ui, sans-serif;
|
||||
--body: "Geist", "Söhne", system-ui, sans-serif;
|
||||
--mono: "Geist Mono", ui-monospace, monospace;
|
||||
|
||||
--space-xs: 0.5rem;
|
||||
--space-sm: 0.75rem;
|
||||
--space-md: 1rem;
|
||||
--space-lg: 1.5rem;
|
||||
--space-xl: 2.5rem;
|
||||
--space-2xl: 4rem;
|
||||
--space-3xl: 6.5rem;
|
||||
|
||||
--radius: 12px;
|
||||
--shadow-sm: 0 1px 2px oklch(50% 0.13 282 / 0.06);
|
||||
--shadow-md: 0 8px 24px -10px oklch(50% 0.13 282 / 0.16);
|
||||
--rule-hair: 0.5px;
|
||||
--rule-fine: 1px;
|
||||
|
||||
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
html {
|
||||
font-family: var(--body);
|
||||
font-size: 16px;
|
||||
line-height: 1.55;
|
||||
color: var(--ink-2);
|
||||
background: var(--paper);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100dvh;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: var(--space-md) clamp(1.5rem, 5vw, 3rem);
|
||||
border-block-end: var(--rule-hair) solid var(--rule);
|
||||
background: color-mix(in oklch, var(--paper) 90%, transparent);
|
||||
backdrop-filter: blur(12px);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.nav .wordmark {
|
||||
font-family: var(--display);
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--ink);
|
||||
}
|
||||
.nav .wordmark::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background: var(--accent);
|
||||
border-radius: 4px;
|
||||
margin-inline-end: 0.6em;
|
||||
vertical-align: -2px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.nav__links {
|
||||
display: flex;
|
||||
gap: var(--space-lg);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.nav__links a {
|
||||
color: var(--ink-2);
|
||||
text-decoration: none;
|
||||
}
|
||||
.nav__links a:hover { color: var(--ink); }
|
||||
.nav__links .btn {
|
||||
border: 1px solid var(--ink);
|
||||
border-radius: 999px;
|
||||
padding: 0.45rem 1rem;
|
||||
color: var(--ink);
|
||||
transition: background 120ms var(--ease-out), color 120ms var(--ease-out);
|
||||
}
|
||||
.nav__links .btn:hover { background: var(--ink); color: var(--paper); }
|
||||
|
||||
/* ─── HERO — clipped-edge with CSS-art trace waterfall ─── */
|
||||
|
||||
.hero {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(20rem, 1fr) 1.4fr;
|
||||
gap: var(--space-2xl);
|
||||
padding: var(--space-3xl) 0 var(--space-2xl) clamp(1.5rem, 5vw, 3rem);
|
||||
align-items: center;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.hero__copy { max-width: 32rem; }
|
||||
|
||||
.hero__eyebrow {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
margin-block-end: var(--space-md);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.6em;
|
||||
}
|
||||
.hero__eyebrow::before {
|
||||
content: "";
|
||||
width: 6px; height: 6px;
|
||||
background: var(--accent);
|
||||
border-radius: 1px;
|
||||
}
|
||||
|
||||
.hero__title {
|
||||
font-family: var(--display);
|
||||
font-weight: 600;
|
||||
font-size: clamp(2.25rem, 4vw + 1rem, 3.875rem);
|
||||
line-height: 1.05;
|
||||
letter-spacing: -0.035em;
|
||||
color: var(--ink);
|
||||
max-width: 16ch;
|
||||
}
|
||||
.hero__title em {
|
||||
font-style: normal;
|
||||
background: linear-gradient(180deg, transparent 70%, var(--accent-soft) 70%);
|
||||
padding: 0 0.05em;
|
||||
}
|
||||
|
||||
.hero__lede {
|
||||
margin-block: var(--space-lg) var(--space-xl);
|
||||
color: var(--ink-2);
|
||||
font-size: 1.0625rem;
|
||||
max-width: 38ch;
|
||||
}
|
||||
|
||||
.hero__cta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-md);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn--primary {
|
||||
background: var(--ink);
|
||||
color: var(--paper);
|
||||
font-family: var(--display);
|
||||
font-weight: 500;
|
||||
font-size: 0.9375rem;
|
||||
padding: 0.65rem 1.2rem;
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
text-decoration: none;
|
||||
transition: transform 100ms var(--ease-out), background 120ms var(--ease-out);
|
||||
}
|
||||
.btn--primary:hover { background: var(--accent); transform: translateY(-1px); }
|
||||
.btn--primary:active { transform: translateY(0); transition-duration: 60ms; }
|
||||
|
||||
.btn--ghost {
|
||||
font-family: var(--display);
|
||||
font-weight: 500;
|
||||
font-size: 0.9375rem;
|
||||
color: var(--ink);
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4em;
|
||||
}
|
||||
.btn--ghost::after {
|
||||
content: "→";
|
||||
transition: transform 200ms var(--ease-out);
|
||||
}
|
||||
.btn--ghost:hover::after { transform: translateX(2px); }
|
||||
|
||||
/* ─── The clipped browser-frame mockup ─── */
|
||||
.mock {
|
||||
width: calc(100% + 12vw);
|
||||
aspect-ratio: 16 / 10;
|
||||
border-radius: 14px;
|
||||
overflow: hidden;
|
||||
background: var(--paper-2);
|
||||
border: var(--rule-hair) solid var(--rule);
|
||||
box-shadow: var(--shadow-md);
|
||||
transform: rotate(-0.4deg) translateY(-2px);
|
||||
transform-origin: left center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.mock__chrome {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 10px 14px;
|
||||
background: color-mix(in oklch, var(--paper-2) 100%, var(--rule) 14%);
|
||||
border-block-end: var(--rule-hair) solid var(--rule);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.mock__dot {
|
||||
width: 9px; height: 9px;
|
||||
border-radius: 50%;
|
||||
background: color-mix(in oklch, var(--rule-2) 80%, transparent);
|
||||
}
|
||||
.mock__url {
|
||||
margin-inline-start: 12px;
|
||||
font-family: var(--mono);
|
||||
font-size: 0.7rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.mock__body {
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-columns: 56px 1fr;
|
||||
background: var(--paper);
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.mock__rail {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
padding: 14px 12px;
|
||||
background: color-mix(in oklch, var(--paper) 100%, var(--paper-2) 60%);
|
||||
border-inline-end: var(--rule-hair) solid var(--rule);
|
||||
}
|
||||
.mock__rail-item {
|
||||
width: 28px; height: 28px;
|
||||
border-radius: 6px;
|
||||
background: color-mix(in oklch, var(--rule) 100%, transparent);
|
||||
}
|
||||
.mock__rail-item.is-active {
|
||||
background: var(--accent);
|
||||
opacity: 0.88;
|
||||
}
|
||||
|
||||
.mock__main {
|
||||
padding: 16px 22px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.mock__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding-block-end: 8px;
|
||||
border-block-end: var(--rule-hair) solid var(--rule);
|
||||
}
|
||||
.mock__breadcrumb {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.75rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
.mock__breadcrumb b { color: var(--ink); font-weight: 500; }
|
||||
.mock__pill {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.65rem;
|
||||
background: var(--accent);
|
||||
color: var(--paper);
|
||||
border-radius: 999px;
|
||||
padding: 2px 10px;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.mock__title {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.78rem;
|
||||
color: var(--ink);
|
||||
font-weight: 500;
|
||||
}
|
||||
.mock__title .meta { color: var(--muted); font-weight: 400; }
|
||||
|
||||
/* The flame chart trace waterfall — pure CSS bars on a time grid */
|
||||
.trace {
|
||||
display: grid;
|
||||
grid-template-columns: 130px 1fr;
|
||||
gap: 0 12px;
|
||||
padding-block-start: 6px;
|
||||
}
|
||||
.trace__row {
|
||||
display: contents;
|
||||
}
|
||||
.trace__label {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.68rem;
|
||||
color: var(--muted);
|
||||
padding-block: 5px 0;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.trace__label b { color: var(--ink); font-weight: 500; }
|
||||
.trace__lane {
|
||||
position: relative;
|
||||
height: 18px;
|
||||
background: linear-gradient(90deg, transparent calc(100% - 1px), var(--rule) calc(100% - 1px));
|
||||
background-size: 25% 100%;
|
||||
}
|
||||
.trace__bar {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
height: 10px;
|
||||
border-radius: 2px;
|
||||
background: var(--span-a);
|
||||
}
|
||||
.trace__bar.b { background: var(--span-b); }
|
||||
.trace__bar.c { background: var(--span-c); }
|
||||
.trace__bar.d { background: var(--span-d); }
|
||||
|
||||
/* ─── BENTO ─── */
|
||||
.bento {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
grid-auto-rows: minmax(180px, auto);
|
||||
gap: var(--space-md);
|
||||
max-width: 76rem;
|
||||
margin: var(--space-3xl) auto 0;
|
||||
padding-inline: clamp(1.5rem, 5vw, 3rem);
|
||||
}
|
||||
|
||||
.tile {
|
||||
background: var(--paper-2);
|
||||
border: var(--rule-hair) solid var(--rule);
|
||||
border-radius: var(--radius);
|
||||
padding: var(--space-lg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-sm);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tile h3 {
|
||||
font-family: var(--display);
|
||||
font-weight: 600;
|
||||
font-size: 1.0625rem;
|
||||
color: var(--ink);
|
||||
letter-spacing: -0.012em;
|
||||
}
|
||||
.tile p {
|
||||
color: var(--ink-2);
|
||||
font-size: 0.9375rem;
|
||||
flex: 1;
|
||||
}
|
||||
.tile .label {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.68rem;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.tile-stat { grid-column: span 2; grid-row: span 2; background: color-mix(in oklch, var(--paper-2) 100%, var(--accent) 7%); }
|
||||
.tile-spark { grid-column: span 2; }
|
||||
.tile-quote { grid-column: span 2; grid-row: span 2; background: var(--paper); }
|
||||
.tile-code { grid-column: span 2; grid-row: span 1; }
|
||||
.tile-integrations{ grid-column: span 4; }
|
||||
|
||||
@media (max-width: 60rem) {
|
||||
.bento { grid-template-columns: 1fr 1fr; }
|
||||
.tile-stat { grid-column: span 2; grid-row: span 1; }
|
||||
.tile-spark { grid-column: span 1; }
|
||||
.tile-quote { grid-column: span 2; grid-row: span 1; }
|
||||
.tile-code { grid-column: span 1; }
|
||||
.tile-integrations{ grid-column: span 2; }
|
||||
}
|
||||
|
||||
@media (max-width: 40rem) {
|
||||
.bento { grid-template-columns: 1fr; }
|
||||
.bento .tile { grid-column: span 1 !important; }
|
||||
}
|
||||
|
||||
/* Stat tile */
|
||||
.stat-num {
|
||||
font-family: var(--display);
|
||||
font-weight: 600;
|
||||
font-size: clamp(3rem, 6vw, 5rem);
|
||||
letter-spacing: -0.04em;
|
||||
line-height: 0.92;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: var(--ink);
|
||||
margin-block-start: var(--space-xs);
|
||||
}
|
||||
.stat-num .accent { color: var(--accent); }
|
||||
|
||||
/* Spark tile — small CSS sparkline */
|
||||
.spark {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: end;
|
||||
gap: 4px;
|
||||
padding-block-start: var(--space-md);
|
||||
}
|
||||
.spark span {
|
||||
flex: 1;
|
||||
background: var(--accent);
|
||||
opacity: 0.55;
|
||||
border-radius: 2px 2px 0 0;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Quote tile */
|
||||
.quote {
|
||||
font-family: var(--display);
|
||||
font-style: italic;
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.4;
|
||||
color: var(--ink);
|
||||
letter-spacing: -0.012em;
|
||||
}
|
||||
.quote::before { content: "“"; color: var(--muted); }
|
||||
.quote::after { content: "”"; color: var(--muted); }
|
||||
.attribution {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.68rem;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
margin-block-start: var(--space-md);
|
||||
}
|
||||
|
||||
/* Code tile */
|
||||
.code-mini {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.78rem;
|
||||
background: var(--ink);
|
||||
color: var(--paper);
|
||||
border-radius: 8px;
|
||||
padding: var(--space-md);
|
||||
line-height: 1.6;
|
||||
overflow-x: auto;
|
||||
flex: 1;
|
||||
}
|
||||
.code-mini .k { color: var(--accent-soft); }
|
||||
.code-mini .s { color: oklch(80% 0.10 130); }
|
||||
|
||||
/* Integrations table */
|
||||
.integrations {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: var(--space-sm);
|
||||
margin-block-start: var(--space-md);
|
||||
font-family: var(--mono);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
.integrations span {
|
||||
padding: 0.5rem 0.6rem;
|
||||
background: var(--paper);
|
||||
border: var(--rule-hair) solid var(--rule);
|
||||
border-radius: 6px;
|
||||
color: var(--ink-2);
|
||||
}
|
||||
|
||||
/* ─── FOOTER — index columns ─── */
|
||||
footer {
|
||||
margin-block-start: var(--space-3xl);
|
||||
padding: var(--space-2xl) clamp(1.5rem, 5vw, 3rem);
|
||||
border-block-start: var(--rule-hair) solid var(--rule);
|
||||
display: grid;
|
||||
grid-template-columns: 2fr repeat(3, 1fr);
|
||||
gap: var(--space-xl);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
@media (max-width: 56rem) {
|
||||
footer { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
|
||||
footer .col h4 {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
font-weight: 400;
|
||||
margin-block-end: var(--space-sm);
|
||||
}
|
||||
|
||||
footer .col ul { list-style: none; display: grid; gap: 0.4em; }
|
||||
footer .col a { color: var(--ink-2); text-decoration: none; }
|
||||
footer .col a:hover { color: var(--accent); }
|
||||
|
||||
footer .brand h4 {
|
||||
font-family: var(--display);
|
||||
font-weight: 600;
|
||||
font-size: 1.0625rem;
|
||||
letter-spacing: -0.01em;
|
||||
text-transform: none;
|
||||
color: var(--ink);
|
||||
}
|
||||
footer .brand p { color: var(--muted); max-width: 36ch; }
|
||||
footer .brand .colophon {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.7rem;
|
||||
color: var(--muted);
|
||||
letter-spacing: 0.06em;
|
||||
margin-block-start: var(--space-md);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
# Test 06 — Anya (software architect personal site)
|
||||
|
||||
## The prompt
|
||||
|
||||
> "I'm Anya, a software architect. Build me a one-pager. Don't ask me questions, just figure it out."
|
||||
|
||||
## Context gate — skipped (explicit)
|
||||
|
||||
The user explicitly opted out. Skill must state inferences in one sentence at the top of the reply.
|
||||
|
||||
**Going with:** *audience = engineering leaders considering Anya for hire / consult · use = read about Anya, then contact · tone = technical-but-warm, restrained, prose-led.* If any of those is wrong, redirect.
|
||||
|
||||
## Domain inference
|
||||
|
||||
`personal / resume / one-pager / individual` → from the domain table: **Long Document / Letter / Index-First**.
|
||||
|
||||
The skill picks **Long Document**. Letter was the runner-up and would have produced a warmer page; Long Document is the more credible shape for a senior IC at this career stage.
|
||||
|
||||
## Theme
|
||||
|
||||
The default attractor for "personal portfolio" is Specimen, and that's exactly the trap. The skill picks **Studio** — italic Fraunces display, forest-green accent, Geist body. Studio is the modern editorial agency aesthetic, and a senior software architect's one-pager benefits from looking like a small design studio's about page rather than a CV.
|
||||
|
||||
## Enrichment
|
||||
|
||||
**None.** Typography only. The decision is deliberate — a personal site that hand-builds a Lottie or commissions a Midjourney portrait of itself reads as overcompensating. Restraint is the credibility signal here.
|
||||
|
||||
The slop test catches: no abstract gradient background ("decoration without purpose"), no Three.js spinning name ("3D for a still object"), no demo video ("there's nothing to demo"), no aurora-blob ("never").
|
||||
|
||||
## Macrostructure stamp
|
||||
|
||||
```
|
||||
/* Hallmark · macrostructure: Long Document · H5 hero knobs: salutation=greeting, body=2 paragraphs, signoff=initials
|
||||
* theme: Studio · accent: forest-green ~2% (a single mark)
|
||||
* enrichment: none (typography only — restraint is the credibility signal)
|
||||
* studied: no · context: skipped, inferred (personal / portfolio domain)
|
||||
*/
|
||||
```
|
||||
|
||||
## What the page does
|
||||
|
||||
- Italic display salutation: "Hello, I'm Anya."
|
||||
- Two short paragraphs, italic display + roman body, on what Anya does and how she works
|
||||
- A tabular "selected work" sheet: four projects, each one a single line — year · client · what changed
|
||||
- A "currently" line in italic noting present focus
|
||||
- A small "elsewhere" line: GitHub · LinkedIn · email
|
||||
- Footer: a single dense colophon line
|
||||
|
||||
The page is one fold tall on a 1440 px display. That's the point.
|
||||
@@ -0,0 +1,75 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Anya Petrov — software architect</title>
|
||||
<meta name="description" content="Anya Petrov, software architect. I help engineering organisations ship hard distributed-systems work without breaking the team." />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="top">
|
||||
<span class="name">Anya Petrov</span>
|
||||
<span>Software architect · Lisbon & remote · 2026</span>
|
||||
</header>
|
||||
|
||||
<main class="fold">
|
||||
|
||||
<h1 class="salutation">Hello, I'm <strong>Anya.</strong></h1>
|
||||
|
||||
<div class="letter">
|
||||
<p>I help engineering organisations ship hard distributed-systems work — multi-region rollouts, payment graphs, eventually-consistent ledgers — without breaking the people doing the shipping. I write a lot, draw a lot of diagrams, and try not to ship a system the on-call rotation can't run on a Saturday.</p>
|
||||
<p>Twelve years across <em>Stripe, Monzo, and a small infra startup that didn't survive 2023.</em> Currently consulting; selectively taking on three to four engagements per year.</p>
|
||||
</div>
|
||||
|
||||
<section class="work">
|
||||
<h2>Selected work</h2>
|
||||
|
||||
<div class="work__row">
|
||||
<span class="work__year">2024–25</span>
|
||||
<span class="work__what">Designed and led a 14-month payments graph rewrite.<em>From a single Postgres write path to a sharded, replay-safe ledger across three regions.</em></span>
|
||||
<span class="work__where">Fintech, 400 engineers</span>
|
||||
</div>
|
||||
|
||||
<div class="work__row">
|
||||
<span class="work__year">2023</span>
|
||||
<span class="work__what">Set up the platform team's first SLO programme.<em>Wrote the runbooks, ran the postmortems, retired the dashboard everyone hated.</em></span>
|
||||
<span class="work__where">Marketplace, 80 engineers</span>
|
||||
</div>
|
||||
|
||||
<div class="work__row">
|
||||
<span class="work__year">2022</span>
|
||||
<span class="work__what">Migrated a 200-service monorepo from one CI to another.<em>Without breaking a single deploy. The PR had 1,800 reviewers.</em></span>
|
||||
<span class="work__where">Infra startup, ~50 engineers</span>
|
||||
</div>
|
||||
|
||||
<div class="work__row">
|
||||
<span class="work__year">2018–21</span>
|
||||
<span class="work__what">Built and ran the on-call programme at a B2B fintech, 0→200 engineers.<em>Wrote the doc that's still pinned in their #incident channel.</em></span>
|
||||
<span class="work__where">Fintech, 0→200 engineers</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<p class="currently"><strong>Currently:</strong> finishing a long engagement on a multi-region ledger; writing a short book on running on-call as a team practice rather than a heroism contest.</p>
|
||||
|
||||
<p class="elsewhere">
|
||||
<a href="#">github.com/anyap</a>
|
||||
<span class="sep">·</span>
|
||||
<a href="#">linkedin.com/in/anyapetrov</a>
|
||||
<span class="sep">·</span>
|
||||
<a href="mailto:hello@anyapetrov.com">hello@anyapetrov.com</a>
|
||||
</p>
|
||||
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<span class="ink">Anya Petrov</span>
|
||||
<span class="sep">·</span>
|
||||
<span>set in Fraunces & Geist</span>
|
||||
<span class="sep">·</span>
|
||||
<span>this page is one HTML file</span>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,235 @@
|
||||
/* Hallmark · macrostructure: Long Document · H5 hero knobs: salutation=greeting, body=2 paragraphs, signoff=initials
|
||||
* theme: Studio · accent: forest-green ~2% (a single mark)
|
||||
* enrichment: none (typography only — restraint is the credibility signal)
|
||||
* studied: no · context: skipped, inferred (personal / portfolio domain)
|
||||
*/
|
||||
|
||||
@import url("https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,400;0,9..144,500;1,9..144,400;1,9..144,500&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500&display=swap");
|
||||
|
||||
:root {
|
||||
--paper: oklch(96.5% 0.014 95);
|
||||
--paper-2: oklch(94% 0.016 95);
|
||||
--rule: oklch(82% 0.012 90);
|
||||
--rule-2: oklch(70% 0.010 90);
|
||||
--muted: oklch(48% 0.010 95);
|
||||
--neutral: oklch(34% 0.014 100);
|
||||
--ink-2: oklch(22% 0.018 100);
|
||||
--ink: oklch(13% 0.020 100);
|
||||
--accent: oklch(46% 0.140 145);
|
||||
|
||||
--serif: "Fraunces", "Tiempos", ui-serif, Georgia, serif;
|
||||
--body: "Geist", "Söhne", system-ui, sans-serif;
|
||||
--mono: "Geist Mono", ui-monospace, monospace;
|
||||
|
||||
--space-xs: 0.5rem;
|
||||
--space-sm: 0.75rem;
|
||||
--space-md: 1rem;
|
||||
--space-lg: 1.5rem;
|
||||
--space-xl: 2.5rem;
|
||||
--space-2xl: 4rem;
|
||||
--space-3xl: 6.5rem;
|
||||
|
||||
--rule-hair: 0.5px;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
html {
|
||||
font-family: var(--body);
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
color: var(--ink-2);
|
||||
background: var(--paper);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100dvh;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
padding-inline: clamp(1.5rem, 7vw, 5rem);
|
||||
}
|
||||
|
||||
/* ─── Top bar ─── */
|
||||
.top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
padding-block: var(--space-lg);
|
||||
border-block-end: var(--rule-hair) solid var(--rule);
|
||||
font-family: var(--mono);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.top .name { color: var(--ink); }
|
||||
.top .name::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: var(--accent);
|
||||
margin-inline-end: 0.6em;
|
||||
vertical-align: 1px;
|
||||
}
|
||||
|
||||
/* ─── The letter ─── */
|
||||
.fold {
|
||||
max-width: 56rem;
|
||||
margin: 0 auto;
|
||||
padding-block: var(--space-3xl) var(--space-2xl);
|
||||
display: grid;
|
||||
gap: var(--space-2xl);
|
||||
}
|
||||
|
||||
.salutation {
|
||||
font-family: var(--serif);
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-size: clamp(2.5rem, 5vw + 1rem, 4.25rem);
|
||||
font-variation-settings: "opsz" 144, "SOFT" 30;
|
||||
line-height: 1.05;
|
||||
letter-spacing: -0.02em;
|
||||
color: var(--ink);
|
||||
max-width: 16ch;
|
||||
}
|
||||
|
||||
.salutation strong {
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
background: linear-gradient(180deg, transparent 78%, color-mix(in oklch, var(--accent) 30%, transparent) 78%);
|
||||
padding: 0 0.05em;
|
||||
}
|
||||
|
||||
.letter {
|
||||
display: grid;
|
||||
gap: var(--space-md);
|
||||
max-width: 38rem;
|
||||
}
|
||||
|
||||
.letter p {
|
||||
font-size: 1.0625rem;
|
||||
line-height: 1.65;
|
||||
color: var(--ink-2);
|
||||
}
|
||||
|
||||
.letter em {
|
||||
font-family: var(--serif);
|
||||
font-style: italic;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
/* ─── Selected work — tabular ─── */
|
||||
.work {
|
||||
display: grid;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.work h2 {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
font-weight: 400;
|
||||
margin-block-end: var(--space-md);
|
||||
}
|
||||
|
||||
.work__row {
|
||||
display: grid;
|
||||
grid-template-columns: 4rem 1fr 14rem;
|
||||
gap: var(--space-md);
|
||||
padding-block: var(--space-md);
|
||||
border-block-start: var(--rule-hair) solid var(--rule);
|
||||
align-items: baseline;
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
|
||||
.work__row:last-child { border-block-end: var(--rule-hair) solid var(--rule); }
|
||||
|
||||
.work__year {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.8125rem;
|
||||
color: var(--muted);
|
||||
letter-spacing: 0.04em;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.work__what {
|
||||
color: var(--ink);
|
||||
font-weight: 500;
|
||||
}
|
||||
.work__what em {
|
||||
font-family: var(--serif);
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
color: var(--neutral);
|
||||
display: block;
|
||||
font-size: 0.9375rem;
|
||||
margin-block-start: 0.15em;
|
||||
}
|
||||
|
||||
.work__where {
|
||||
font-family: var(--mono);
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--muted);
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@media (max-width: 44rem) {
|
||||
.work__row { grid-template-columns: 4rem 1fr; }
|
||||
.work__where { grid-column: 1 / -1; text-align: left; padding-inline-start: calc(4rem + var(--space-md)); margin-block-start: -0.4em; }
|
||||
}
|
||||
|
||||
/* ─── Currently + elsewhere ─── */
|
||||
.currently,
|
||||
.elsewhere {
|
||||
font-family: var(--serif);
|
||||
font-style: italic;
|
||||
font-size: 1.125rem;
|
||||
color: var(--ink-2);
|
||||
max-width: 38rem;
|
||||
}
|
||||
|
||||
.currently strong {
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.elsewhere {
|
||||
font-family: var(--body);
|
||||
font-style: normal;
|
||||
font-size: 0.9375rem;
|
||||
}
|
||||
|
||||
.elsewhere a {
|
||||
color: var(--ink);
|
||||
text-decoration: none;
|
||||
background-image: linear-gradient(var(--accent), var(--accent));
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 calc(100% - 1px);
|
||||
background-size: 100% 1px;
|
||||
padding-block-end: 0.1em;
|
||||
transition: background-size 200ms cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
.elsewhere a:hover { background-size: 100% 2px; }
|
||||
.elsewhere .sep { color: var(--rule-2); margin-inline: 0.6em; }
|
||||
|
||||
/* ─── Footer ─── */
|
||||
footer {
|
||||
padding-block: var(--space-lg);
|
||||
border-block-start: var(--rule-hair) solid var(--rule);
|
||||
font-family: var(--mono);
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--muted);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: var(--space-md);
|
||||
}
|
||||
footer .ink { color: var(--ink); }
|
||||
footer .sep { color: var(--rule-2); }
|
||||
@@ -0,0 +1,88 @@
|
||||
# Hallmark — generation tests
|
||||
|
||||
Six landing pages produced by exercising the Hallmark skill across contrasting briefs. The point: see what comes out *without* hand-holding the AI on theme, structure, or enrichment. Each test deliberately tests a different scenario.
|
||||
|
||||
| # | Brief | Context given | Theme requested | What the skill picked |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 01 | Indie podcast `Tide`, no detail | **Skipped** ("you pick") | none | Quote-Led + Atelier + no enrichment |
|
||||
| 02 | Open-source CLI `Streampipe` for stream parsing | Full (devs / install + docs / technical) | **Terminal** (requested) | Workbench + Terminal + Tier-A CSS-art terminal mockup |
|
||||
| 03 | Artisan bakery `Maple Street Bread` | Full (locals / browse + visit / warm hand-set) | none | Long Document + Linen + Tier-B hand-built SVG loaf |
|
||||
| 04 | Manifesto for an environmental studio `Meridian` | Partial ("declarative, no flashy stuff") | none (implied) | Manifesto + Manifesto + no enrichment |
|
||||
| 05 | SaaS observability tool `Tracejam` | Full (SREs / try-or-talk-to-sales / technical) | none | Bento Grid + Pastel + E1 clipped-edge dashboard mockup |
|
||||
| 06 | Personal one-pager for `Anya` (software architect) | **Skipped** ("don't ask, just figure it out") | none | Long Document + Studio + no enrichment |
|
||||
|
||||
Each folder contains:
|
||||
|
||||
- **`brief.md`** — the verbatim prompt, what the skill asked / inferred, and the macrostructure stamp the page ships with
|
||||
- **`index.html`** — the rendered page (self-contained, no shared assets)
|
||||
- **`style.css`** — the page's tokens + components, with the Hallmark stamp at the top
|
||||
|
||||
Open any one with `open site/_tests/<folder>/index.html` or browse via the local dev server at `http://localhost:8765/_tests/<folder>/`.
|
||||
|
||||
## What this exercise was for
|
||||
|
||||
Stress-testing the skill on briefs that range from "complete spec" to "you figure it out, I'm lazy". Specifically watching for:
|
||||
|
||||
1. Does the skill default to the same shape twice across the six tests? → No two should share a macrostructure or hero archetype.
|
||||
2. Does the skill enrich every page even when it shouldn't? → Tests 01, 04, 06 should be typography-only.
|
||||
3. When the user skips the design-context gate, does the skill state its inferences in one sentence at the top? → Yes, see brief.md headers.
|
||||
4. Does the skill bend toward "Built for the modern team" voice when copy is left to it? → Each brief has its own voice; no template-soup.
|
||||
|
||||
Findings and a priority list of skill improvements are at the bottom of this file, after the six test cases.
|
||||
|
||||
---
|
||||
|
||||
## Findings — what worked, what didn't, what to fix
|
||||
|
||||
### What worked
|
||||
|
||||
1. **Six different briefs produced six structurally different pages.** No two share a macrostructure, hero archetype, or footer voice. The page-shape variety the skill is built around held up under the test.
|
||||
2. **Three of six tests ship with no enrichment.** The skill correctly resisted the urge to add a Lottie / illustration / mockup to the podcast (typography-only is right for voice), the manifesto (a manifesto is not a marketing page), and the personal portfolio (restraint is the credibility signal). This is the test the *previous* anti-slop generation failed.
|
||||
3. **The two tests that reach for hand-built CSS art (Tracejam flame chart, bakery loaf) produced custom DOM — not Lottie.** The skill picked Tier-A or Tier-B from the enrichment hierarchy without asking, and the result feels designed, not pulled.
|
||||
4. **The skipped-context flow worked twice (01 podcast, 06 architect).** Both pages open with an inferred-context disclosure sentence at the top of `brief.md`, which is the documented pattern. The skill states what it inferred.
|
||||
5. **Type pairing held across all six.** No two tests share a font stack. Newsreader (1, 3), Geist Mono (2, 5), Inter Tight (4), Fraunces (6) each carry one page; pairing logic is theme-driven so this fell out naturally.
|
||||
6. **The clipped-edge mockup (test 5) was hand-built CSS, not a video file.** The skill resisted the temptation to ship "embed a Loom" — the trace waterfall is pure CSS bars on a percentage grid.
|
||||
|
||||
### Friction points I hit while generating
|
||||
|
||||
- **The "domain → trio" table covers most cases but not all.** The bakery is in the table; the manifesto is in the table; the personal portfolio is in the table. The SaaS tracing tool is *implicitly* in the table under "platform / tool / infra". The podcast is in the table. **But:** I had to combine "SaaS marketing" + "developer tool" mentally to land on Bento Grid for Tracejam — the skill could be more explicit about *which* of the three suggested macrostructures wins for a given sub-domain. (E.g., "platform/tool" → Workbench or Bento; pick Workbench when the brief says docs/walkthrough, pick Bento when the brief says try-or-talk-to-sales.)
|
||||
- **Theme picking is still by feel.** The skill has a strong rule that "the next theme can't be Specimen if the last one was Specimen" — but no rule like *"the next theme should be categorically different on at least one of: paper-band, display-style, accent-hue."* I avoided repeating themes here by deliberate intuition; a documented rule would help future runs.
|
||||
- **Copy generation bent toward "Built for the modern team" twice — and I had to course-correct.** Tracejam's first draft headline was *"Trace what matters."* — a near-template; I rewrote it to *"Distributed tracing that explains itself."* which is more specific. **The skill needs example copy fixtures per macrostructure** (Tier 3 in the existing roadmap) — without them, the model bends toward LLM-distribution copy.
|
||||
- **The `hero-enrichment.md` decision protocol is text-only — no concrete worked examples per domain.** I had to imagine what Tracejam's "Demo Video Clipped-Edge" looks like vs. "Mockup Split"; an in-file example for each enrichment archetype on a real page would speed this up.
|
||||
- **Within-archetype variation knobs work in theory but I leaned on the same defaults.** The two pages with footer-index (Tracejam, none of the others ended up there) and footer-inline (the podcast, the bakery, the architect) all converge on the same shape. A future run on the *same* footer archetype should pick *different* knobs — but the rule lives in the cookbook, not in muscle memory.
|
||||
- **Free fonts only got me so far.** The bakery wanted Tiempos Headline (paid, Klim); I substituted Newsreader. The Studio theme on Anya's portfolio wanted Tobias (paid); Fraunces is a strong understudy but not the same. The skill should explicitly call out free-vs-paid trade-offs with two-tier candidates per pairing.
|
||||
|
||||
### Improvement list — priority order
|
||||
|
||||
This is the punch list. Do these and the skill goes from "good" to "as good as a human-curated design.md gets in 2026".
|
||||
|
||||
#### Tier 1 — Real holes (next session)
|
||||
|
||||
1. **Per-archetype example copy fixtures.** For each of the 21 macrostructures, ship 2–3 example *opening-line copy fragments* in `references/macrostructures.md`. The model imitates them instead of bending to LLM distribution. Highest single payoff for output quality. Currently roadmap Tier 3.
|
||||
2. **Sub-domain refinement for the trio table.** Split "platform / tool / infra / dashboard / developer" into two rows: *docs/walkthrough* → Workbench / Long Doc; *try-or-talk-to-sales* → Bento / Stat-Led / Workbench. Same for "personal" (one-pager Letter vs. multi-page Long Doc).
|
||||
3. **A documented theme-diversification rule.** *"Across consecutive Hallmark outputs in this user's session/project, no two themes should share more than one of: paper-band, display-style, accent-hue."* Currently the rule is "don't repeat Specimen", which is tighter on one tail than it should be.
|
||||
4. **Free-vs-paid font trade-off in `typography.md`.** Each tone gets two pairings: "free baseline" and "paid upgrade if the user can pay". The bakery example would say: "Free: Newsreader + Geist + Geist Mono. Paid upgrade: Tiempos Headline + Söhne + Söhne Mono."
|
||||
5. **Worked examples in `hero-enrichment.md` per archetype.** A 5-line "what this looks like for X brief" under each E1–E8. Right now the descriptions are abstract; concrete worked examples accelerate generation.
|
||||
|
||||
#### Tier 2 — Quality polish
|
||||
|
||||
6. **A "different knobs" check in the slop test.** Question 34: *"If I used the same archetype as a previous Hallmark output (e.g., footer-inline twice), did I pick at least one different variation knob?"* Catches the template-soup-within-an-archetype risk this exercise surfaced.
|
||||
7. **More illustration recipes in `custom-craft.md`.** The bakery loaf was the only worked example. Add one for: a workflow diagram, a mascot, a small architectural diagram, a botanical accent. Each in 30–60 lines. The bakery loaf I generated above had room to be sharper — the recipes should show what *good* looks like at this scale.
|
||||
8. **A `references/copy.md` extension with non-LLM voice samples.** Three voices per tone (editorial / brutalist / soft / technical / luxury / playful / austere) with 5 example sentences each. Borrow from real sites (Klim, Linear, Apple, Tufte, Rauno, Frieze) — credit them, don't copy.
|
||||
9. **Mobile breakpoint discipline per archetype.** I caught this on test 5 (Tracejam): the clipped-edge mockup needed a 60rem media query to collapse to stacked. The cookbook entries should say *"on viewports below X, this archetype collapses to Y"* so it's automatic.
|
||||
10. **An in-skill check: did I write `aria-label` on every visual-only SVG?** I caught it manually for the bakery loaf and the trace waterfall, but a slop-test gate would make it automatic.
|
||||
|
||||
#### Tier 3 — Long bet
|
||||
|
||||
11. **Project-level memory beyond the CSS stamp.** Right now the skill reads the *latest* stamp in the project. A `.hallmark/log.json` per-project file (history of macrostructures + themes + briefs) would let the diversification rules see further back.
|
||||
12. **A "study + remix" combined verb.** `hallmark study + redesign` in one — extract DNA from a screenshot, then rebuild the user's content with that DNA. Today the user has to chain `study` then `redesign`; one verb would be tighter.
|
||||
13. **A real example gallery on the landing page.** These six tests should be linked from the main `site/index.html` as a "Generated by Hallmark" gallery, with one-line captions. Marketing the skill via the skill's outputs.
|
||||
14. **Auto-generated Open Graph cards per page.** Each test ships its own meta description, but the OG image is missing. A small Hallmark utility that generates an OG image per page from the macrostructure stamp + headline would close the loop.
|
||||
|
||||
### What this means for the skill
|
||||
|
||||
Hallmark is *good*. The structural variety position holds — six briefs, six different pages, no template repetition. The enrichment hierarchy works — three of six pages correctly choose to ship nothing visual, and the three that do enrich pick the right tier (custom-built, not Lottie).
|
||||
|
||||
The remaining gap is **copy voice** and **theme-pick discipline**. The slop test catches *visual* tells well; it doesn't yet catch *prose* tells (like "Built for the modern team" headlines). And the diversification rules are tight on one axis (no Specimen fall-through) but loose on the rest.
|
||||
|
||||
Tier 1 of this list is the next session's work.
|
||||
Reference in New Issue
Block a user