Refine Swiss + Editorial themes; add optional date field

Swiss: drop all-caps on titles, soften heavy ink borders to muted rules
or remove them where background contrast carries the form, balance hero
name weights (smaller lighter first name vs heavier italic last name),
fix unreadable white description text on portfolio hover.

Editorial: enlarge the section "No" mark and set it in italic Fraunces so
it reads as typography instead of a tick, swap the first name into DM
Serif Display for a different J, sharpen the section number caption.

Client tile: contain custom logos at 86% so wide marks like
"The Cooperative Way" stop getting cropped to "operative".

New: optional `date` field on every item type (link/card/portfolio/
youtube/client). Accepts YYYY, YYYY-MM, or YYYY-MM-DD. Rendered
human-friendly with the raw ISO preserved in <time datetime>.
Documented in README and demonstrated in links.example.json.
This commit is contained in:
Joel Brock
2026-05-15 16:33:53 -07:00
parent 36084013c8
commit 543d3bd8e1
6 changed files with 155 additions and 66 deletions

View File

@@ -114,6 +114,37 @@ has `type: "client"`.
Every item lives in a section's `items` array and has a `type` field. The
five available types are documented below.
#### Optional `date` (any item type)
Every item type supports an optional `date` string. Use it to surface
"published", "shipped", "released", or "worked on" timestamps without
changing the item's `type`.
Accepted formats (all ISO-style, all interpreted as plain strings — no
time zones, no parsing surprises):
| Input | Rendered as | Use for |
|----------------|------------------------|------------------------------------|
| `"2026"` | `2026` | Year-only — "this is from 2026" |
| `"2026-05"` | `May 2026` | Year + month — soft / recurring |
| `"2026-05-14"` | `May 14, 2026` | Full date — talks, launches, posts |
Where it appears per type:
- **`link`** — small monospaced caption under the host (separated by a `·`).
- **`card` (project)** — at the end of the tag row.
- **`portfolio`** — under the caption description.
- **`youtube`** — inline after the title overlay.
- **`client`** — rendered as a small caption under the client title.
The raw value is also written to the `<time datetime="…">` attribute so
the original ISO string is preserved for machines and screen readers,
even though humans see the friendly form.
```json
{ "type": "card", "title": "CoVote launched", "date": "2025-11-04" }
```
#### `type: "link"` — standard link card
```json

View File

@@ -125,6 +125,12 @@ body::before {
letter-spacing: -0.045em;
color: var(--ink);
}
.hero__name .hero__name-first {
font-family: "DM Serif Display", var(--display);
font-weight: 400;
font-variation-settings: normal;
letter-spacing: -0.03em;
}
.hero__name em {
font-style: italic;
font-variation-settings: "opsz" 144, "SOFT" 100, "WONK" 1;
@@ -191,11 +197,14 @@ body::before {
}
.section__numwrap { display: flex; flex-direction: column; gap: 0.2rem; align-self: end; }
.section__numwrap small {
font-family: var(--mono);
font-size: var(--fs-mini);
text-transform: uppercase;
letter-spacing: 0.18em;
font-family: var(--display);
font-size: var(--fs-md);
font-style: italic;
font-variation-settings: "opsz" 36, "SOFT" 100;
letter-spacing: 0;
color: var(--accent);
font-weight: 500;
line-height: 1;
}
.section__num {
font-family: var(--display);
@@ -299,7 +308,13 @@ body::before {
.card--link .favicon[data-fallback] { font-family: var(--display); font-weight: 500; font-size: 1.7rem; color: var(--ink); }
.card__title { font-weight: 540; letter-spacing: -0.005em; line-height: 1.25; }
.card__desc { color: var(--muted); font-size: var(--fs-sm); }
.card__host { font-family: var(--mono); font-size: var(--fs-mini); color: var(--muted); margin-top: 0.2rem; text-transform: lowercase; }
.card__host { font-family: var(--mono); font-size: var(--fs-mini); color: var(--muted); text-transform: lowercase; }
.card__meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.4rem 0.8rem; margin-top: 0.2rem; }
.card__date { font-family: var(--mono); font-size: var(--fs-mini); color: var(--muted); letter-spacing: 0.02em; text-transform: uppercase; }
.card__meta .card__date::before { content: "·"; margin-right: 0.5rem; color: var(--accent); }
.card__meta .card__date:only-child::before { content: none; }
.tags .card__date { padding: 3px 0; margin-left: auto; }
.portfolio__caption .card__date { margin-top: 0.25rem; }
/* project card */
.card--project { padding: 1.5rem; gap: 0.75rem; }
@@ -425,7 +440,7 @@ body::before {
box-shadow: 0 1px 0 rgba(20,18,34,0.04), 0 14px 28px -18px rgba(20,18,34,0.28);
}
.client__logo img.is-favicon { width: 60%; height: 60%; object-fit: contain; }
.client__logo img:not(.is-favicon) { width: 100%; height: 100%; object-fit: cover; }
.client__logo img:not(.is-favicon) { width: 86%; height: 86%; object-fit: contain; }
.client__logo[data-fallback] { font-family: var(--display); font-weight: 500; font-size: 2.2rem; color: var(--ink); }
.client__title {
font-size: var(--fs-sm);

View File

@@ -11,7 +11,7 @@
--ink: oklch(0.10 0 0);
--ink-2: oklch(0.18 0 0);
--muted: oklch(0.42 0 0);
--rule: oklch(0.10 0 0);
--rule: oklch(0.78 0 0);
--accent: #DC2127;
--on-accent: oklch(0.99 0 0);
--display: "Archivo", "Helvetica Neue", Helvetica, Arial, sans-serif;
@@ -27,7 +27,7 @@
--ink: oklch(0.985 0 0);
--ink-2: oklch(0.92 0 0);
--muted: oklch(0.62 0 0);
--rule: oklch(0.985 0 0);
--rule: oklch(0.32 0 0);
}
}
:root[data-template="swiss"][data-theme="dark"] {
@@ -36,7 +36,7 @@
--ink: oklch(0.985 0 0);
--ink-2: oklch(0.92 0 0);
--muted: oklch(0.62 0 0);
--rule: oklch(0.985 0 0);
--rule: oklch(0.32 0 0);
}
/* clean off the editorial paper grain */
@@ -44,7 +44,7 @@
/* ───── marker bar ───── */
:root[data-template="swiss"] .marker {
border-bottom: 2px solid var(--ink);
border-bottom: 1px solid var(--rule);
font-weight: 700;
letter-spacing: 0.06em;
color: var(--ink);
@@ -60,7 +60,7 @@
:root[data-template="swiss"] .marker__year { color: var(--accent); }
:root[data-template="swiss"] .theme {
border-radius: 0;
border-color: var(--ink);
border-color: var(--rule);
font-family: var(--display);
font-weight: 700;
text-transform: uppercase;
@@ -74,15 +74,22 @@
:root[data-template="swiss"] .hero__name {
font-family: var(--display);
font-style: normal;
font-weight: 900;
font-weight: 700;
font-variation-settings: normal;
text-transform: uppercase;
letter-spacing: -0.045em;
line-height: 0.84;
text-transform: none;
letter-spacing: -0.035em;
line-height: 0.86;
}
:root[data-template="swiss"] .hero__name .hero__name-first {
font-family: var(--display);
font-weight: 500;
font-size: 0.78em;
letter-spacing: -0.025em;
color: var(--ink);
}
:root[data-template="swiss"] .hero__name em {
font-style: normal;
font-weight: 900;
font-weight: 800;
font-variation-settings: normal;
color: var(--accent);
}
@@ -122,8 +129,8 @@
/* ───── social ───── */
:root[data-template="swiss"] .social a {
border-radius: 0;
border-color: var(--ink);
border-width: 1.5px;
border-color: var(--rule);
border-width: 1px;
font-family: var(--display);
font-weight: 700;
text-transform: uppercase;
@@ -141,13 +148,15 @@
/* ───── sections ───── */
:root[data-template="swiss"] .section__head {
border-bottom: 4px solid var(--ink);
border-bottom: 2px solid var(--ink);
padding-bottom: 0.85rem;
}
:root[data-template="swiss"] .section__numwrap small {
color: var(--ink);
font-weight: 700;
font-style: normal;
font-family: var(--display);
font-variation-settings: normal;
}
:root[data-template="swiss"] .section__num {
font-family: var(--display);
@@ -160,9 +169,9 @@
:root[data-template="swiss"] .section__title {
font-family: var(--display);
font-style: normal;
font-weight: 900;
font-weight: 800;
font-variation-settings: normal;
text-transform: uppercase;
text-transform: none;
letter-spacing: -0.025em;
color: var(--ink);
}
@@ -180,8 +189,8 @@
/* ───── cards ───── */
:root[data-template="swiss"] .card {
border-radius: 0;
border-color: var(--ink);
background: var(--paper);
border: 0;
background: var(--paper-2);
transition: background 200ms cubic-bezier(0.22, 1, 0.36, 1), color 200ms cubic-bezier(0.22, 1, 0.36, 1);
isolation: isolate;
}
@@ -190,7 +199,6 @@
box-shadow: none;
background: var(--ink);
color: var(--paper);
border-color: var(--ink);
}
:root[data-template="swiss"] .card:hover .card__title,
:root[data-template="swiss"] .card:hover .card__desc,
@@ -205,8 +213,8 @@
:root[data-template="swiss"] .card__title {
font-family: var(--display);
font-weight: 700;
text-transform: uppercase;
letter-spacing: -0.005em;
text-transform: none;
letter-spacing: -0.01em;
}
:root[data-template="swiss"] .card__desc {
font-family: var(--body);
@@ -218,16 +226,25 @@
font-weight: 600;
letter-spacing: 0.08em;
}
:root[data-template="swiss"] .card__date {
font-family: var(--display);
font-weight: 600;
color: var(--muted);
letter-spacing: 0.06em;
}
:root[data-template="swiss"] .card:hover .card__date,
:root[data-template="swiss"] .card:hover .card__host { color: var(--paper); }
:root[data-template="swiss"] .card__meta .card__date::before { color: var(--accent); }
:root[data-template="swiss"] .card:hover .card__meta .card__date::before { color: var(--paper); }
/* link card favicon */
:root[data-template="swiss"] .card--link .favicon {
border-radius: 0;
border-color: var(--ink);
border: 0;
background: var(--paper);
}
:root[data-template="swiss"] .card--link:hover .favicon {
background: var(--paper);
border-color: var(--paper);
}
:root[data-template="swiss"] .card--link .favicon[data-fallback] {
font-family: var(--display);
@@ -238,19 +255,20 @@
/* project card */
:root[data-template="swiss"] .card--project .card__title {
font-family: var(--display);
font-weight: 900;
font-weight: 800;
font-style: normal;
font-variation-settings: normal;
text-transform: uppercase;
text-transform: none;
font-size: var(--fs-lg);
line-height: 0.95;
line-height: 1.05;
letter-spacing: -0.02em;
}
/* featured — solid red, white text, true poster */
:root[data-template="swiss"] .card--featured {
background: var(--accent);
color: var(--on-accent);
border-color: var(--ink);
border: 0;
}
:root[data-template="swiss"] .card--featured:hover {
background: var(--ink);
@@ -263,9 +281,11 @@
}
:root[data-template="swiss"] .card--featured .card__title {
color: var(--on-accent);
font-weight: 900;
text-transform: uppercase;
font-weight: 800;
text-transform: none;
font-size: clamp(1.95rem, 1.30rem + 1.8vw, 2.8rem);
letter-spacing: -0.025em;
line-height: 1;
}
:root[data-template="swiss"] .card--featured:hover .card__title,
:root[data-template="swiss"] .card--featured:hover .card__desc { color: var(--paper); }
@@ -276,7 +296,7 @@
/* tags — square, monoline, all caps */
:root[data-template="swiss"] .tag {
border-radius: 0;
border-color: var(--ink);
border: 0;
background: var(--paper);
font-family: var(--display);
font-weight: 600;
@@ -287,7 +307,6 @@
:root[data-template="swiss"] .card:hover .tag {
background: var(--paper);
color: var(--ink);
border-color: var(--paper);
}
:root[data-template="swiss"] .card--featured .tag {
background: color-mix(in oklch, var(--on-accent) 14%, transparent);
@@ -303,7 +322,7 @@
/* youtube */
:root[data-template="swiss"] .card--youtube {
border-radius: 0;
border-color: var(--ink);
border: 0;
}
:root[data-template="swiss"] .yt__play {
border-radius: 0;
@@ -318,37 +337,40 @@
:root[data-template="swiss"] .yt__title {
font-family: var(--display);
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.02em;
text-transform: none;
letter-spacing: -0.005em;
}
/* portfolio */
:root[data-template="swiss"] .card--portfolio {
border-radius: 0;
border-color: var(--ink);
border: 0;
}
:root[data-template="swiss"] .card--portfolio:hover { background: var(--paper); color: var(--ink); }
:root[data-template="swiss"] .card--portfolio:hover { background: var(--paper-2); color: var(--ink); }
:root[data-template="swiss"] .card--portfolio:hover .portfolio__caption .card__title { color: var(--accent); }
:root[data-template="swiss"] .card--portfolio:hover .card__desc,
:root[data-template="swiss"] .card--portfolio:hover .card__date,
:root[data-template="swiss"] .card--portfolio:hover .card__host { color: var(--muted); }
:root[data-template="swiss"] .portfolio__caption {
border-top: 2px solid var(--ink);
border-top: 0;
padding: 1.1rem 1.35rem 1.2rem;
}
:root[data-template="swiss"] .portfolio__caption .card__title {
font-family: var(--display);
font-weight: 900;
font-weight: 800;
font-variation-settings: normal;
text-transform: uppercase;
text-transform: none;
letter-spacing: -0.02em;
font-size: var(--fs-lg);
line-height: 1;
line-height: 1.05;
}
:root[data-template="swiss"] .portfolio__caption .card__desc { color: var(--muted); }
/* clients */
:root[data-template="swiss"] .client__logo {
border-radius: 0;
border-color: var(--ink);
background: var(--paper);
border: 0;
background: var(--paper-2);
}
/* opt the client tile out of the global card hover-flip: the card has no
chrome, so filling it with ink would hide the title text below the logo */
@@ -360,7 +382,6 @@
:root[data-template="swiss"] .card--client:hover .client__title { color: var(--accent); }
:root[data-template="swiss"] .card--client:hover .client__logo {
background: var(--ink);
border-color: var(--ink);
transform: none;
box-shadow: none;
}
@@ -374,15 +395,15 @@
:root[data-template="swiss"] .client__title {
font-family: var(--display);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
font-size: var(--fs-mini);
text-transform: none;
letter-spacing: 0;
font-size: var(--fs-sm);
color: var(--ink);
}
/* footer */
:root[data-template="swiss"] .foot {
border-top: 2px solid var(--ink);
border-top: 1px solid var(--rule);
color: var(--ink);
font-family: var(--display);
font-weight: 600;
@@ -391,7 +412,7 @@
font-family: var(--display);
font-style: normal;
text-transform: uppercase;
font-weight: 900;
font-weight: 800;
color: var(--accent);
font-size: var(--fs-md);
letter-spacing: 0.08em;

View File

@@ -38,7 +38,10 @@
<span>
<span class="card__title">${esc(it.title)}</span>
${it.description ? `<span class="card__desc">${esc(it.description)}</span>` : ""}
${host ? `<span class="card__host">${esc(host)}</span>` : ""}
<span class="card__meta">
${host ? `<span class="card__host">${esc(host)}</span>` : ""}
${dateMarkup(it)}
</span>
</span>
</a>`;
}
@@ -52,7 +55,7 @@
<${tag} class="card card--project${featured} reveal" ${attrs}>
<span class="card__title">${esc(it.title)}</span>
${it.description ? `<p class="card__desc">${esc(it.description)}</p>` : ""}
${tags ? `<div class="tags">${tags}</div>` : ""}
${(tags || it.date) ? `<div class="tags">${tags}${dateMarkup(it)}</div>` : ""}
</${tag}>`;
}
@@ -63,7 +66,7 @@
<div class="yt__play" aria-hidden="true">
<svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z" fill="currentColor"/></svg>
</div>
<div class="yt__title">${esc(it.title)}</div>
<div class="yt__title">${esc(it.title)}${it.date ? ` <span class="yt__date">${esc(fmtDate(it.date))}</span>` : ""}</div>
</div>
</div>`;
}
@@ -78,10 +81,11 @@
<div class="portfolio__media"${ratio}>
${src ? `<img loading="lazy" alt="${esc(it.title || "")}" src="${esc(src)}">` : ""}
</div>
${(it.title || it.description) ? `
${(it.title || it.description || it.date) ? `
<div class="portfolio__caption">
${it.title ? `<span class="card__title">${esc(it.title)}</span>` : ""}
${it.description ? `<span class="card__desc">${esc(it.description)}</span>` : ""}
${dateMarkup(it)}
</div>` : ""}
</${tag}>`;
}
@@ -102,6 +106,7 @@
: esc(initial)}
</span>
${it.title ? `<span class="client__title">${esc(it.title)}</span>` : ""}
${dateMarkup(it)}
</${tag}>`;
}
@@ -140,7 +145,8 @@
rss: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M5 3a16 16 0 0 1 16 16h-3A13 13 0 0 0 5 6V3Zm0 7a9 9 0 0 1 9 9h-3a6 6 0 0 0-6-6v-3Zm1.5 6a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Z"/></svg>',
link: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7 0l3-3a5 5 0 0 0-7-7l-1 1"/><path d="M14 11a5 5 0 0 0-7 0l-3 3a5 5 0 0 0 7 7l1-1"/></svg>',
calendar: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-calendar-days-icon lucide-calendar-days"><path d="M8 2v4"/><path d="M16 2v4"/><rect width="18" height="18" x="3" y="4" rx="2"/><path d="M3 10h18"/><path d="M8 14h.01"/><path d="M12 14h.01"/><path d="M16 14h.01"/><path d="M8 18h.01"/><path d="M12 18h.01"/><path d="M16 18h.01"/></svg>',
bluesky: '<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Bluesky</title><path d="M5.202 2.857C7.954 4.922 10.913 9.11 12 11.358c1.087-2.247 4.046-6.436 6.798-8.501C20.783 1.366 24 .213 24 3.883c0 .732-.42 6.156-.667 7.037-.856 3.061-3.978 3.842-6.755 3.37 4.854.826 6.089 3.562 3.422 6.299-5.065 5.196-7.28-1.304-7.847-2.97-.104-.305-.152-.448-.153-.327 0-.121-.05.022-.153.327-.568 1.666-2.782 8.166-7.847 2.97-2.667-2.737-1.432-5.473 3.422-6.3-2.777.473-5.899-.308-6.755-3.369C.42 10.04 0 4.615 0 3.883c0-3.67 3.217-2.517 5.202-1.026"/></svg>'
bluesky: '<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Bluesky</title><path d="M5.202 2.857C7.954 4.922 10.913 9.11 12 11.358c1.087-2.247 4.046-6.436 6.798-8.501C20.783 1.366 24 .213 24 3.883c0 .732-.42 6.156-.667 7.037-.856 3.061-3.978 3.842-6.755 3.37 4.854.826 6.089 3.562 3.422 6.299-5.065 5.196-7.28-1.304-7.847-2.97-.104-.305-.152-.448-.153-.327 0-.121-.05.022-.153.327-.568 1.666-2.782 8.166-7.847 2.97-2.667-2.737-1.432-5.473 3.422-6.3-2.777.473-5.899-.308-6.755-3.369C.42 10.04 0 4.615 0 3.883c0-3.67 3.217-2.517 5.202-1.026"/></svg>',
lastfm: '<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Last.fm</title><path d="M10.584 17.21l-.88-2.392s-1.43 1.594-3.573 1.594c-1.897 0-3.244-1.649-3.244-4.288 0-3.382 1.704-4.591 3.381-4.591 2.42 0 3.189 1.567 3.849 3.574l.88 2.749c.88 2.666 2.529 4.81 7.285 4.81 3.409 0 5.718-1.044 5.718-3.793 0-2.227-1.265-3.381-3.63-3.931l-1.758-.385c-1.21-.275-1.567-.77-1.567-1.595 0-.934.742-1.484 1.952-1.484 1.32 0 2.034.495 2.144 1.677l2.749-.33c-.22-2.474-1.924-3.492-4.729-3.492-2.474 0-4.893.935-4.893 3.932 0 1.87.907 3.051 3.189 3.601l1.87.44c1.402.33 1.869.907 1.869 1.704 0 1.017-.99 1.43-2.86 1.43-2.776 0-3.93-1.457-4.59-3.464l-.907-2.75c-1.155-3.573-2.997-4.893-6.653-4.893C2.144 5.333 0 7.89 0 12.233c0 4.18 2.144 6.434 5.993 6.434 3.106 0 4.591-1.457 4.591-1.457z"/></svg>'
};
function renderSocial(items) {
@@ -152,11 +158,24 @@
function nameMarkup(name) {
if (!name) return "";
const parts = String(name).trim().split(/\s+/);
if (parts.length < 2) return esc(name);
if (parts.length < 2) return `<span class="hero__name-first">${esc(name)}</span>`;
const last = parts.pop();
return `${esc(parts.join(" "))} <em>${esc(last)}</em>`;
return `<span class="hero__name-first">${esc(parts.join(" "))}</span> <em>${esc(last)}</em>`;
}
function fmtDate(d) {
if (!d) return "";
const m = String(d).match(/^(\d{4})(?:-(\d{2}))?(?:-(\d{2}))?$/);
if (!m) return String(d);
const [, y, mo, dd] = m;
const months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
if (dd && mo) return `${months[+mo - 1]} ${+dd}, ${y}`;
if (mo) return `${months[+mo - 1]} ${y}`;
return y;
}
const dateMarkup = (it) =>
it.date ? `<time class="card__date" datetime="${esc(it.date)}">${esc(fmtDate(it.date))}</time>` : "";
function attachFaviconFallback(root) {
$$("img[data-fallback-initial]", root).forEach(img => {
img.addEventListener("error", () => {

View File

@@ -17,8 +17,8 @@
"kicker": "Where I live online",
"items": [
{ "type": "link", "title": "Personal site", "url": "https://example.com", "description": "What I do, who I am" },
{ "type": "link", "title": "Notes", "url": "https://notes.example.com", "description": "Long-form writing" },
{ "type": "link", "title": "Lab", "url": "https://lab.example.com", "description": "Experiments & sketches" }
{ "type": "link", "title": "Notes", "url": "https://notes.example.com", "description": "Long-form writing", "date": "2025-09" },
{ "type": "link", "title": "Lab", "url": "https://lab.example.com", "description": "Experiments & sketches", "date": "2024" }
]
},
{
@@ -32,14 +32,16 @@
"title": "Analytical Engine",
"url": "https://example.com/projects/engine",
"description": "An early proposal for a general-purpose computing machine, with looping and conditional branching.",
"tags": ["computing", "research"]
"tags": ["computing", "research"],
"date": "1837"
},
{
"type": "card",
"title": "Bernoulli Numbers",
"url": "https://example.com/projects/bernoulli",
"description": "Method for computing Bernoulli numbers using the Engine.",
"tags": ["mathematics"]
"tags": ["mathematics"],
"date": "1843-07"
}
]
},
@@ -53,7 +55,8 @@
"title": "Notes G — diagrams for publication",
"url": "https://example.com/showcase/notes-g",
"image": "https://images.unsplash.com/photo-1518770660439-4636190af475?w=1600&q=80",
"description": "Tables and diagrams prepared for the 1843 translation."
"description": "Tables and diagrams prepared for the 1843 translation.",
"date": "1843-10-15"
}
]
},

View File

@@ -19,7 +19,7 @@
<!-- Editorial: Fraunces (variable serif) + Geist (refined sans) + Geist Mono
Swiss: Archivo (variable grotesque, near-Akzidenz/Univers) -->
<link href="https://fonts.googleapis.com/css2?family=Archivo:wght@400;500;600;700;900&family=Fraunces:opsz,wght,SOFT,WONK@9..144,300..600,30..100,0..1&family=Geist:wght@300;400;500;600&family=Geist+Mono:wght@400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Archivo:wght@400;500;600;700;900&family=DM+Serif+Display:ital@0;1&family=Fraunces:opsz,wght,SOFT,WONK@9..144,300..600,30..100,0..1&family=Geist:wght@300;400;500;600&family=Geist+Mono:wght@400;500&display=swap" rel="stylesheet">
<script>
// Apply saved theme + template before paint to avoid flash