Skip to content

Commit d1abbfe

Browse files
Home page: series showcase with direction identity, honest review cards, live milestone sample
1 parent 35954c7 commit d1abbfe

7 files changed

Lines changed: 185 additions & 19 deletions

File tree

eleventy.config.mjs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,20 @@ export default function (eleventyConfig) {
135135
eleventyConfig.addPassthroughCopy({ [`.content/${r.slug}/assets`]: `${r.slug}/assets` });
136136
}
137137

138+
// One real milestone (heading + its proof block) lifted out of a rendered roadmap,
139+
// so the home page can *show* the format instead of describing it. Anchors are
140+
// repointed at the roadmap page so the sample stays clickable.
141+
eleventyConfig.addFilter('milestoneSample', (roadmap, msId) => {
142+
const { html } = renderRoadmap(roadmap);
143+
const id = String(msId).replace('.', '\\.');
144+
const re = new RegExp(
145+
`<h3 id="[^"]*"[^>]*data-ms="${id}">[\\s\\S]*?<\\/h3>\\s*<blockquote class="ps-criterion">[\\s\\S]*?<\\/blockquote>`
146+
);
147+
const m = html.match(re);
148+
if (!m) return '';
149+
return m[0].replace(/href="#([^"]*)"/g, `href="/${roadmap.slug}/#$1"`);
150+
});
151+
138152
eleventyConfig.addFilter('roadmapMarkdown', (_content, roadmap) => renderRoadmap(roadmap).html);
139153
eleventyConfig.addFilter('roadmapToc', (roadmap) => renderRoadmap(roadmap).toc);
140154
eleventyConfig.addFilter('roadmapStars', (roadmap) => renderRoadmap(roadmap).stars);

roadmaps.config.mjs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,14 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
1111
const contentRoot = join(__dirname, '.content');
1212

1313
export const roadmaps = [
14+
// status: 'live' → fetched, rendered, linked, gets a page
15+
// status: 'review' → shown on the home page as a card with an honest status badge.
16+
// Deliberately has NO `repo` field: these repositories are private
17+
// until an external practitioner review passes, so a link would
18+
// hand visitors a 404. Nothing to link, nothing to leak.
1419
{
1520
slug: 'ai-safety-engineer', // = repo name without the "-roadmap" suffix
21+
accent: 'blue',
1622
// Org path — valid after the Ф3 transfer of ai-safety-engineer-roadmap into the
1723
// proofstone org (transfer is step 1 of deploy). Local pre-transfer builds keep
1824
// working from the cached .content/. Old personal-account links stay redirected.
@@ -27,17 +33,25 @@ export const roadmaps = [
2733
},
2834
{
2935
slug: 'distributed-systems-engineer', // = repo name without the "-roadmap" suffix
36+
accent: 'teal',
3037
repo: 'proofstone/distributed-systems-engineer-roadmap',
3138
branch: 'main',
3239
title: 'Distributed Systems Engineer Roadmap',
3340
tagline: 'The map for the engineer moving into distributed systems — consensus, replication, failure detection — where every node is a test that passes or fails, not a keyword you nod at.',
3441
milestones: 29, // fallback only; real count computed from the README at build
3542
order: 2,
3643
status: 'live',
37-
star: true
44+
star: true,
45+
// Own reference implementation promised by §4 of this roadmap, shipped and public.
46+
lab: {
47+
name: 'swim-lab',
48+
url: 'https://github.com/proofstone/swim-lab',
49+
note: 'the §4.1 milestone made executable — you implement SWIM, a deterministic suite grades it'
50+
}
3851
},
3952
{
4053
slug: 'applied-cryptography',
54+
accent: 'violet',
4155
repo: 'proofstone/applied-cryptography-roadmap',
4256
branch: 'main',
4357
title: 'Applied Cryptography Roadmap',
@@ -46,10 +60,25 @@ export const roadmaps = [
4660
order: 3,
4761
status: 'live',
4862
star: true
63+
},
64+
{
65+
slug: 'robotics-software-engineer',
66+
accent: 'amber',
67+
title: 'Robotics Software Engineer Roadmap',
68+
tagline: 'You already ship software. Robotics does not need you to start over — it needs you to port what you know and respect what is genuinely different.',
69+
order: 4,
70+
status: 'review'
71+
},
72+
{
73+
slug: 'pcb-design',
74+
accent: 'green',
75+
title: 'PCB Design Roadmap',
76+
tagline: 'Embedded roadmaps teach you to write the firmware and say plainly that the board is not their topic. This is the map for the board.',
77+
order: 5,
78+
status: 'review',
79+
// Its own reference board is designed but not yet fabricated — mentioned, not linked.
80+
labNote: 'ships with a reference board of its own — designed, fabrication pending'
4981
}
50-
// Future directions stay OUT of v1 (decision C): the homepage says "a growing
51-
// series" in one line, no per-direction teaser cards. When #2 is close, add it
52-
// here with status:'teaser' (renders as a restrained card, no page) or 'live'.
5382
];
5483

5584
// Count milestones straight from the README ("### M<sec>.<n>" headings) so the

scripts/screenshots.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ const shots = [
1515
{ name: '01-home-light', url: '/', scheme: 'light', ...DESKTOP, vh: 900 },
1616
{ name: '02-home-dark', url: '/', scheme: 'dark', ...DESKTOP, vh: 900 },
1717
{ name: '03-home-mobile', url: '/', scheme: 'dark', ...MOBILE },
18+
{ name: '03a-home-full-dark', url: '/', scheme: 'dark', ...DESKTOP, full: true },
19+
{ name: '03b-home-full-light', url: '/', scheme: 'light', ...DESKTOP, full: true },
1820

1921
{ name: '04-aisafety-light-top', url: '/ai-safety-engineer/', scheme: 'light', ...DESKTOP },
2022
{ name: '05-aisafety-dark-top', url: '/ai-safety-engineer/', scheme: 'dark', ...DESKTOP },
@@ -38,7 +40,7 @@ for (const s of shots) {
3840
const page = await ctx.newPage();
3941
await page.goto(base + s.url, { waitUntil: 'networkidle' });
4042
await page.waitForTimeout(400);
41-
await page.screenshot({ path: `${out}/${s.name}.png` });
43+
await page.screenshot({ path: `${out}/${s.name}.png`, fullPage: !!s.full });
4244
await ctx.close();
4345
console.log('✓', s.name);
4446
}

src/_data/reviewRoadmaps.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { loadRoadmaps } from '../../roadmaps.config.mjs';
2+
3+
// Directions whose content is written but still private, pending an external
4+
// practitioner review. Shown on the home page as honest status cards — they have
5+
// no repo field, so there is nothing to link and nothing to leak.
6+
export default function () {
7+
return loadRoadmaps()
8+
.filter((r) => r.status === 'review')
9+
.sort((a, b) => a.order - b.order);
10+
}

src/_data/site.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ export default {
77
thesisSub: 'Measured in artifacts, not keywords.',
88
thesis:
99
"Every node is a milestone you can prove you passed — a merged eval, a confirmed break, a shipped guardrail — not a keyword you can nod at. When you finish a node, you have an artifact, not a feeling.",
10-
seriesLine: 'proofstone is a growing series of engineering roadmaps — AI Safety is the first.',
10+
// Kept factual: counts come from the registry, so this line cannot go stale silently.
11+
seriesLine: 'A growing series of engineering roadmaps for people who already ship software.',
1112
org: 'https://github.com/proofstone',
1213
noindex: process.env.SITE_NOINDEX !== 'false',
1314
buildYear: 2026

src/assets/styles.css

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,17 +123,81 @@ main { padding: 2.5rem 1.25rem 4rem; }
123123
border: 1px dashed var(--border); border-radius: 999px; padding: .35rem .85rem;
124124
}
125125

126+
/* Per-direction identity. One restrained accent each — enough to tell the
127+
directions apart at a glance without turning the page into a landing page. */
128+
[data-accent="blue"] { --card-accent: #2457d6; }
129+
[data-accent="teal"] { --card-accent: #0f766e; }
130+
[data-accent="violet"] { --card-accent: #6d28d9; }
131+
[data-accent="amber"] { --card-accent: #a16207; }
132+
[data-accent="green"] { --card-accent: #15803d; }
133+
[data-theme="dark"] [data-accent="blue"] { --card-accent: #6f9bff; }
134+
[data-theme="dark"] [data-accent="teal"] { --card-accent: #2dd4bf; }
135+
[data-theme="dark"] [data-accent="violet"] { --card-accent: #a78bfa; }
136+
[data-theme="dark"] [data-accent="amber"] { --card-accent: #e0b64d; }
137+
[data-theme="dark"] [data-accent="green"] { --card-accent: #4ade80; }
138+
@media (prefers-color-scheme: dark) {
139+
:root:not([data-theme="light"]) [data-accent="blue"] { --card-accent: #6f9bff; }
140+
:root:not([data-theme="light"]) [data-accent="teal"] { --card-accent: #2dd4bf; }
141+
:root:not([data-theme="light"]) [data-accent="violet"] { --card-accent: #a78bfa; }
142+
:root:not([data-theme="light"]) [data-accent="amber"] { --card-accent: #e0b64d; }
143+
:root:not([data-theme="light"]) [data-accent="green"] { --card-accent: #4ade80; }
144+
}
145+
126146
.roadmaps { max-width: var(--page); margin: 0 auto; display: grid; gap: 1rem; }
127147
.card {
128-
display: block; background: var(--surface); border: 1px solid var(--border);
129-
border-radius: 14px; padding: 1.4rem 1.5rem; box-shadow: var(--shadow);
148+
position: relative; overflow: hidden;
149+
background: var(--surface); border: 1px solid var(--border);
150+
border-left: 4px solid var(--card-accent, var(--accent));
151+
border-radius: 12px; box-shadow: var(--shadow);
130152
transition: transform .12s ease, border-color .12s ease;
131153
}
132-
.card:hover { text-decoration: none; transform: translateY(-2px); border-color: var(--accent); }
154+
.card:hover { transform: translateY(-2px); }
155+
.card:hover { border-color: var(--card-accent, var(--accent)); }
156+
.card__main { display: block; padding: 1.4rem 1.5rem; color: inherit; }
157+
.card__main:hover { text-decoration: none; }
158+
.card--review { padding: 1.4rem 1.5rem; background: transparent; border-style: dashed; }
159+
.card--review:hover { transform: none; border-color: var(--border); }
133160
.card__title { margin: 0 0 .35rem; font-size: 1.4rem; letter-spacing: -.01em; color: var(--text); }
134161
.card__tagline { margin: 0 0 1rem; color: var(--muted); }
135162
.card__meta { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin: 0; }
136-
.card__go { margin-left: auto; color: var(--accent); font-size: 1.3rem; }
163+
.card__go { margin-left: auto; color: var(--card-accent, var(--accent)); font-size: 1.3rem; }
164+
.badge--review {
165+
color: var(--muted); background: transparent; border-style: dashed; white-space: normal;
166+
}
167+
.card__lab {
168+
margin: 0; padding: .75rem 1.5rem 1.1rem; font-size: .87rem; color: var(--muted);
169+
border-top: 1px dashed var(--border);
170+
}
171+
.card--review .card__lab { padding: .75rem 0 0; }
172+
.card__lab-tag {
173+
font-family: var(--mono); font-size: .62rem; text-transform: uppercase; letter-spacing: .08em;
174+
color: var(--card-accent, var(--accent)); border: 1px solid color-mix(in srgb, var(--card-accent, var(--accent)) 40%, var(--border));
175+
border-radius: 4px; padding: .1rem .35rem; margin-right: .45rem;
176+
}
177+
.card__lab code { background: var(--code-bg); padding: .08em .35em; border-radius: 4px; font-size: .95em; }
178+
179+
/* ── Live milestone sample ────────────────────────────────────────────────── */
180+
.sample { max-width: var(--measure); margin: 3.5rem auto 0; }
181+
.sample__title { font-size: 1.3rem; letter-spacing: -.01em; margin: 0 0 .3rem; }
182+
.sample__lead { color: var(--muted); margin: 0 0 1.2rem; font-size: .95rem; }
183+
.sample__block { margin: 0; }
184+
.sample__block h3 { margin-top: 0; }
185+
186+
/* ── How it works ─────────────────────────────────────────────────────────── */
187+
.how { max-width: var(--measure); margin: 3.5rem auto 0; }
188+
.how h2 { font-size: 1.3rem; letter-spacing: -.01em; }
189+
.how__steps { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: .85rem; }
190+
.how__steps li {
191+
display: grid; grid-template-columns: 1.6rem 1fr; gap: .7rem; align-items: baseline;
192+
color: var(--muted); line-height: 1.6;
193+
}
194+
.how__steps strong { color: var(--text); }
195+
.how__num {
196+
font-family: var(--mono); font-size: .75rem; color: var(--accent);
197+
border: 1px solid var(--border); border-radius: 5px; text-align: center; padding: .1rem 0;
198+
}
199+
200+
.hero__counts { display: block; margin-top: .3rem; font-family: var(--mono); font-size: .74rem; color: var(--muted); }
137201

138202
.thesis { max-width: var(--measure); margin: 3rem auto 0; }
139203
.thesis h2 { font-size: 1.3rem; letter-spacing: -.01em; }
@@ -148,7 +212,12 @@ main { padding: 2.5rem 1.25rem 4rem; }
148212
color: var(--muted); background: var(--bg);
149213
}
150214
.badge--count { color: var(--muted); }
151-
.badge--star { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); background: var(--accent-weak); }
215+
/* Falls back to the site accent; on a direction card it inherits that card's accent. */
216+
.badge--star {
217+
color: var(--card-accent, var(--accent));
218+
border-color: color-mix(in srgb, var(--card-accent, var(--accent)) 40%, var(--border));
219+
background: transparent;
220+
}
152221
.badge--flag { color: var(--gold); border-color: color-mix(in srgb, var(--gold) 45%, var(--border)); background: var(--gold-weak); }
153222

154223
/* ── Roadmap page: top bar ────────────────────────────────────────────────── */

src/index.njk

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,62 @@ description: Engineering roadmaps where every milestone is a proof — a verifia
77
<section class="hero">
88
<h1 class="hero__title">{{ site.tagline }}</h1>
99
<p class="hero__sub">{{ site.thesisSub }}</p>
10-
<p class="hero__series">{{ site.seriesLine }}</p>
10+
<p class="hero__series">
11+
{{ site.seriesLine }}
12+
<span class="hero__counts">{{ liveRoadmaps.length }} live · {{ reviewRoadmaps.length }} in review</span>
13+
</p>
1114
</section>
1215

1316
<section class="roadmaps" aria-label="Roadmaps">
14-
{% for r in liveRoadmaps %}
15-
<a class="card" href="/{{ r.slug }}/">
17+
{%- for r in liveRoadmaps %}
18+
<article class="card" data-accent="{{ r.accent }}">
19+
<a class="card__main" href="/{{ r.slug }}/">
20+
<h2 class="card__title">{{ r.title }}</h2>
21+
<p class="card__tagline">{{ r.tagline }}</p>
22+
<p class="card__meta">
23+
<span class="badge badge--count">{{ r.milestones }} milestones</span>
24+
<span class="badge badge--star">{{ r | roadmapStars }} ★ artifacts</span>
25+
<span class="card__go" aria-hidden="true">→</span>
26+
</p>
27+
</a>
28+
{%- if r.lab %}
29+
<p class="card__lab">
30+
<span class="card__lab-tag">lab</span>
31+
<a href="{{ r.lab.url }}" rel="noopener"><code>{{ r.lab.name }}</code></a> — {{ r.lab.note }}.
32+
</p>
33+
{%- endif %}
34+
</article>
35+
{%- endfor %}
36+
37+
{%- for r in reviewRoadmaps %}
38+
<article class="card card--review" data-accent="{{ r.accent }}">
1639
<h2 class="card__title">{{ r.title }}</h2>
1740
<p class="card__tagline">{{ r.tagline }}</p>
1841
<p class="card__meta">
19-
<span class="badge badge--count">{{ r.milestones }} milestones</span>
20-
{% if r.star %}<span class="badge badge--star">★ on GitHub</span>{% endif %}
21-
<span class="card__go" aria-hidden="true">→</span>
42+
<span class="badge badge--review">In practitioner review — private until published</span>
2243
</p>
23-
</a>
24-
{% endfor %}
44+
{%- if r.labNote %}
45+
<p class="card__lab"><span class="card__lab-tag">lab</span> {{ r.labNote }}.</p>
46+
{%- endif %}
47+
</article>
48+
{%- endfor %}
49+
</section>
50+
51+
<section class="sample" aria-label="What a milestone looks like">
52+
<h2 class="sample__title">What a milestone looks like</h2>
53+
<p class="sample__lead">Every node in every roadmap is one of these — taken here, unedited, from the AI Safety map.</p>
54+
<div class="prose sample__block">
55+
{%- for r in liveRoadmaps %}{% if r.slug == 'ai-safety-engineer' %}{{ r | milestoneSample('M2.1') | safe }}{% endif %}{% endfor %}
56+
</div>
57+
</section>
58+
59+
<section class="how">
60+
<h2>How it works</h2>
61+
<ol class="how__steps">
62+
<li><span class="how__num">1</span><span class="how__body"><strong>You're done when …</strong> Each node states one finishing condition, in advance.</span></li>
63+
<li><span class="how__num">2</span><span class="how__body"><strong>An artifact.</strong> Meeting it leaves something outside your head — a merged eval, a green test suite, a fabricated board.</span></li>
64+
<li><span class="how__num">3</span><span class="how__body"><strong>Proof.</strong> The artifact is the evidence. Nothing here is finished by reading about it.</span></li>
65+
</ol>
2566
</section>
2667

2768
<section class="thesis">

0 commit comments

Comments
 (0)