Phase 5: every page of the site, the resume PDF and RSS

Landing, About, Now, Resume (with a generated PDF), Work and case studies,
Inquiries and the sent page, Library and the Shelf, Workshop and craft pages,
Tabletop, Blog with topics and posts, RSS, the Store, product lines, the
order form and the 404, all rendered from the content service's notes.

Schema: experience previousRoles, skills stack core items, page stats,
make tags and deck favorite. Icon names from notes map to brand icons in one
place, and HTML comments are dropped when Markdown renders.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx
This commit is contained in:
Claude
2026-10-11 21:33:49 +00:00
parent 7025e95f89
commit 99ecdc2113
83 changed files with 11681 additions and 14 deletions
+162
View File
@@ -0,0 +1,162 @@
---
/**
* Off the star chart. Renders even when the content service is down: the
* only data it reads (an optional Page text note for /404) is wrapped.
*/
import Button from "@gk/ui/components/Button.astro";
import PageShell from "@gk/ui/components/PageShell.astro";
import Seal from "@gk/ui/components/Seal.astro";
import Star from "@gk/ui/components/Star.astro";
import StarPath from "@gk/ui/components/StarPath.astro";
import TickScale from "@gk/ui/components/TickScale.astro";
import { site } from "../lib/page.ts";
Astro.response.status = 404;
let lede = "This page isn't here. It may have moved when the site was rebuilt.";
let timer: ReturnType<typeof setTimeout> | undefined;
try {
const s = await Promise.race([site(), new Promise<never>((_, reject) => (timer = setTimeout(() => reject(new Error("timeout")), 2000)))]);
lede = s.pageText("/404")?.data.lede ?? lede;
} catch {
// The content service is unreachable; the page needs nothing from it.
} finally {
clearTimeout(timer);
}
const groups = [
{ id: "nf-work", label: "Work with me", links: [["Resume", "/resume"], ["Work", "/work"], ["Inquiries", "/inquiries"]] },
{ id: "nf-world", label: "My world", links: [["Library", "/library"], ["Workshop", "/workshop"], ["Tabletop", "/tabletop"], ["Now", "/now"]] },
{ id: "nf-also", label: "Also", links: [["Blog", "/blog"], ["Store", "/store"], ["About", "/about"]] },
];
---
<PageShell title="Off the star chart" family="notFound" description="This page isn't here.">
<meta slot="head" name="robots" content="noindex" />
<section class="lost" aria-labelledby="nf-title">
<Seal name="zodiac" size={180} class="seal" />
<span class="eyebrow">404 · Page not found</span>
<h1 id="nf-title">Off the star chart</h1>
<p class="lede">{lede}</p>
<div class="actions">
<Button href="/">Back home</Button>
<Button href="/blog" variant="secondary">Blog</Button>
</div>
</section>
<div class="path" aria-hidden="true"><StarPath seed="not-found" strength="calm" centerStar={false} /></div>
<nav class="groups" aria-label="Elsewhere on the site">
{
groups.map((g) => (
<section class="group" aria-labelledby={g.id}>
<h2 id={g.id} class="kicker">
<Star size={11} />
{g.label}
</h2>
<TickScale width={150} height={10} count={30} class="ticks" />
<ul>
{g.links.map(([label, href]) => (
<li>
<a href={href}>{label}</a>
</li>
))}
</ul>
</section>
))
}
</nav>
</PageShell>
<style>
.lost {
max-width: 680px;
margin: 0 auto;
padding: 40px 0 24px;
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
text-align: center;
}
.lost :global(.seal) {
opacity: 0.85;
filter: drop-shadow(0 0 22px rgba(43, 194, 183, 0.35));
}
.eyebrow {
font-size: 11px;
letter-spacing: 0.3em;
font-weight: 600;
color: var(--gk-gold-bright);
text-transform: uppercase;
}
h1 {
margin: 0;
font-size: clamp(32px, 6vw, 46px);
letter-spacing: 0.06em;
}
.lede {
margin: 0;
font-size: 17px;
line-height: 1.6;
color: var(--gk-soft);
}
.actions {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 12px;
margin-top: 6px;
}
.path {
max-width: 680px;
margin: 0 auto;
padding: 16px 0;
}
.groups {
max-width: 680px;
margin: 0 auto;
padding: 8px 0 32px;
display: flex;
flex-wrap: wrap;
gap: 28px;
}
.group {
flex: 1 1 180px;
display: flex;
flex-direction: column;
gap: 8px;
}
.kicker {
margin: 0;
display: flex;
align-items: center;
gap: 8px;
font-family: var(--gk-font-body);
font-size: 11px;
letter-spacing: 0.3em;
font-weight: 600;
color: var(--gk-gold-bright);
text-transform: uppercase;
}
.group :global(.ticks) {
margin: -4px 0 2px;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
li a {
display: block;
padding: 6px 0;
border-top: 1px solid var(--gk-lines);
font-size: 15px;
font-weight: 600;
color: var(--gk-text);
text-decoration: none;
transition: color var(--gk-ease);
}
li a:hover {
color: var(--gk-teal-bright);
}
</style>