@takazudo/zudo-doc 5.22.1 → 5.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,28 @@ The format is based on Keep a Changelog, and release notes are generated from th
8
8
 
9
9
  No unreleased changes.
10
10
 
11
+ ## [5.24.0] - 2026-09-13
12
+
13
+ ### Features
14
+
15
+ - Home-page h2 headings (compact intro, sitemap, and Tags) now share a unified `zd-home-heading` style that neutralizes theme-pack h2 decoration, and a divider now precedes the Tags section. (9ca2eef7)
16
+
17
+ ### Bug Fixes
18
+
19
+ - Academia theme pack: the home hero title stays aligned with the identity block and home dividers are centered, instead of inheriting the pack's paper-title centering and left-set footnote rule. (81266a2b)
20
+
21
+ ## [5.23.0] - 2026-09-13
22
+
23
+ ### Features
24
+
25
+ - Added strict `zudo-doc check images` validation for missing local image and media references in built HTML. (90e513fa8)
26
+ - Expanded asset scanning to relative `src` values, `srcset`, media elements, SVG references, `<base href>`, and containment-safe path resolution. (9f1cf6e31)
27
+
28
+ ### Bug Fixes
29
+
30
+ - Made generated Claude resource frontmatter stable across the MDX formatter, including legacy YAML scalar aliases. (9165c0547, db90c9685)
31
+ - Covered qualified SVG `xlink:href`, pnpm argument separators, empty base URLs, and literal percent characters in page paths. (48b0aa781, 994e6aec3, c9b772b76)
32
+
11
33
  ## [5.22.1] - 2026-09-13
12
34
 
13
35
  ### Bug Fixes
package/bin/zudo-doc.mjs CHANGED
@@ -3,20 +3,16 @@
3
3
  //
4
4
  // Package bin: `zudo-doc eject <component>` swizzle CLI, plus
5
5
  // `zudo-doc theme list|apply <slug>` (issue #2824; ADR
6
- // docs/adr/theme-packs.md), plus `zudo-doc eject logo` (issue #3050; epic
7
- // #3047).
6
+ // docs/adr/theme-packs.md), `zudo-doc eject logo` (issue #3050; epic #3047),
7
+ // and the strict `zudo-doc check images` post-build gate (issue #4185).
8
8
  //
9
9
  // Self-contained ESM — runs on plain `node` with NO `tsx` requirement. The
10
- // eject, theme-cli, and eject-logo logic are imported from the package's
11
- // COMPILED `../dist/eject/index.js` / `../dist/theme-cli/index.js` /
12
- // `../dist/eject-logo/index.js`, and the only runtime deps are `minimist` +
13
- // `picocolors` (declared deps of this package, so they are present
14
- // transitively in any consumer's node_modules). This is the key difference
15
- // from the tsx-runner pattern used by `bin/tags-audit.mjs`: tags-audit must
16
- // load the *project's* TypeScript config files at runtime (hence tsx),
17
- // whereas eject/theme-cli/eject-logo only copy files / rewrite text — no TS
18
- // eval needed — so all three work in a default generated project that never
19
- // installed tsx (#2367).
10
+ // eject, theme-cli, eject-logo, and image-check logic are imported from the
11
+ // package's COMPILED `../dist/...` files. The bin never loads package
12
+ // TypeScript source or requires `tsx`. This is the key difference from the
13
+ // tsx-runner pattern used by `bin/tags-audit.mjs`: tags-audit must load the
14
+ // *project's* TypeScript config files at runtime (hence tsx), whereas these
15
+ // commands only copy files, rewrite text, or scan already-built HTML.
20
16
  //
21
17
  // `eject logo` is special-cased below, BEFORE the EJECTABLE lookup — it is
22
18
  // NOT a source-copy swizzle (EJECTABLE's contract), it renders a fresh SVG
@@ -27,20 +23,37 @@
27
23
  // zudo-doc eject logo # render public/img/logo.svg + rewrite the logo field
28
24
  // zudo-doc theme list # list installed theme packs + the active one
29
25
  // zudo-doc theme apply <slug> # rewrite zfb.config.ts's themePack field
26
+ // zudo-doc check images # fail when built HTML references a missing asset
30
27
  // zudo-doc --help # show help
31
28
 
32
29
  import minimist from "minimist";
33
30
  import pc from "picocolors";
31
+ import { existsSync, readFileSync, statSync } from "node:fs";
32
+ import { resolve } from "node:path";
34
33
  import { eject, EJECTABLE } from "../dist/eject/index.js";
35
34
  import { ejectLogo } from "../dist/eject-logo/index.js";
35
+ import { scanImgSrcs } from "../dist/plugins/internal/img-src-check/index.js";
36
36
  import { applyThemePack, formatThemeList, listThemePacks } from "../dist/theme-cli/index.js";
37
37
 
38
- const argv = minimist(process.argv.slice(2), {
39
- string: ["seed"],
38
+ const cliArgs = process.argv.slice(2);
39
+ if (cliArgs[0] === "check" && cliArgs[1] === "images" && cliArgs[2] === "--") {
40
+ cliArgs.splice(2, 1);
41
+ }
42
+
43
+ const argv = minimist(cliArgs, {
44
+ string: ["allowlist", "base", "dist", "seed"],
40
45
  boolean: ["help", "force"],
41
46
  alias: { h: "help" },
42
47
  });
43
48
 
49
+ const CHECK_IMAGES_USAGE = `Usage: zudo-doc check images [options]
50
+
51
+ Options:
52
+ -h, --help Show this help message
53
+ --dist <dir> Built HTML directory (default: dist)
54
+ --base <path> Public URL base (default: /)
55
+ --allowlist <file> File of <page>:<url> exceptions`;
56
+
44
57
  function printHelp() {
45
58
  const validNames = Object.keys(EJECTABLE).sort().join(", ");
46
59
  console.log(`
@@ -54,6 +67,8 @@ ${pc.bold("Subcommands:")}
54
67
  logo field to point at it.
55
68
  theme list List the installed theme packs and which one is active.
56
69
  theme apply <slug> Rewrite zfb.config.ts's themePack field to <slug>.
70
+ check images [options]
71
+ Fail when built HTML references a missing local asset.
57
72
 
58
73
  ${pc.bold("Ejectable components:")}
59
74
  ${validNames}
@@ -78,9 +93,16 @@ ${pc.bold("Examples:")}
78
93
 
79
94
  ${pc.dim("# Switch to the foundry theme pack")}
80
95
  zudo-doc theme apply foundry
96
+
97
+ ${pc.dim("# Check built HTML for missing local images and assets")}
98
+ zudo-doc check images
81
99
  `);
82
100
  }
83
101
 
102
+ function printCheckImagesHelp() {
103
+ console.log(CHECK_IMAGES_USAGE);
104
+ }
105
+
84
106
  async function runEject(componentArg) {
85
107
  if (!componentArg) {
86
108
  console.error(
@@ -152,7 +174,100 @@ async function runTheme(themeArgs) {
152
174
  process.exit(1);
153
175
  }
154
176
 
177
+ function readImageAllowlist(allowlistPath) {
178
+ if (!allowlistPath) return new Set();
179
+
180
+ // Keep this parser in lockstep with scripts/check-links.js: trim each line,
181
+ // ignore blank/comment-only lines, and compare the remaining entries
182
+ // literally. The image checker key is the compact `<page>:<url>` form.
183
+ const path = resolve(process.cwd(), allowlistPath);
184
+ if (!existsSync(path)) return new Set();
185
+ const text = readFileSync(path, "utf8");
186
+ return new Set(
187
+ text
188
+ .split("\n")
189
+ .map((line) => line.trim())
190
+ .filter((line) => line.length > 0 && !line.startsWith("#")),
191
+ );
192
+ }
193
+
194
+ function imageAllowlistKey(reference) {
195
+ return `${reference.pagePath}:${reference.src}`;
196
+ }
197
+
198
+ function runCheckImages(checkArgs) {
199
+ const knownArgvKeys = new Set(["_", "allowlist", "base", "dist", "force", "h", "help"]);
200
+ const unknownOption = Object.keys(argv).find((key) => !knownArgvKeys.has(key));
201
+ const unsupportedOption = unknownOption ?? (argv.force ? "force" : undefined);
202
+ if (unsupportedOption !== undefined) {
203
+ console.error(pc.red(`Unknown option "--${unsupportedOption}".`) + `\n${CHECK_IMAGES_USAGE}`);
204
+ process.exit(1);
205
+ }
206
+
207
+ const [unexpected] = checkArgs;
208
+ if (unexpected !== undefined) {
209
+ console.error(pc.red(`Unexpected argument "${unexpected}".`) + `\n${CHECK_IMAGES_USAGE}`);
210
+ process.exit(1);
211
+ }
212
+
213
+ const distArg = argv.dist ?? "dist";
214
+ const baseArg = argv.base ?? "/";
215
+ const allowlistArg = argv.allowlist;
216
+ if (typeof distArg !== "string" || distArg.length === 0) {
217
+ console.error(pc.red("Missing value for --dist.") + `\n${CHECK_IMAGES_USAGE}`);
218
+ process.exit(1);
219
+ }
220
+ if (typeof baseArg !== "string" || baseArg.length === 0) {
221
+ console.error(pc.red("Missing value for --base.") + `\n${CHECK_IMAGES_USAGE}`);
222
+ process.exit(1);
223
+ }
224
+ if (allowlistArg !== undefined && (typeof allowlistArg !== "string" || allowlistArg.length === 0)) {
225
+ console.error(pc.red("Missing value for --allowlist.") + `\n${CHECK_IMAGES_USAGE}`);
226
+ process.exit(1);
227
+ }
228
+
229
+ const outDir = resolve(process.cwd(), distArg ?? "dist");
230
+ try {
231
+ if (!statSync(outDir).isDirectory()) throw new Error("not a directory");
232
+ } catch {
233
+ console.error(
234
+ pc.red(`Build output directory not found: ${outDir}.`) +
235
+ "\nRun the build first, then run `zudo-doc check images` again.",
236
+ );
237
+ process.exit(1);
238
+ }
239
+
240
+ const allowlist = readImageAllowlist(allowlistArg);
241
+ const result = scanImgSrcs({ outDir, base: baseArg });
242
+ const broken = result.broken.filter((reference) => !allowlist.has(imageAllowlistKey(reference)));
243
+
244
+ for (const reference of broken) {
245
+ // The CLI's contract is deliberately compact and stable, unlike the
246
+ // plugin logger's prefixed prose.
247
+ const element = reference.element ? `${reference.element} ` : "";
248
+ console.log(`${reference.pagePath}: ${element}${reference.src} (${reference.reason})`);
249
+ }
250
+
251
+ if (broken.length > 0) {
252
+ console.log(
253
+ `Found ${broken.length} broken image reference${broken.length === 1 ? "" : "s"} in ${result.htmlFileCount} HTML file${result.htmlFileCount === 1 ? "" : "s"} after allowlist.`,
254
+ );
255
+ process.exit(1);
256
+ }
257
+
258
+ const allowlisted = result.broken.length - broken.length;
259
+ const allowlistNote = allowlisted > 0 ? `; ${allowlisted} allowlisted` : "";
260
+ console.log(
261
+ `No broken image references found (${result.htmlFileCount} HTML file${result.htmlFileCount === 1 ? "" : "s"}, ${result.imageCount} local reference${result.imageCount === 1 ? "" : "s"}${allowlistNote}).`,
262
+ );
263
+ }
264
+
155
265
  async function main() {
266
+ if (argv._[0] === "check" && argv._[1] === "images" && argv["help"]) {
267
+ printCheckImagesHelp();
268
+ process.exit(0);
269
+ }
270
+
156
271
  if (argv["help"] || argv._.length === 0) {
157
272
  printHelp();
158
273
  process.exit(0);
@@ -163,6 +278,7 @@ async function main() {
163
278
  if (subcommand === "eject" && rest[0] === "logo") return runEjectLogo();
164
279
  if (subcommand === "eject") return runEject(rest[0]);
165
280
  if (subcommand === "theme") return runTheme(rest);
281
+ if (subcommand === "check" && rest[0] === "images") return runCheckImages(rest.slice(1));
166
282
 
167
283
  console.error(
168
284
  pc.red(`Unknown subcommand "${subcommand}".`) +
package/dist/catalog.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // validated src/theme-packs/<slug>/meta.json registry. Re-run `pnpm build`
4
4
  // to regenerate after adding, removing, or editing a theme pack.
5
5
  // Catalog schemaVersion 2 is distinct from each pack's meta.json schemaVersion 1.
6
- const catalog = {"schemaVersion":2,"packs":[{"slug":"default","meta":{"schemaVersion":1,"slug":"default","name":"Default","description":"The stock zudo-doc look. No pack stylesheet is loaded — your project's configured color scheme and fonts render unchanged.","mode":"dark","version":"1.0.0","fonts":{"sans":"System","mono":"System","loaded":[]},"preview":{"light":{"bg":"oklch(0.965 0.004 65)","fg":"oklch(0.185 0.005 65)","accent":"oklch(0.470 0.120 56)","syntax":{"keyword":"oklch(0.470 0.120 56)","string":"oklch(0.470 0.140 145)","comment":"oklch(0.480 0.008 65)","callable":"oklch(0.485 0.122 245)"}},"dark":{"bg":"oklch(0.185 0.005 65)","fg":"oklch(0.965 0.004 65)","accent":"oklch(0.700 0.158 62)","syntax":{"keyword":"oklch(0.700 0.158 62)","string":"oklch(0.680 0.145 145)","comment":"oklch(0.705 0.008 65)","callable":"oklch(0.680 0.130 245)"}}}},"hasStylesheet":false},{"slug":"academia","meta":{"schemaVersion":1,"slug":"academia","name":"Academia","description":"A LaTeX paper for the web — white page, Computer-Modern-black ink, hyperref navy links, numbered theorem admonitions, booktabs tables. Dark mode is a scholarly slate.","mode":"light","version":"1.0.1","fonts":{"sans":"Source Serif 4","mono":"Courier Prime","loaded":["Source Serif 4","Courier Prime"]},"preview":{"light":{"bg":"#ffffff","fg":"#141414","accent":"#1c3d8f","syntax":{"keyword":"#1c3d8f","string":"#93262a","comment":"#2e6b2e","callable":"#295e80"}},"dark":{"bg":"#1d222b","fg":"#e7eaf0","accent":"#97b8ee","syntax":{"keyword":"#a3c0f2","string":"#e0a09a","comment":"#8cbc8c","callable":"#85c2da"}}}},"hasStylesheet":true},{"slug":"bauhaus","meta":{"schemaVersion":1,"slug":"bauhaus","name":"Bauhaus","description":"Primary triad as structure — gallery white, near-black ink, and red, yellow, blue doing structural work: circle, triangle, square markers, thick geometric rules, zero radius.","mode":"light","version":"1.0.0","fonts":{"sans":"Jost","mono":"Space Mono","display":"Archivo Black","loaded":["Jost","Archivo Black","Space Mono"]},"preview":{"light":{"bg":"#ffffff","fg":"#141414","accent":"#b3231b","syntax":{"keyword":"#b3231b","string":"#6d5800","comment":"#63615a","callable":"#1c4bbe"}},"dark":{"bg":"#18181b","fg":"#f0efe9","accent":"#ff7d70","syntax":{"keyword":"#ff7d70","string":"#e9ce6a","comment":"#999689","callable":"#8fb0ff"}}}},"hasStylesheet":true},{"slug":"beacon","meta":{"schemaVersion":1,"slug":"beacon","name":"Beacon","description":"WCAG-AAA high contrast — 7:1+ ink everywhere, 3px focus rings, always-underlined links. Clarity as beauty.","mode":"light","version":"1.0.1","fonts":{"sans":"Atkinson Hyperlegible","mono":"Atkinson Hyperlegible Mono","loaded":["Atkinson Hyperlegible","Atkinson Hyperlegible Mono"]},"preview":{"light":{"bg":"#ffffff","fg":"#000000","accent":"#0044cc","syntax":{"keyword":"#0044cc","string":"#a50000","comment":"#595959","callable":"#00287a"}},"dark":{"bg":"#000000","fg":"#ffffff","accent":"#6db3ff","syntax":{"keyword":"#6db3ff","string":"#ff8f8f","comment":"#b3b3b3","callable":"#a8d1ff"}}}},"hasStylesheet":true},{"slug":"blueprint","meta":{"schemaVersion":1,"slug":"blueprint","name":"Blueprint","description":"Cyanotype drafting sheet — pale-cyan linework on prussian grid paper, title-block caps, dashed rules, mono-caps labels. Light mode is the vellum original.","mode":"dark","version":"1.0.1","fonts":{"sans":"Inter","mono":"IBM Plex Mono","display":"Josefin Sans","loaded":["Inter","Josefin Sans","IBM Plex Mono"]},"preview":{"light":{"bg":"oklch(0.965 0.012 235)","fg":"oklch(0.28 0.05 253)","accent":"oklch(0.44 0.095 235)","syntax":{"keyword":"oklch(0.46 0.1 80)","string":"oklch(0.43 0.085 195)","comment":"oklch(0.5 0.03 248)","callable":"oklch(0.44 0.09 235)"}},"dark":{"bg":"oklch(0.27 0.055 252)","fg":"oklch(0.965 0.01 220)","accent":"oklch(0.86 0.09 205)","syntax":{"keyword":"oklch(0.87 0.12 95)","string":"oklch(0.83 0.1 185)","comment":"oklch(0.72 0.032 235)","callable":"oklch(0.83 0.085 215)"}}}},"hasStylesheet":true},{"slug":"botanica","meta":{"schemaVersion":1,"slug":"botanica","name":"Botanica","description":"A vintage botanical plate — herbarium cream, deep green ink, engraved double rules, Latin small-caps plate labels.","mode":"light","version":"1.0.0","fonts":{"sans":"Crimson Pro","mono":"Courier Prime","display":"Cormorant Garamond","loaded":["Cormorant Garamond","Crimson Pro","Courier Prime"]},"preview":{"light":{"bg":"oklch(0.956 0.02 92)","fg":"oklch(0.29 0.03 95)","accent":"oklch(0.42 0.09 152)","syntax":{"keyword":"oklch(0.4 0.075 60)","string":"oklch(0.43 0.095 150)","comment":"oklch(0.48 0.04 95)","callable":"oklch(0.43 0.075 205)"}},"dark":{"bg":"oklch(0.215 0.022 160)","fg":"oklch(0.92 0.02 95)","accent":"oklch(0.78 0.09 148)","syntax":{"keyword":"oklch(0.82 0.085 80)","string":"oklch(0.8 0.095 145)","comment":"oklch(0.7 0.03 110)","callable":"oklch(0.78 0.075 200)"}}}},"hasStylesheet":true},{"slug":"broadsheet","meta":{"schemaVersion":1,"slug":"broadsheet","name":"Broadsheet","description":"Newspaper editorial — Playfair masthead, Oxford ink rules, a red drop cap, and a night-edition dark mode.","mode":"light","version":"1.0.2","fonts":{"sans":"Source Serif 4","mono":"Courier Prime","display":"Playfair Display","loaded":["Playfair Display","Source Serif 4","Courier Prime"]},"preview":{"light":{"bg":"oklch(0.955 0.012 92)","fg":"oklch(0.205 0.012 80)","accent":"oklch(0.455 0.155 27)","syntax":{"keyword":"oklch(0.445 0.155 27)","string":"oklch(0.45 0.075 60)","comment":"oklch(0.5 0.016 78)","callable":"oklch(0.42 0.09 252)"}},"dark":{"bg":"oklch(0.185 0.012 75)","fg":"oklch(0.935 0.015 90)","accent":"oklch(0.7 0.145 27)","syntax":{"keyword":"oklch(0.72 0.14 27)","string":"oklch(0.79 0.07 70)","comment":"oklch(0.7 0.02 85)","callable":"oklch(0.74 0.08 248)"}}}},"hasStylesheet":true},{"slug":"brutalist","meta":{"schemaVersion":1,"slug":"brutalist","name":"Brutalist","description":"Raw concrete web — stark black on white, 4px slab borders, zero radius, hazard-orange tape. Structure you can see.","mode":"light","version":"1.0.3","fonts":{"sans":"Space Grotesk","mono":"IBM Plex Mono","display":"Archivo Black","loaded":["Archivo Black","Space Grotesk","IBM Plex Mono"]},"preview":{"light":{"bg":"#ffffff","fg":"#000000","accent":"#c2410c","syntax":{"keyword":"#c2410c","string":"#9a3412","comment":"#767676","callable":"#000000"}},"dark":{"bg":"#0d0d0d","fg":"#f5f5f5","accent":"#ff6a1f","syntax":{"keyword":"#ff7a2e","string":"#ffb38a","comment":"#878787","callable":"#ffffff"}}}},"hasStylesheet":true},{"slug":"drift","meta":{"schemaVersion":1,"slug":"drift","name":"Drift","description":"Floaty slate-blue comfort dark for long reading — soft glows in place of borders, gentle elevation, relaxed Plex type.","mode":"dark","version":"1.0.2","fonts":{"sans":"IBM Plex Sans","mono":"IBM Plex Mono","loaded":["IBM Plex Sans","IBM Plex Mono"]},"preview":{"light":{"bg":"#edf2f6","fg":"#24303d","accent":"#236799","syntax":{"keyword":"#49539c","string":"#2b754f","comment":"#596673","callable":"#00688d"}},"dark":{"bg":"#181f26","fg":"#d6dee4","accent":"#6cafd9","syntax":{"keyword":"#9caeee","string":"#88cba4","comment":"#8494a4","callable":"#75c6e1"}}}},"hasStylesheet":true},{"slug":"eink","meta":{"schemaVersion":1,"slug":"eink","name":"E-Ink","description":"E-reader grayscale — warm paper, near-black ink, shadowless hairline chrome, zero radius. Code is told by weight, slant, and gray value; only diffs keep a trace of ink-blue and ink-red.","mode":"light","version":"1.0.0","fonts":{"sans":"EB Garamond","mono":"Courier Prime","loaded":["EB Garamond","Courier Prime"]},"preview":{"light":{"bg":"oklch(0.956 0.007 90)","fg":"oklch(0.24 0.012 85)","accent":"oklch(0.155 0.01 85)","syntax":{"keyword":"oklch(0.18 0.01 85)","string":"oklch(0.46 0.012 85)","comment":"oklch(0.505 0.01 85)","callable":"oklch(0.31 0.012 85)"}},"dark":{"bg":"oklch(0.215 0.008 80)","fg":"oklch(0.885 0.012 85)","accent":"oklch(0.955 0.008 85)","syntax":{"keyword":"oklch(0.96 0.008 85)","string":"oklch(0.715 0.012 85)","comment":"oklch(0.645 0.01 85)","callable":"oklch(0.82 0.01 85)"}}}},"hasStylesheet":true},{"slug":"fjord","meta":{"schemaVersion":1,"slug":"fjord","name":"Fjord","description":"Polar-night blue under a faint aurora — frost-cyan accents, icy borders, snow-white text. Cold nordic calm.","mode":"dark","version":"1.0.2","fonts":{"sans":"Inter","mono":"JetBrains Mono","loaded":["Inter","JetBrains Mono"]},"preview":{"light":{"bg":"oklch(0.971 0.008 230)","fg":"oklch(0.255 0.03 255)","accent":"oklch(0.455 0.092 205)","syntax":{"keyword":"oklch(0.455 0.105 250)","string":"oklch(0.495 0.105 165)","comment":"oklch(0.51 0.03 245)","callable":"oklch(0.445 0.098 202)"}},"dark":{"bg":"oklch(0.232 0.024 255)","fg":"oklch(0.93 0.012 235)","accent":"oklch(0.79 0.095 195)","syntax":{"keyword":"oklch(0.76 0.095 242)","string":"oklch(0.8 0.105 158)","comment":"oklch(0.63 0.03 245)","callable":"oklch(0.815 0.095 195)"}}}},"hasStylesheet":true},{"slug":"foundry","meta":{"schemaVersion":1,"slug":"foundry","name":"Foundry","description":"GitHub-neutral baseline — white paper, near-black ink, Primer-blue links, quiet gray rules. Restraint, perfected.","mode":"light","version":"1.0.2","fonts":{"sans":"Inter","mono":"System","loaded":["Inter"]},"preview":{"light":{"bg":"#ffffff","fg":"#1f2328","accent":"#0969da","syntax":{"keyword":"#cf222e","string":"#0a3069","comment":"#6e7781","callable":"#8250df"}},"dark":{"bg":"#0d1117","fg":"#e6edf3","accent":"#4493f8","syntax":{"keyword":"#ff7b72","string":"#a5d6ff","comment":"#8b949e","callable":"#d2a8ff"}}}},"hasStylesheet":true},{"slug":"futura-editorial","meta":{"schemaVersion":1,"slug":"futura-editorial","name":"Futura Editorial","description":"Normal-weight geometric Futura headings over Noto Sans body — clean white, near-black ink, one restrained red accent.","mode":"light","version":"1.0.1","fonts":{"sans":"Noto Sans","mono":"Space Mono","display":"Jost","loaded":["Jost","Noto Sans","Space Mono"]},"preview":{"light":{"bg":"oklch(1 0 0)","fg":"oklch(0.17 0 0)","accent":"oklch(0.505 0.18 28)","syntax":{"keyword":"oklch(0.505 0.18 28)","string":"oklch(0.47 0.09 160)","comment":"oklch(0.52 0 0)","callable":"oklch(0.45 0.1 250)"}},"dark":{"bg":"oklch(0.165 0 0)","fg":"oklch(0.935 0 0)","accent":"oklch(0.72 0.15 29)","syntax":{"keyword":"oklch(0.72 0.15 29)","string":"oklch(0.76 0.1 160)","comment":"oklch(0.63 0 0)","callable":"oklch(0.73 0.095 250)"}}}},"hasStylesheet":true},{"slug":"hearth","meta":{"schemaVersion":1,"slug":"hearth","name":"Hearth","description":"Warm cream & brick-red fireside docs — Fraunces headings, amber code blocks, ember-glow dark mode.","mode":"light","version":"1.0.3","fonts":{"sans":"Nunito","mono":"Spline Sans Mono","display":"Fraunces","loaded":["Fraunces","Nunito","Spline Sans Mono"]},"preview":{"light":{"bg":"oklch(0.958 0.02 85)","fg":"oklch(0.255 0.028 40)","accent":"oklch(0.49 0.145 31)","syntax":{"keyword":"oklch(0.48 0.148 30)","string":"oklch(0.47 0.1 135)","comment":"oklch(0.51 0.035 60)","callable":"oklch(0.45 0.092 245)"}},"dark":{"bg":"oklch(0.215 0.022 45)","fg":"oklch(0.915 0.02 80)","accent":"oklch(0.735 0.128 45)","syntax":{"keyword":"oklch(0.745 0.138 40)","string":"oklch(0.785 0.1 130)","comment":"oklch(0.645 0.032 65)","callable":"oklch(0.765 0.075 235)"}}}},"hasStylesheet":true},{"slug":"hollow","meta":{"schemaVersion":1,"slug":"hollow","name":"Hollow","description":"Dark violet space — neon pink headings, violet links, a quiet starfield, and razor-square midnight chrome.","mode":"dark","version":"1.0.2","fonts":{"sans":"Space Grotesk","mono":"Space Mono","loaded":["Space Grotesk","Space Mono"]},"preview":{"light":{"bg":"oklch(0.962 0.012 300)","fg":"oklch(0.24 0.045 295)","accent":"oklch(0.45 0.17 300)","syntax":{"keyword":"oklch(0.53 0.2 348)","string":"oklch(0.45 0.1 195)","comment":"oklch(0.51 0.035 292)","callable":"oklch(0.46 0.16 300)"}},"dark":{"bg":"oklch(0.155 0.035 295)","fg":"oklch(0.93 0.015 295)","accent":"oklch(0.74 0.14 300)","syntax":{"keyword":"oklch(0.76 0.17 347)","string":"oklch(0.83 0.1 190)","comment":"oklch(0.62 0.045 292)","callable":"oklch(0.76 0.13 300)"}}}},"hasStylesheet":true},{"slug":"ledger","meta":{"schemaVersion":1,"slug":"ledger","name":"Ledger","description":"Cream academic serif in the Tufte tradition — warm paper, ink prose, oxblood links, hairline rules, quiet code.","mode":"light","version":"1.0.1","fonts":{"sans":"Crimson Pro","mono":"IBM Plex Mono","loaded":["Crimson Pro","IBM Plex Mono"]},"preview":{"light":{"bg":"#fffff8","fg":"#16130e","accent":"#8b1a1a","syntax":{"keyword":"#8b1a1a","string":"#4e6328","comment":"#766b5c","callable":"#33586e"}},"dark":{"bg":"#191510","fg":"#e9e2d0","accent":"#d68a75","syntax":{"keyword":"#e2907c","string":"#a8b47c","comment":"#948a77","callable":"#93b4cd"}}}},"hasStylesheet":true},{"slug":"manuscript","meta":{"schemaVersion":1,"slug":"manuscript","name":"Manuscript","description":"A quiet Garamond book page — warm paper, soft ink, sepia rubrication, small-caps subheads, fleuron section marks.","mode":"light","version":"1.0.1","fonts":{"sans":"EB Garamond","mono":"Courier Prime","loaded":["EB Garamond","Courier Prime"]},"preview":{"light":{"bg":"oklch(0.963 0.013 85)","fg":"oklch(0.245 0.018 60)","accent":"oklch(0.44 0.095 55)","syntax":{"keyword":"oklch(0.455 0.13 30)","string":"oklch(0.44 0.085 150)","comment":"oklch(0.5 0.03 70)","callable":"oklch(0.435 0.09 275)"}},"dark":{"bg":"oklch(0.215 0.015 65)","fg":"oklch(0.885 0.022 80)","accent":"oklch(0.735 0.095 70)","syntax":{"keyword":"oklch(0.72 0.115 35)","string":"oklch(0.745 0.085 150)","comment":"oklch(0.645 0.03 75)","callable":"oklch(0.745 0.07 275)"}}}},"hasStylesheet":true},{"slug":"matcha","meta":{"schemaVersion":1,"slug":"matcha","name":"Matcha","description":"Green tea ceremony — deep matcha on warm cream, mincho headings, zen whitespace, tea-foam surfaces in both modes.","mode":"light","version":"1.0.2","fonts":{"sans":"Zen Kaku Gothic New","mono":"M PLUS 1 Code","display":"Shippori Mincho","loaded":["Zen Kaku Gothic New","Shippori Mincho","M PLUS 1 Code"]},"preview":{"light":{"bg":"oklch(0.973 0.012 106)","fg":"oklch(0.245 0.018 148)","accent":"oklch(0.435 0.105 142)","syntax":{"keyword":"oklch(0.418 0.105 150)","string":"oklch(0.47 0.092 106)","comment":"oklch(0.512 0.034 130)","callable":"oklch(0.438 0.082 196)"}},"dark":{"bg":"oklch(0.205 0.016 152)","fg":"oklch(0.938 0.018 106)","accent":"oklch(0.79 0.115 138)","syntax":{"keyword":"oklch(0.782 0.118 148)","string":"oklch(0.822 0.102 108)","comment":"oklch(0.668 0.034 130)","callable":"oklch(0.772 0.088 192)"}}}},"hasStylesheet":true},{"slug":"nocturne","meta":{"schemaVersion":1,"slug":"nocturne","name":"Nocturne","description":"Purple midnight — velvet aubergine depths, lavender links, and muted gold hairlines. Elegant, never neon.","mode":"dark","version":"1.0.2","fonts":{"sans":"Manrope","mono":"IBM Plex Mono","display":"Cormorant Garamond","loaded":["Cormorant Garamond","Manrope","IBM Plex Mono"]},"preview":{"light":{"bg":"oklch(0.958 0.012 300)","fg":"oklch(0.245 0.05 312)","accent":"oklch(0.455 0.15 295)","syntax":{"keyword":"oklch(0.46 0.16 295)","string":"oklch(0.5 0.105 85)","comment":"oklch(0.5 0.042 305)","callable":"oklch(0.455 0.125 265)"}},"dark":{"bg":"oklch(0.155 0.03 310)","fg":"oklch(0.922 0.018 300)","accent":"oklch(0.765 0.105 292)","syntax":{"keyword":"oklch(0.772 0.128 293)","string":"oklch(0.8 0.108 88)","comment":"oklch(0.645 0.045 302)","callable":"oklch(0.77 0.095 262)"}}}},"hasStylesheet":true},{"slug":"observatory","meta":{"schemaVersion":1,"slug":"observatory","name":"Observatory","description":"Night-sky atlas: star-field depth, constellation-line rules, nebula violet and comet gold over astronomer's navy.","mode":"dark","version":"1.0.3","fonts":{"sans":"Jost","mono":"Space Mono","display":"Josefin Sans","loaded":["Josefin Sans","Jost","Space Mono"]},"preview":{"light":{"bg":"oklch(0.965 0.012 255)","fg":"oklch(0.235 0.045 268)","accent":"oklch(0.47 0.1 75)","syntax":{"keyword":"oklch(0.46 0.14 300)","string":"oklch(0.45 0.1 162)","comment":"oklch(0.5 0.03 265)","callable":"oklch(0.46 0.11 250)"}},"dark":{"bg":"oklch(0.155 0.028 268)","fg":"oklch(0.93 0.015 255)","accent":"oklch(0.82 0.13 86)","syntax":{"keyword":"oklch(0.76 0.118 300)","string":"oklch(0.8 0.112 162)","comment":"oklch(0.61 0.038 265)","callable":"oklch(0.78 0.095 240)"}}}},"hasStylesheet":true},{"slug":"onyx","meta":{"schemaVersion":1,"slug":"onyx","name":"Onyx","description":"Luxury noir — jet black, champagne serif headings, and a single gold hairline accent. Watch-brand restraint.","mode":"dark","version":"1.0.3","fonts":{"sans":"Jost","mono":"IBM Plex Mono","display":"Cormorant Garamond","loaded":["Cormorant Garamond","Jost","IBM Plex Mono"]},"preview":{"light":{"bg":"oklch(0.972 0.005 85)","fg":"oklch(0.22 0.012 80)","accent":"oklch(0.5 0.105 80)","syntax":{"keyword":"oklch(0.51 0.105 82)","string":"oklch(0.47 0.07 155)","comment":"oklch(0.5 0.015 85)","callable":"oklch(0.45 0.045 250)"}},"dark":{"bg":"oklch(0.145 0.003 80)","fg":"oklch(0.92 0.022 88)","accent":"oklch(0.79 0.115 88)","syntax":{"keyword":"oklch(0.8 0.115 88)","string":"oklch(0.78 0.07 155)","comment":"oklch(0.62 0.02 85)","callable":"oklch(0.82 0.04 250)"}}}},"hasStylesheet":true},{"slug":"phosphor","meta":{"schemaVersion":1,"slug":"phosphor","name":"Phosphor","description":"Green CRT terminal — phosphor glow, scanlines, inverse-video nav, monospace everything.","mode":"dark","version":"1.0.1","fonts":{"sans":"JetBrains Mono","mono":"JetBrains Mono","display":"VT323","loaded":["VT323","JetBrains Mono"]},"preview":{"light":{"bg":"oklch(0.955 0.014 150)","fg":"oklch(0.28 0.058 150)","accent":"oklch(0.42 0.13 150)","syntax":{"keyword":"oklch(0.4 0.13 148)","string":"oklch(0.39 0.095 165)","comment":"oklch(0.49 0.05 150)","callable":"oklch(0.43 0.095 200)"}},"dark":{"bg":"oklch(0.135 0.014 150)","fg":"oklch(0.845 0.145 150)","accent":"oklch(0.87 0.215 148)","syntax":{"keyword":"oklch(0.9 0.21 148)","string":"oklch(0.9 0.12 165)","comment":"oklch(0.58 0.062 150)","callable":"oklch(0.83 0.125 200)"}}}},"hasStylesheet":true},{"slug":"riso","meta":{"schemaVersion":1,"slug":"riso","name":"Riso","description":"Risograph duotone print — warm paper, two inks (riso blue + fluorescent coral), misregistered offset shadows, and a soft linear paper grain.","mode":"light","version":"1.0.0","fonts":{"sans":"Space Grotesk","mono":"Space Mono","display":"Archivo Black","loaded":["Space Grotesk","Archivo Black","Space Mono"]},"preview":{"light":{"bg":"#f9f3e5","fg":"#1d3345","accent":"#0d5c94","syntax":{"keyword":"#a02310","string":"#0d5e6e","comment":"#4d5c66","callable":"#0b5b9b"}},"dark":{"bg":"#101d28","fg":"#f1e9d8","accent":"#58b8e8","syntax":{"keyword":"#ff8d72","string":"#66d4cf","comment":"#93a4ad","callable":"#5aaef5"}}}},"hasStylesheet":true},{"slug":"sakura","meta":{"schemaVersion":1,"slug":"sakura","name":"Sakura","description":"Cherry-blossom pastels — blush-white paper, plum ink, and rose accents on petal-soft corners; dark mode is yozakura, night blossoms under lantern-pink light.","mode":"light","version":"1.0.1","fonts":{"sans":"Nunito","mono":"M PLUS 1 Code","display":"Shippori Mincho","loaded":["Nunito","Shippori Mincho","M PLUS 1 Code"]},"preview":{"light":{"bg":"oklch(0.977 0.009 350)","fg":"oklch(0.29 0.055 340)","accent":"oklch(0.5 0.168 358)","syntax":{"keyword":"oklch(0.5 0.168 358)","string":"oklch(0.49 0.115 150)","comment":"oklch(0.5 0.04 340)","callable":"oklch(0.49 0.135 300)"}},"dark":{"bg":"oklch(0.215 0.03 330)","fg":"oklch(0.935 0.014 350)","accent":"oklch(0.78 0.115 355)","syntax":{"keyword":"oklch(0.79 0.125 355)","string":"oklch(0.78 0.115 150)","comment":"oklch(0.71 0.038 345)","callable":"oklch(0.78 0.105 300)"}}}},"hasStylesheet":true},{"slug":"scandi","meta":{"schemaVersion":1,"slug":"scandi","name":"Scandi","description":"Hygge minimalism — oat linen, sage & clay, pill nav actives and 8px corners; night mode is warm charcoal lit by candle amber.","mode":"light","version":"1.0.0","fonts":{"sans":"Manrope","mono":"Spline Sans Mono","loaded":["Manrope","Spline Sans Mono"]},"preview":{"light":{"bg":"oklch(0.963 0.012 83)","fg":"oklch(0.28 0.02 55)","accent":"oklch(0.44 0.08 152)","syntax":{"keyword":"oklch(0.47 0.105 40)","string":"oklch(0.45 0.09 150)","comment":"oklch(0.5 0.02 70)","callable":"oklch(0.45 0.08 240)"}},"dark":{"bg":"oklch(0.235 0.015 55)","fg":"oklch(0.92 0.012 85)","accent":"oklch(0.82 0.105 75)","syntax":{"keyword":"oklch(0.76 0.1 45)","string":"oklch(0.78 0.09 150)","comment":"oklch(0.66 0.02 75)","callable":"oklch(0.75 0.075 238)"}}}},"hasStylesheet":true},{"slug":"solar","meta":{"schemaVersion":1,"slug":"solar","name":"Solar","description":"Solarized precision — low-eyestrain paper tones, blue/cyan/orange accents, mono labels, technical calm in both modes.","mode":"light","version":"1.0.1","fonts":{"sans":"Source Sans 3","mono":"Source Code Pro","loaded":["Source Sans 3","Source Code Pro"]},"preview":{"light":{"bg":"#fdf6e3","fg":"#4f6369","accent":"#1c6a9f","syntax":{"keyword":"#606f00","string":"#12746c","comment":"#586b72","callable":"#1d6da3"}},"dark":{"bg":"#002b36","fg":"#93a1a1","accent":"#53a6df","syntax":{"keyword":"#8ea300","string":"#2caaa0","comment":"#899da4","callable":"#49a0dd"}}}},"hasStylesheet":true},{"slug":"sumi","meta":{"schemaVersion":1,"slug":"sumi","name":"Sumi","description":"Sumi-e ink on washi — bold mincho brush headings, deep black strokes, one vermillion hanko seal accent.","mode":"light","version":"1.0.3","fonts":{"sans":"Zen Kaku Gothic New","mono":"IBM Plex Mono","display":"Shippori Mincho B1","loaded":["Shippori Mincho B1","Zen Kaku Gothic New","IBM Plex Mono"]},"preview":{"light":{"bg":"#f7f4ec","fg":"#1b1a18","accent":"#b8232e","syntax":{"keyword":"#b8232e","string":"#6a6156","comment":"#726b5e","callable":"#433d35"}},"dark":{"bg":"#161514","fg":"#ece7db","accent":"#ea6a72","syntax":{"keyword":"#ee7a81","string":"#c8bfa6","comment":"#837d72","callable":"#f4efe4"}}}},"hasStylesheet":true},{"slug":"swissgrid","meta":{"schemaVersion":1,"slug":"swissgrid","name":"Swissgrid","description":"International Typographic Style — grid discipline, black on white, one hot Swiss-red accent, oversized section numbers.","mode":"light","version":"1.0.1","fonts":{"sans":"Inter","mono":"IBM Plex Mono","display":"Inter Tight","loaded":["Inter","Inter Tight","IBM Plex Mono"]},"preview":{"light":{"bg":"#ffffff","fg":"#0a0a0a","accent":"#e60000","syntax":{"keyword":"#d40000","string":"#3f3f3f","comment":"#6b6b6b","callable":"#0a0a0a"}},"dark":{"bg":"#0c0c0c","fg":"#f2f2f2","accent":"#ff4d4d","syntax":{"keyword":"#ff4d4d","string":"#c9c9c9","comment":"#8c8c8c","callable":"#ffffff"}}}},"hasStylesheet":true},{"slug":"tidepool","meta":{"schemaVersion":1,"slug":"tidepool","name":"Tidepool","description":"Marine teal dark — deep-sea teal surfaces, seafoam text, bioluminescent aqua accents, kelp-green success, coral danger. Light mode is the same pool at low tide.","mode":"dark","version":"1.0.0","fonts":{"sans":"Space Grotesk","mono":"JetBrains Mono","loaded":["Space Grotesk","JetBrains Mono"]},"preview":{"light":{"bg":"oklch(0.966 0.013 165)","fg":"oklch(0.27 0.045 200)","accent":"oklch(0.43 0.073 195)","syntax":{"keyword":"oklch(0.42 0.071 193)","string":"oklch(0.42 0.11 150)","comment":"oklch(0.44 0.03 200)","callable":"oklch(0.44 0.089 232)"}},"dark":{"bg":"oklch(0.235 0.032 205)","fg":"oklch(0.92 0.028 180)","accent":"oklch(0.82 0.13 185)","syntax":{"keyword":"oklch(0.84 0.14 183)","string":"oklch(0.83 0.14 152)","comment":"oklch(0.69 0.035 195)","callable":"oklch(0.8 0.1 228)"}}}},"hasStylesheet":true},{"slug":"timberline","meta":{"schemaVersion":1,"slug":"timberline","name":"Timberline","description":"Forest cabin after dark — pine & umber planks, cream prose, and matte lantern-amber accents; light mode is the same lodge at morning.","mode":"dark","version":"1.0.1","fonts":{"sans":"Source Sans 3","mono":"JetBrains Mono","display":"Fraunces","loaded":["Fraunces","Source Sans 3","JetBrains Mono"]},"preview":{"light":{"bg":"oklch(0.962 0.016 90)","fg":"oklch(0.27 0.032 155)","accent":"oklch(0.465 0.11 62)","syntax":{"keyword":"oklch(0.475 0.11 70)","string":"oklch(0.46 0.09 145)","comment":"oklch(0.49 0.035 140)","callable":"oklch(0.45 0.085 235)"}},"dark":{"bg":"oklch(0.207 0.02 60)","fg":"oklch(0.928 0.02 88)","accent":"oklch(0.78 0.125 72)","syntax":{"keyword":"oklch(0.8 0.125 80)","string":"oklch(0.79 0.1 135)","comment":"oklch(0.66 0.035 110)","callable":"oklch(0.78 0.075 220)"}}}},"hasStylesheet":true},{"slug":"washi","meta":{"schemaVersion":1,"slug":"washi","name":"Washi","description":"Warm washi paper, sumi ink, and ai-iro indigo seals — quiet Japanese stationery for calm documentation.","mode":"light","version":"1.0.3","fonts":{"sans":"Zen Kaku Gothic New","mono":"M PLUS 1 Code","display":"Shippori Mincho","loaded":["Zen Kaku Gothic New","Shippori Mincho","M PLUS 1 Code"]},"preview":{"light":{"bg":"#f6f1e4","fg":"#2b2822","accent":"#274a78","syntax":{"keyword":"#274a78","string":"#41653a","comment":"#6f6654","callable":"#963f34"}},"dark":{"bg":"#1b212c","fg":"#eae3d2","accent":"#8ab0dd","syntax":{"keyword":"#97b9e6","string":"#a9cb90","comment":"#8894a4","callable":"#e0a49a"}}}},"hasStylesheet":true}]};
6
+ const catalog = {"schemaVersion":2,"packs":[{"slug":"default","meta":{"schemaVersion":1,"slug":"default","name":"Default","description":"The stock zudo-doc look. No pack stylesheet is loaded — your project's configured color scheme and fonts render unchanged.","mode":"dark","version":"1.0.0","fonts":{"sans":"System","mono":"System","loaded":[]},"preview":{"light":{"bg":"oklch(0.965 0.004 65)","fg":"oklch(0.185 0.005 65)","accent":"oklch(0.470 0.120 56)","syntax":{"keyword":"oklch(0.470 0.120 56)","string":"oklch(0.470 0.140 145)","comment":"oklch(0.480 0.008 65)","callable":"oklch(0.485 0.122 245)"}},"dark":{"bg":"oklch(0.185 0.005 65)","fg":"oklch(0.965 0.004 65)","accent":"oklch(0.700 0.158 62)","syntax":{"keyword":"oklch(0.700 0.158 62)","string":"oklch(0.680 0.145 145)","comment":"oklch(0.705 0.008 65)","callable":"oklch(0.680 0.130 245)"}}}},"hasStylesheet":false},{"slug":"academia","meta":{"schemaVersion":1,"slug":"academia","name":"Academia","description":"A LaTeX paper for the web — white page, Computer-Modern-black ink, hyperref navy links, numbered theorem admonitions, booktabs tables. Dark mode is a scholarly slate.","mode":"light","version":"1.0.2","fonts":{"sans":"Source Serif 4","mono":"Courier Prime","loaded":["Source Serif 4","Courier Prime"]},"preview":{"light":{"bg":"#ffffff","fg":"#141414","accent":"#1c3d8f","syntax":{"keyword":"#1c3d8f","string":"#93262a","comment":"#2e6b2e","callable":"#295e80"}},"dark":{"bg":"#1d222b","fg":"#e7eaf0","accent":"#97b8ee","syntax":{"keyword":"#a3c0f2","string":"#e0a09a","comment":"#8cbc8c","callable":"#85c2da"}}}},"hasStylesheet":true},{"slug":"bauhaus","meta":{"schemaVersion":1,"slug":"bauhaus","name":"Bauhaus","description":"Primary triad as structure — gallery white, near-black ink, and red, yellow, blue doing structural work: circle, triangle, square markers, thick geometric rules, zero radius.","mode":"light","version":"1.0.0","fonts":{"sans":"Jost","mono":"Space Mono","display":"Archivo Black","loaded":["Jost","Archivo Black","Space Mono"]},"preview":{"light":{"bg":"#ffffff","fg":"#141414","accent":"#b3231b","syntax":{"keyword":"#b3231b","string":"#6d5800","comment":"#63615a","callable":"#1c4bbe"}},"dark":{"bg":"#18181b","fg":"#f0efe9","accent":"#ff7d70","syntax":{"keyword":"#ff7d70","string":"#e9ce6a","comment":"#999689","callable":"#8fb0ff"}}}},"hasStylesheet":true},{"slug":"beacon","meta":{"schemaVersion":1,"slug":"beacon","name":"Beacon","description":"WCAG-AAA high contrast — 7:1+ ink everywhere, 3px focus rings, always-underlined links. Clarity as beauty.","mode":"light","version":"1.0.1","fonts":{"sans":"Atkinson Hyperlegible","mono":"Atkinson Hyperlegible Mono","loaded":["Atkinson Hyperlegible","Atkinson Hyperlegible Mono"]},"preview":{"light":{"bg":"#ffffff","fg":"#000000","accent":"#0044cc","syntax":{"keyword":"#0044cc","string":"#a50000","comment":"#595959","callable":"#00287a"}},"dark":{"bg":"#000000","fg":"#ffffff","accent":"#6db3ff","syntax":{"keyword":"#6db3ff","string":"#ff8f8f","comment":"#b3b3b3","callable":"#a8d1ff"}}}},"hasStylesheet":true},{"slug":"blueprint","meta":{"schemaVersion":1,"slug":"blueprint","name":"Blueprint","description":"Cyanotype drafting sheet — pale-cyan linework on prussian grid paper, title-block caps, dashed rules, mono-caps labels. Light mode is the vellum original.","mode":"dark","version":"1.0.1","fonts":{"sans":"Inter","mono":"IBM Plex Mono","display":"Josefin Sans","loaded":["Inter","Josefin Sans","IBM Plex Mono"]},"preview":{"light":{"bg":"oklch(0.965 0.012 235)","fg":"oklch(0.28 0.05 253)","accent":"oklch(0.44 0.095 235)","syntax":{"keyword":"oklch(0.46 0.1 80)","string":"oklch(0.43 0.085 195)","comment":"oklch(0.5 0.03 248)","callable":"oklch(0.44 0.09 235)"}},"dark":{"bg":"oklch(0.27 0.055 252)","fg":"oklch(0.965 0.01 220)","accent":"oklch(0.86 0.09 205)","syntax":{"keyword":"oklch(0.87 0.12 95)","string":"oklch(0.83 0.1 185)","comment":"oklch(0.72 0.032 235)","callable":"oklch(0.83 0.085 215)"}}}},"hasStylesheet":true},{"slug":"botanica","meta":{"schemaVersion":1,"slug":"botanica","name":"Botanica","description":"A vintage botanical plate — herbarium cream, deep green ink, engraved double rules, Latin small-caps plate labels.","mode":"light","version":"1.0.0","fonts":{"sans":"Crimson Pro","mono":"Courier Prime","display":"Cormorant Garamond","loaded":["Cormorant Garamond","Crimson Pro","Courier Prime"]},"preview":{"light":{"bg":"oklch(0.956 0.02 92)","fg":"oklch(0.29 0.03 95)","accent":"oklch(0.42 0.09 152)","syntax":{"keyword":"oklch(0.4 0.075 60)","string":"oklch(0.43 0.095 150)","comment":"oklch(0.48 0.04 95)","callable":"oklch(0.43 0.075 205)"}},"dark":{"bg":"oklch(0.215 0.022 160)","fg":"oklch(0.92 0.02 95)","accent":"oklch(0.78 0.09 148)","syntax":{"keyword":"oklch(0.82 0.085 80)","string":"oklch(0.8 0.095 145)","comment":"oklch(0.7 0.03 110)","callable":"oklch(0.78 0.075 200)"}}}},"hasStylesheet":true},{"slug":"broadsheet","meta":{"schemaVersion":1,"slug":"broadsheet","name":"Broadsheet","description":"Newspaper editorial — Playfair masthead, Oxford ink rules, a red drop cap, and a night-edition dark mode.","mode":"light","version":"1.0.2","fonts":{"sans":"Source Serif 4","mono":"Courier Prime","display":"Playfair Display","loaded":["Playfair Display","Source Serif 4","Courier Prime"]},"preview":{"light":{"bg":"oklch(0.955 0.012 92)","fg":"oklch(0.205 0.012 80)","accent":"oklch(0.455 0.155 27)","syntax":{"keyword":"oklch(0.445 0.155 27)","string":"oklch(0.45 0.075 60)","comment":"oklch(0.5 0.016 78)","callable":"oklch(0.42 0.09 252)"}},"dark":{"bg":"oklch(0.185 0.012 75)","fg":"oklch(0.935 0.015 90)","accent":"oklch(0.7 0.145 27)","syntax":{"keyword":"oklch(0.72 0.14 27)","string":"oklch(0.79 0.07 70)","comment":"oklch(0.7 0.02 85)","callable":"oklch(0.74 0.08 248)"}}}},"hasStylesheet":true},{"slug":"brutalist","meta":{"schemaVersion":1,"slug":"brutalist","name":"Brutalist","description":"Raw concrete web — stark black on white, 4px slab borders, zero radius, hazard-orange tape. Structure you can see.","mode":"light","version":"1.0.3","fonts":{"sans":"Space Grotesk","mono":"IBM Plex Mono","display":"Archivo Black","loaded":["Archivo Black","Space Grotesk","IBM Plex Mono"]},"preview":{"light":{"bg":"#ffffff","fg":"#000000","accent":"#c2410c","syntax":{"keyword":"#c2410c","string":"#9a3412","comment":"#767676","callable":"#000000"}},"dark":{"bg":"#0d0d0d","fg":"#f5f5f5","accent":"#ff6a1f","syntax":{"keyword":"#ff7a2e","string":"#ffb38a","comment":"#878787","callable":"#ffffff"}}}},"hasStylesheet":true},{"slug":"drift","meta":{"schemaVersion":1,"slug":"drift","name":"Drift","description":"Floaty slate-blue comfort dark for long reading — soft glows in place of borders, gentle elevation, relaxed Plex type.","mode":"dark","version":"1.0.2","fonts":{"sans":"IBM Plex Sans","mono":"IBM Plex Mono","loaded":["IBM Plex Sans","IBM Plex Mono"]},"preview":{"light":{"bg":"#edf2f6","fg":"#24303d","accent":"#236799","syntax":{"keyword":"#49539c","string":"#2b754f","comment":"#596673","callable":"#00688d"}},"dark":{"bg":"#181f26","fg":"#d6dee4","accent":"#6cafd9","syntax":{"keyword":"#9caeee","string":"#88cba4","comment":"#8494a4","callable":"#75c6e1"}}}},"hasStylesheet":true},{"slug":"eink","meta":{"schemaVersion":1,"slug":"eink","name":"E-Ink","description":"E-reader grayscale — warm paper, near-black ink, shadowless hairline chrome, zero radius. Code is told by weight, slant, and gray value; only diffs keep a trace of ink-blue and ink-red.","mode":"light","version":"1.0.0","fonts":{"sans":"EB Garamond","mono":"Courier Prime","loaded":["EB Garamond","Courier Prime"]},"preview":{"light":{"bg":"oklch(0.956 0.007 90)","fg":"oklch(0.24 0.012 85)","accent":"oklch(0.155 0.01 85)","syntax":{"keyword":"oklch(0.18 0.01 85)","string":"oklch(0.46 0.012 85)","comment":"oklch(0.505 0.01 85)","callable":"oklch(0.31 0.012 85)"}},"dark":{"bg":"oklch(0.215 0.008 80)","fg":"oklch(0.885 0.012 85)","accent":"oklch(0.955 0.008 85)","syntax":{"keyword":"oklch(0.96 0.008 85)","string":"oklch(0.715 0.012 85)","comment":"oklch(0.645 0.01 85)","callable":"oklch(0.82 0.01 85)"}}}},"hasStylesheet":true},{"slug":"fjord","meta":{"schemaVersion":1,"slug":"fjord","name":"Fjord","description":"Polar-night blue under a faint aurora — frost-cyan accents, icy borders, snow-white text. Cold nordic calm.","mode":"dark","version":"1.0.2","fonts":{"sans":"Inter","mono":"JetBrains Mono","loaded":["Inter","JetBrains Mono"]},"preview":{"light":{"bg":"oklch(0.971 0.008 230)","fg":"oklch(0.255 0.03 255)","accent":"oklch(0.455 0.092 205)","syntax":{"keyword":"oklch(0.455 0.105 250)","string":"oklch(0.495 0.105 165)","comment":"oklch(0.51 0.03 245)","callable":"oklch(0.445 0.098 202)"}},"dark":{"bg":"oklch(0.232 0.024 255)","fg":"oklch(0.93 0.012 235)","accent":"oklch(0.79 0.095 195)","syntax":{"keyword":"oklch(0.76 0.095 242)","string":"oklch(0.8 0.105 158)","comment":"oklch(0.63 0.03 245)","callable":"oklch(0.815 0.095 195)"}}}},"hasStylesheet":true},{"slug":"foundry","meta":{"schemaVersion":1,"slug":"foundry","name":"Foundry","description":"GitHub-neutral baseline — white paper, near-black ink, Primer-blue links, quiet gray rules. Restraint, perfected.","mode":"light","version":"1.0.2","fonts":{"sans":"Inter","mono":"System","loaded":["Inter"]},"preview":{"light":{"bg":"#ffffff","fg":"#1f2328","accent":"#0969da","syntax":{"keyword":"#cf222e","string":"#0a3069","comment":"#6e7781","callable":"#8250df"}},"dark":{"bg":"#0d1117","fg":"#e6edf3","accent":"#4493f8","syntax":{"keyword":"#ff7b72","string":"#a5d6ff","comment":"#8b949e","callable":"#d2a8ff"}}}},"hasStylesheet":true},{"slug":"futura-editorial","meta":{"schemaVersion":1,"slug":"futura-editorial","name":"Futura Editorial","description":"Normal-weight geometric Futura headings over Noto Sans body — clean white, near-black ink, one restrained red accent.","mode":"light","version":"1.0.1","fonts":{"sans":"Noto Sans","mono":"Space Mono","display":"Jost","loaded":["Jost","Noto Sans","Space Mono"]},"preview":{"light":{"bg":"oklch(1 0 0)","fg":"oklch(0.17 0 0)","accent":"oklch(0.505 0.18 28)","syntax":{"keyword":"oklch(0.505 0.18 28)","string":"oklch(0.47 0.09 160)","comment":"oklch(0.52 0 0)","callable":"oklch(0.45 0.1 250)"}},"dark":{"bg":"oklch(0.165 0 0)","fg":"oklch(0.935 0 0)","accent":"oklch(0.72 0.15 29)","syntax":{"keyword":"oklch(0.72 0.15 29)","string":"oklch(0.76 0.1 160)","comment":"oklch(0.63 0 0)","callable":"oklch(0.73 0.095 250)"}}}},"hasStylesheet":true},{"slug":"hearth","meta":{"schemaVersion":1,"slug":"hearth","name":"Hearth","description":"Warm cream & brick-red fireside docs — Fraunces headings, amber code blocks, ember-glow dark mode.","mode":"light","version":"1.0.3","fonts":{"sans":"Nunito","mono":"Spline Sans Mono","display":"Fraunces","loaded":["Fraunces","Nunito","Spline Sans Mono"]},"preview":{"light":{"bg":"oklch(0.958 0.02 85)","fg":"oklch(0.255 0.028 40)","accent":"oklch(0.49 0.145 31)","syntax":{"keyword":"oklch(0.48 0.148 30)","string":"oklch(0.47 0.1 135)","comment":"oklch(0.51 0.035 60)","callable":"oklch(0.45 0.092 245)"}},"dark":{"bg":"oklch(0.215 0.022 45)","fg":"oklch(0.915 0.02 80)","accent":"oklch(0.735 0.128 45)","syntax":{"keyword":"oklch(0.745 0.138 40)","string":"oklch(0.785 0.1 130)","comment":"oklch(0.645 0.032 65)","callable":"oklch(0.765 0.075 235)"}}}},"hasStylesheet":true},{"slug":"hollow","meta":{"schemaVersion":1,"slug":"hollow","name":"Hollow","description":"Dark violet space — neon pink headings, violet links, a quiet starfield, and razor-square midnight chrome.","mode":"dark","version":"1.0.2","fonts":{"sans":"Space Grotesk","mono":"Space Mono","loaded":["Space Grotesk","Space Mono"]},"preview":{"light":{"bg":"oklch(0.962 0.012 300)","fg":"oklch(0.24 0.045 295)","accent":"oklch(0.45 0.17 300)","syntax":{"keyword":"oklch(0.53 0.2 348)","string":"oklch(0.45 0.1 195)","comment":"oklch(0.51 0.035 292)","callable":"oklch(0.46 0.16 300)"}},"dark":{"bg":"oklch(0.155 0.035 295)","fg":"oklch(0.93 0.015 295)","accent":"oklch(0.74 0.14 300)","syntax":{"keyword":"oklch(0.76 0.17 347)","string":"oklch(0.83 0.1 190)","comment":"oklch(0.62 0.045 292)","callable":"oklch(0.76 0.13 300)"}}}},"hasStylesheet":true},{"slug":"ledger","meta":{"schemaVersion":1,"slug":"ledger","name":"Ledger","description":"Cream academic serif in the Tufte tradition — warm paper, ink prose, oxblood links, hairline rules, quiet code.","mode":"light","version":"1.0.1","fonts":{"sans":"Crimson Pro","mono":"IBM Plex Mono","loaded":["Crimson Pro","IBM Plex Mono"]},"preview":{"light":{"bg":"#fffff8","fg":"#16130e","accent":"#8b1a1a","syntax":{"keyword":"#8b1a1a","string":"#4e6328","comment":"#766b5c","callable":"#33586e"}},"dark":{"bg":"#191510","fg":"#e9e2d0","accent":"#d68a75","syntax":{"keyword":"#e2907c","string":"#a8b47c","comment":"#948a77","callable":"#93b4cd"}}}},"hasStylesheet":true},{"slug":"manuscript","meta":{"schemaVersion":1,"slug":"manuscript","name":"Manuscript","description":"A quiet Garamond book page — warm paper, soft ink, sepia rubrication, small-caps subheads, fleuron section marks.","mode":"light","version":"1.0.1","fonts":{"sans":"EB Garamond","mono":"Courier Prime","loaded":["EB Garamond","Courier Prime"]},"preview":{"light":{"bg":"oklch(0.963 0.013 85)","fg":"oklch(0.245 0.018 60)","accent":"oklch(0.44 0.095 55)","syntax":{"keyword":"oklch(0.455 0.13 30)","string":"oklch(0.44 0.085 150)","comment":"oklch(0.5 0.03 70)","callable":"oklch(0.435 0.09 275)"}},"dark":{"bg":"oklch(0.215 0.015 65)","fg":"oklch(0.885 0.022 80)","accent":"oklch(0.735 0.095 70)","syntax":{"keyword":"oklch(0.72 0.115 35)","string":"oklch(0.745 0.085 150)","comment":"oklch(0.645 0.03 75)","callable":"oklch(0.745 0.07 275)"}}}},"hasStylesheet":true},{"slug":"matcha","meta":{"schemaVersion":1,"slug":"matcha","name":"Matcha","description":"Green tea ceremony — deep matcha on warm cream, mincho headings, zen whitespace, tea-foam surfaces in both modes.","mode":"light","version":"1.0.2","fonts":{"sans":"Zen Kaku Gothic New","mono":"M PLUS 1 Code","display":"Shippori Mincho","loaded":["Zen Kaku Gothic New","Shippori Mincho","M PLUS 1 Code"]},"preview":{"light":{"bg":"oklch(0.973 0.012 106)","fg":"oklch(0.245 0.018 148)","accent":"oklch(0.435 0.105 142)","syntax":{"keyword":"oklch(0.418 0.105 150)","string":"oklch(0.47 0.092 106)","comment":"oklch(0.512 0.034 130)","callable":"oklch(0.438 0.082 196)"}},"dark":{"bg":"oklch(0.205 0.016 152)","fg":"oklch(0.938 0.018 106)","accent":"oklch(0.79 0.115 138)","syntax":{"keyword":"oklch(0.782 0.118 148)","string":"oklch(0.822 0.102 108)","comment":"oklch(0.668 0.034 130)","callable":"oklch(0.772 0.088 192)"}}}},"hasStylesheet":true},{"slug":"nocturne","meta":{"schemaVersion":1,"slug":"nocturne","name":"Nocturne","description":"Purple midnight — velvet aubergine depths, lavender links, and muted gold hairlines. Elegant, never neon.","mode":"dark","version":"1.0.2","fonts":{"sans":"Manrope","mono":"IBM Plex Mono","display":"Cormorant Garamond","loaded":["Cormorant Garamond","Manrope","IBM Plex Mono"]},"preview":{"light":{"bg":"oklch(0.958 0.012 300)","fg":"oklch(0.245 0.05 312)","accent":"oklch(0.455 0.15 295)","syntax":{"keyword":"oklch(0.46 0.16 295)","string":"oklch(0.5 0.105 85)","comment":"oklch(0.5 0.042 305)","callable":"oklch(0.455 0.125 265)"}},"dark":{"bg":"oklch(0.155 0.03 310)","fg":"oklch(0.922 0.018 300)","accent":"oklch(0.765 0.105 292)","syntax":{"keyword":"oklch(0.772 0.128 293)","string":"oklch(0.8 0.108 88)","comment":"oklch(0.645 0.045 302)","callable":"oklch(0.77 0.095 262)"}}}},"hasStylesheet":true},{"slug":"observatory","meta":{"schemaVersion":1,"slug":"observatory","name":"Observatory","description":"Night-sky atlas: star-field depth, constellation-line rules, nebula violet and comet gold over astronomer's navy.","mode":"dark","version":"1.0.3","fonts":{"sans":"Jost","mono":"Space Mono","display":"Josefin Sans","loaded":["Josefin Sans","Jost","Space Mono"]},"preview":{"light":{"bg":"oklch(0.965 0.012 255)","fg":"oklch(0.235 0.045 268)","accent":"oklch(0.47 0.1 75)","syntax":{"keyword":"oklch(0.46 0.14 300)","string":"oklch(0.45 0.1 162)","comment":"oklch(0.5 0.03 265)","callable":"oklch(0.46 0.11 250)"}},"dark":{"bg":"oklch(0.155 0.028 268)","fg":"oklch(0.93 0.015 255)","accent":"oklch(0.82 0.13 86)","syntax":{"keyword":"oklch(0.76 0.118 300)","string":"oklch(0.8 0.112 162)","comment":"oklch(0.61 0.038 265)","callable":"oklch(0.78 0.095 240)"}}}},"hasStylesheet":true},{"slug":"onyx","meta":{"schemaVersion":1,"slug":"onyx","name":"Onyx","description":"Luxury noir — jet black, champagne serif headings, and a single gold hairline accent. Watch-brand restraint.","mode":"dark","version":"1.0.3","fonts":{"sans":"Jost","mono":"IBM Plex Mono","display":"Cormorant Garamond","loaded":["Cormorant Garamond","Jost","IBM Plex Mono"]},"preview":{"light":{"bg":"oklch(0.972 0.005 85)","fg":"oklch(0.22 0.012 80)","accent":"oklch(0.5 0.105 80)","syntax":{"keyword":"oklch(0.51 0.105 82)","string":"oklch(0.47 0.07 155)","comment":"oklch(0.5 0.015 85)","callable":"oklch(0.45 0.045 250)"}},"dark":{"bg":"oklch(0.145 0.003 80)","fg":"oklch(0.92 0.022 88)","accent":"oklch(0.79 0.115 88)","syntax":{"keyword":"oklch(0.8 0.115 88)","string":"oklch(0.78 0.07 155)","comment":"oklch(0.62 0.02 85)","callable":"oklch(0.82 0.04 250)"}}}},"hasStylesheet":true},{"slug":"phosphor","meta":{"schemaVersion":1,"slug":"phosphor","name":"Phosphor","description":"Green CRT terminal — phosphor glow, scanlines, inverse-video nav, monospace everything.","mode":"dark","version":"1.0.1","fonts":{"sans":"JetBrains Mono","mono":"JetBrains Mono","display":"VT323","loaded":["VT323","JetBrains Mono"]},"preview":{"light":{"bg":"oklch(0.955 0.014 150)","fg":"oklch(0.28 0.058 150)","accent":"oklch(0.42 0.13 150)","syntax":{"keyword":"oklch(0.4 0.13 148)","string":"oklch(0.39 0.095 165)","comment":"oklch(0.49 0.05 150)","callable":"oklch(0.43 0.095 200)"}},"dark":{"bg":"oklch(0.135 0.014 150)","fg":"oklch(0.845 0.145 150)","accent":"oklch(0.87 0.215 148)","syntax":{"keyword":"oklch(0.9 0.21 148)","string":"oklch(0.9 0.12 165)","comment":"oklch(0.58 0.062 150)","callable":"oklch(0.83 0.125 200)"}}}},"hasStylesheet":true},{"slug":"riso","meta":{"schemaVersion":1,"slug":"riso","name":"Riso","description":"Risograph duotone print — warm paper, two inks (riso blue + fluorescent coral), misregistered offset shadows, and a soft linear paper grain.","mode":"light","version":"1.0.0","fonts":{"sans":"Space Grotesk","mono":"Space Mono","display":"Archivo Black","loaded":["Space Grotesk","Archivo Black","Space Mono"]},"preview":{"light":{"bg":"#f9f3e5","fg":"#1d3345","accent":"#0d5c94","syntax":{"keyword":"#a02310","string":"#0d5e6e","comment":"#4d5c66","callable":"#0b5b9b"}},"dark":{"bg":"#101d28","fg":"#f1e9d8","accent":"#58b8e8","syntax":{"keyword":"#ff8d72","string":"#66d4cf","comment":"#93a4ad","callable":"#5aaef5"}}}},"hasStylesheet":true},{"slug":"sakura","meta":{"schemaVersion":1,"slug":"sakura","name":"Sakura","description":"Cherry-blossom pastels — blush-white paper, plum ink, and rose accents on petal-soft corners; dark mode is yozakura, night blossoms under lantern-pink light.","mode":"light","version":"1.0.1","fonts":{"sans":"Nunito","mono":"M PLUS 1 Code","display":"Shippori Mincho","loaded":["Nunito","Shippori Mincho","M PLUS 1 Code"]},"preview":{"light":{"bg":"oklch(0.977 0.009 350)","fg":"oklch(0.29 0.055 340)","accent":"oklch(0.5 0.168 358)","syntax":{"keyword":"oklch(0.5 0.168 358)","string":"oklch(0.49 0.115 150)","comment":"oklch(0.5 0.04 340)","callable":"oklch(0.49 0.135 300)"}},"dark":{"bg":"oklch(0.215 0.03 330)","fg":"oklch(0.935 0.014 350)","accent":"oklch(0.78 0.115 355)","syntax":{"keyword":"oklch(0.79 0.125 355)","string":"oklch(0.78 0.115 150)","comment":"oklch(0.71 0.038 345)","callable":"oklch(0.78 0.105 300)"}}}},"hasStylesheet":true},{"slug":"scandi","meta":{"schemaVersion":1,"slug":"scandi","name":"Scandi","description":"Hygge minimalism — oat linen, sage & clay, pill nav actives and 8px corners; night mode is warm charcoal lit by candle amber.","mode":"light","version":"1.0.0","fonts":{"sans":"Manrope","mono":"Spline Sans Mono","loaded":["Manrope","Spline Sans Mono"]},"preview":{"light":{"bg":"oklch(0.963 0.012 83)","fg":"oklch(0.28 0.02 55)","accent":"oklch(0.44 0.08 152)","syntax":{"keyword":"oklch(0.47 0.105 40)","string":"oklch(0.45 0.09 150)","comment":"oklch(0.5 0.02 70)","callable":"oklch(0.45 0.08 240)"}},"dark":{"bg":"oklch(0.235 0.015 55)","fg":"oklch(0.92 0.012 85)","accent":"oklch(0.82 0.105 75)","syntax":{"keyword":"oklch(0.76 0.1 45)","string":"oklch(0.78 0.09 150)","comment":"oklch(0.66 0.02 75)","callable":"oklch(0.75 0.075 238)"}}}},"hasStylesheet":true},{"slug":"solar","meta":{"schemaVersion":1,"slug":"solar","name":"Solar","description":"Solarized precision — low-eyestrain paper tones, blue/cyan/orange accents, mono labels, technical calm in both modes.","mode":"light","version":"1.0.1","fonts":{"sans":"Source Sans 3","mono":"Source Code Pro","loaded":["Source Sans 3","Source Code Pro"]},"preview":{"light":{"bg":"#fdf6e3","fg":"#4f6369","accent":"#1c6a9f","syntax":{"keyword":"#606f00","string":"#12746c","comment":"#586b72","callable":"#1d6da3"}},"dark":{"bg":"#002b36","fg":"#93a1a1","accent":"#53a6df","syntax":{"keyword":"#8ea300","string":"#2caaa0","comment":"#899da4","callable":"#49a0dd"}}}},"hasStylesheet":true},{"slug":"sumi","meta":{"schemaVersion":1,"slug":"sumi","name":"Sumi","description":"Sumi-e ink on washi — bold mincho brush headings, deep black strokes, one vermillion hanko seal accent.","mode":"light","version":"1.0.3","fonts":{"sans":"Zen Kaku Gothic New","mono":"IBM Plex Mono","display":"Shippori Mincho B1","loaded":["Shippori Mincho B1","Zen Kaku Gothic New","IBM Plex Mono"]},"preview":{"light":{"bg":"#f7f4ec","fg":"#1b1a18","accent":"#b8232e","syntax":{"keyword":"#b8232e","string":"#6a6156","comment":"#726b5e","callable":"#433d35"}},"dark":{"bg":"#161514","fg":"#ece7db","accent":"#ea6a72","syntax":{"keyword":"#ee7a81","string":"#c8bfa6","comment":"#837d72","callable":"#f4efe4"}}}},"hasStylesheet":true},{"slug":"swissgrid","meta":{"schemaVersion":1,"slug":"swissgrid","name":"Swissgrid","description":"International Typographic Style — grid discipline, black on white, one hot Swiss-red accent, oversized section numbers.","mode":"light","version":"1.0.1","fonts":{"sans":"Inter","mono":"IBM Plex Mono","display":"Inter Tight","loaded":["Inter","Inter Tight","IBM Plex Mono"]},"preview":{"light":{"bg":"#ffffff","fg":"#0a0a0a","accent":"#e60000","syntax":{"keyword":"#d40000","string":"#3f3f3f","comment":"#6b6b6b","callable":"#0a0a0a"}},"dark":{"bg":"#0c0c0c","fg":"#f2f2f2","accent":"#ff4d4d","syntax":{"keyword":"#ff4d4d","string":"#c9c9c9","comment":"#8c8c8c","callable":"#ffffff"}}}},"hasStylesheet":true},{"slug":"tidepool","meta":{"schemaVersion":1,"slug":"tidepool","name":"Tidepool","description":"Marine teal dark — deep-sea teal surfaces, seafoam text, bioluminescent aqua accents, kelp-green success, coral danger. Light mode is the same pool at low tide.","mode":"dark","version":"1.0.0","fonts":{"sans":"Space Grotesk","mono":"JetBrains Mono","loaded":["Space Grotesk","JetBrains Mono"]},"preview":{"light":{"bg":"oklch(0.966 0.013 165)","fg":"oklch(0.27 0.045 200)","accent":"oklch(0.43 0.073 195)","syntax":{"keyword":"oklch(0.42 0.071 193)","string":"oklch(0.42 0.11 150)","comment":"oklch(0.44 0.03 200)","callable":"oklch(0.44 0.089 232)"}},"dark":{"bg":"oklch(0.235 0.032 205)","fg":"oklch(0.92 0.028 180)","accent":"oklch(0.82 0.13 185)","syntax":{"keyword":"oklch(0.84 0.14 183)","string":"oklch(0.83 0.14 152)","comment":"oklch(0.69 0.035 195)","callable":"oklch(0.8 0.1 228)"}}}},"hasStylesheet":true},{"slug":"timberline","meta":{"schemaVersion":1,"slug":"timberline","name":"Timberline","description":"Forest cabin after dark — pine & umber planks, cream prose, and matte lantern-amber accents; light mode is the same lodge at morning.","mode":"dark","version":"1.0.1","fonts":{"sans":"Source Sans 3","mono":"JetBrains Mono","display":"Fraunces","loaded":["Fraunces","Source Sans 3","JetBrains Mono"]},"preview":{"light":{"bg":"oklch(0.962 0.016 90)","fg":"oklch(0.27 0.032 155)","accent":"oklch(0.465 0.11 62)","syntax":{"keyword":"oklch(0.475 0.11 70)","string":"oklch(0.46 0.09 145)","comment":"oklch(0.49 0.035 140)","callable":"oklch(0.45 0.085 235)"}},"dark":{"bg":"oklch(0.207 0.02 60)","fg":"oklch(0.928 0.02 88)","accent":"oklch(0.78 0.125 72)","syntax":{"keyword":"oklch(0.8 0.125 80)","string":"oklch(0.79 0.1 135)","comment":"oklch(0.66 0.035 110)","callable":"oklch(0.78 0.075 220)"}}}},"hasStylesheet":true},{"slug":"washi","meta":{"schemaVersion":1,"slug":"washi","name":"Washi","description":"Warm washi paper, sumi ink, and ai-iro indigo seals — quiet Japanese stationery for calm documentation.","mode":"light","version":"1.0.3","fonts":{"sans":"Zen Kaku Gothic New","mono":"M PLUS 1 Code","display":"Shippori Mincho","loaded":["Zen Kaku Gothic New","Shippori Mincho","M PLUS 1 Code"]},"preview":{"light":{"bg":"#f6f1e4","fg":"#2b2822","accent":"#274a78","syntax":{"keyword":"#274a78","string":"#41653a","comment":"#6f6654","callable":"#963f34"}},"dark":{"bg":"#1b212c","fg":"#eae3d2","accent":"#8ab0dd","syntax":{"keyword":"#97b9e6","string":"#a9cb90","comment":"#8894a4","callable":"#e0a49a"}}}},"hasStylesheet":true}]};
7
7
 
8
8
  /**
9
9
  * Validate the browser-facing catalog contract before consuming its entries.
package/dist/compiled.css CHANGED
@@ -2770,7 +2770,6 @@ a.text-accent.underline {
2770
2770
  background: none;
2771
2771
  }
2772
2772
  .zd-compact-prose :where(h2) {
2773
- font-size: 1.3rem;
2774
2773
  --flow-space: 1.75rem;
2775
2774
  }
2776
2775
  .zd-compact-prose :where(h3) {
@@ -2799,6 +2798,18 @@ a.text-accent.underline {
2799
2798
  max-inline-size: 100%;
2800
2799
  block-size: auto;
2801
2800
  }
2801
+ .zd-content h2.zd-home-heading, html[data-theme-pack] .zd-content h2.zd-home-heading {
2802
+ font-size: var(--text-title);
2803
+ line-height: var(--leading-tight);
2804
+ padding: 0;
2805
+ border: 0;
2806
+ background: none;
2807
+ position: static;
2808
+ counter-increment: none;
2809
+ }
2810
+ .zd-content h2.zd-home-heading::before, .zd-content h2.zd-home-heading::after, html[data-theme-pack] .zd-content h2.zd-home-heading::before, html[data-theme-pack] .zd-content h2.zd-home-heading::after {
2811
+ content: none;
2812
+ }
2802
2813
  .page-loading-overlay {
2803
2814
  position: fixed;
2804
2815
  inset: 0;
package/dist/content.css CHANGED
@@ -580,7 +580,10 @@ a.text-accent.underline {
580
580
  border: 0;
581
581
  background: none;
582
582
  }
583
- .zd-compact-prose :where(h2) { font-size: 1.3rem; --flow-space: 1.75rem; }
583
+ /* The h2 look (size, weight, no decoration) is owned by `.zd-home-heading`
584
+ * below — shared with the sitemap and Tags headings; only the flow gap lives
585
+ * here. */
586
+ .zd-compact-prose :where(h2) { --flow-space: 1.75rem; }
584
587
  .zd-compact-prose :where(h3) { font-size: 1.125rem; --flow-space: 1.5rem; }
585
588
  .zd-compact-prose :where(h4) { font-size: 1rem; --flow-space: 1.25rem; }
586
589
  .zd-compact-prose :where(h5, h6) { font-size: .9375rem; --flow-space: 1.25rem; }
@@ -588,3 +591,30 @@ a.text-accent.underline {
588
591
  .zd-compact-prose > :first-child { margin-top: 0; }
589
592
  .zd-compact-prose :where(pre) { max-inline-size: 100%; overflow-x: auto; }
590
593
  .zd-compact-prose :where(img) { max-inline-size: 100%; block-size: auto; }
594
+
595
+ /* Home-page section headings (#4194). The intro heading (compact prose), the
596
+ * sitemap heading and the Tags heading share one look: bold, one step above
597
+ * body size, and NO rule / counter / bar decoration — the home page already
598
+ * separates its sections with `.zd-home-rule` dividers, so a theme pack's h2
599
+ * top-rule would double up against them. Theme packs style
600
+ * `html[data-theme-pack="x"] .zd-content h2` at (0,2,2); the second selector
601
+ * here is (0,3,2) so it wins without `!important`. A pack's
602
+ * `border-image … !important` is defeated by the zero border width: a
603
+ * border-image paints nothing into a zero-width border box. */
604
+ .zd-content h2.zd-home-heading,
605
+ html[data-theme-pack] .zd-content h2.zd-home-heading {
606
+ font-size: var(--text-title);
607
+ line-height: var(--leading-tight);
608
+ padding: 0;
609
+ border: 0;
610
+ background: none;
611
+ position: static;
612
+ counter-increment: none;
613
+ }
614
+
615
+ .zd-content h2.zd-home-heading::before,
616
+ .zd-content h2.zd-home-heading::after,
617
+ html[data-theme-pack] .zd-content h2.zd-home-heading::before,
618
+ html[data-theme-pack] .zd-content h2.zd-home-heading::after {
619
+ content: none;
620
+ }
@@ -4,6 +4,15 @@ import { h } from "preact";
4
4
  import type { PreparedHomeIntro } from "./types.js";
5
5
  export type { IntroNode, PreparedHomeIntro, PreparedHomeIntros } from "./types.js";
6
6
  export { resolveHomeIntro } from "./resolve.js";
7
+ /**
8
+ * Class list shared by every home-page section heading (#4194): the compact
9
+ * intro h2 rendered here, and the sitemap / Tags headings in `home-page`.
10
+ * `zd-home-heading` is the hook content.css uses to strip theme-pack h2
11
+ * decoration (rules, counters, bars) — the home page already separates its
12
+ * sections with `.zd-home-rule` dividers, so a pack's h2 top-rule would
13
+ * double up against them.
14
+ */
15
+ export declare const HOME_SECTION_HEADING_CLASS = "zd-home-heading text-title font-bold leading-tight";
7
16
  /** Synchronous SSR view of output produced by home-intro/prepare. No raw HTML sink. */
8
17
  export declare function CompactProse({ intro }: {
9
18
  intro: PreparedHomeIntro | null | undefined;
@@ -3,8 +3,14 @@ import { h } from "preact";
3
3
  import { defaultComponents } from "../content/index.js";
4
4
  import { makeAdmonition } from "../content-admonition/index.js";
5
5
  import { resolveHomeIntro } from "./resolve.js";
6
+ const HOME_SECTION_HEADING_CLASS = "zd-home-heading text-title font-bold leading-tight";
6
7
  const { h2: _h2, h3: _h3, h4: _h4, ...typography } = defaultComponents;
7
8
  const components = { ...typography };
9
+ components.h2 = ({ class: klass, className, children, ...rest }) => h(
10
+ "h2",
11
+ { ...rest, class: [HOME_SECTION_HEADING_CLASS, klass, className].filter(Boolean).join(" ") },
12
+ children
13
+ );
8
14
  for (const variant of ["note", "tip", "info", "warning", "danger", "caution", "important"]) components[variant] = makeAdmonition(variant);
9
15
  function renderNode(node) {
10
16
  if (typeof node === "string") return node;
@@ -16,5 +22,6 @@ function CompactProse({ intro }) {
16
22
  }
17
23
  export {
18
24
  CompactProse,
25
+ HOME_SECTION_HEADING_CLASS,
19
26
  resolveHomeIntro
20
27
  };