getfilepress 0.0.0 → 0.1.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.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +178 -4
  3. package/package.json +89 -7
  4. package/packages/app/package.json +31 -0
  5. package/packages/app/src/app.d.ts +12 -0
  6. package/packages/app/src/app.html +12 -0
  7. package/packages/app/src/critical-theme.d.ts +4 -0
  8. package/packages/app/src/lib/content.server.ts +8 -0
  9. package/packages/app/src/lib/empty-theme.css +1 -0
  10. package/packages/app/src/lib/genie/GenieHost.svelte +16 -0
  11. package/packages/app/src/lib/genie/GeniePanel.svelte +429 -0
  12. package/packages/app/src/lib/genie/ops.ts +237 -0
  13. package/packages/app/src/lib/genie/store.ts +217 -0
  14. package/packages/app/src/lib/genie/types.ts +61 -0
  15. package/packages/app/src/lib/pages.server.ts +7 -0
  16. package/packages/app/src/lib/site.server.ts +52 -0
  17. package/packages/app/src/lib/theme-entry.ts +7 -0
  18. package/packages/app/src/routes/+layout.svelte +29 -0
  19. package/packages/app/src/routes/+layout.ts +10 -0
  20. package/packages/app/src/routes/+page.server.ts +28 -0
  21. package/packages/app/src/routes/+page.svelte +67 -0
  22. package/packages/app/src/routes/[slug]/+page.server.ts +20 -0
  23. package/packages/app/src/routes/[slug]/+page.svelte +47 -0
  24. package/packages/app/src/routes/page/[n]/+page.server.ts +21 -0
  25. package/packages/app/src/routes/page/[n]/+page.svelte +36 -0
  26. package/packages/app/src/routes/posts/[slug]/+page.server.ts +24 -0
  27. package/packages/app/src/routes/posts/[slug]/+page.svelte +95 -0
  28. package/packages/app/src/routes/robots.txt/+server.ts +11 -0
  29. package/packages/app/src/routes/rss.xml/+server.ts +13 -0
  30. package/packages/app/src/routes/sitemap.xml/+server.ts +19 -0
  31. package/packages/app/src/routes/tags/+page.server.ts +4 -0
  32. package/packages/app/src/routes/tags/+page.svelte +26 -0
  33. package/packages/app/src/routes/tags/[tag]/+page.server.ts +15 -0
  34. package/packages/app/src/routes/tags/[tag]/+page.svelte +23 -0
  35. package/packages/app/src/routes/topics/+page.server.ts +28 -0
  36. package/packages/app/src/routes/topics/+page.svelte +47 -0
  37. package/packages/app/src/routes/writing/+page.server.ts +12 -0
  38. package/packages/app/src/routes/writing/+page.svelte +38 -0
  39. package/packages/app/src/site-theme.d.ts +2 -0
  40. package/packages/app/static/.gitkeep +0 -0
  41. package/packages/app/tsconfig.json +15 -0
  42. package/packages/app/vite-plugin-critical-theme.ts +70 -0
  43. package/packages/app/vite-plugin-genie.ts +114 -0
  44. package/packages/app/vite.config.ts +141 -0
  45. package/packages/core/package.json +51 -0
  46. package/packages/core/src/lib/assets/favicon.svg +1 -0
  47. package/packages/core/src/lib/components/Newsletter.svelte +13 -0
  48. package/packages/core/src/lib/components/PostCard.svelte +34 -0
  49. package/packages/core/src/lib/components/PostIndex.svelte +86 -0
  50. package/packages/core/src/lib/components/SiteFooter.svelte +15 -0
  51. package/packages/core/src/lib/components/SiteHeader.svelte +28 -0
  52. package/packages/core/src/lib/config.ts +154 -0
  53. package/packages/core/src/lib/content/content.ts +193 -0
  54. package/packages/core/src/lib/content/feeds.ts +102 -0
  55. package/packages/core/src/lib/content/markdown.ts +78 -0
  56. package/packages/core/src/lib/content/pages.ts +103 -0
  57. package/packages/core/src/lib/content/parse.ts +214 -0
  58. package/packages/core/src/lib/content/rehype-figure.ts +79 -0
  59. package/packages/core/src/lib/content/types.ts +88 -0
  60. package/packages/core/src/lib/format.ts +25 -0
  61. package/packages/core/src/lib/index.ts +27 -0
  62. package/packages/core/src/lib/server.ts +33 -0
  63. package/packages/core/src/lib/styles/fonts.css +72 -0
  64. package/packages/core/src/lib/styles/theme.css +762 -0
  65. package/packages/core/src/lib/theme.ts +5 -0
  66. package/packages/import/package.json +27 -0
  67. package/packages/import/src/cli.ts +346 -0
  68. package/packages/import/src/discover.ts +190 -0
  69. package/packages/import/src/extract.ts +378 -0
  70. package/packages/import/src/fetch.ts +63 -0
  71. package/packages/import/src/html-to-md.ts +21 -0
  72. package/packages/import/src/images.ts +247 -0
  73. package/packages/import/src/inspire.ts +417 -0
  74. package/packages/import/src/ir.ts +109 -0
  75. package/packages/import/src/ollama.ts +145 -0
  76. package/packages/import/src/stock.ts +218 -0
  77. package/packages/import/src/theme.ts +478 -0
  78. package/packages/import/src/write-site.ts +371 -0
  79. package/packages/import/tsconfig.json +13 -0
  80. package/pnpm-workspace.yaml +2 -0
  81. package/scripts/create-site.mjs +260 -0
  82. package/scripts/filepress.mjs +273 -0
  83. package/scripts/link-embedded-packages.mjs +40 -0
  84. package/scripts/postinstall.mjs +56 -0
@@ -0,0 +1,762 @@
1
+ /*
2
+ * filepress core theme — "Essay" (default).
3
+ *
4
+ * Quiet, text-first, editorial — in the spirit of Stratechery and Ben Evans.
5
+ * One reading serif (Newsreader), one UI sans for meta/labels (Inter), a single
6
+ * restrained accent used at three intensities (strong / rule / wash), hairline
7
+ * rules, and generous whitespace.
8
+ *
9
+ * Sites may override via theme.css at the site root (loaded after
10
+ * this file). Prefer CSS custom properties and the documented class names —
11
+ * see docs/THEME.md.
12
+ */
13
+
14
+ :root {
15
+ --font-serif: 'Newsreader Variable', 'Iowan Old Style', Charter, Georgia, 'Times New Roman', serif;
16
+ --font-sans: 'Inter Variable', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
17
+ --font-mono: 'SF Mono', 'JetBrains Mono', Menlo, Consolas, monospace;
18
+
19
+ /* Warm parchment light theme (avoid sterile blue-grey). */
20
+ --bg: #faf8f3;
21
+ --surface: #ffffff;
22
+ --ink: #1a1917;
23
+ --ink-soft: #4d4a44;
24
+ --ink-faint: #837f77;
25
+ --rule: #e7e2d8;
26
+ --rule-strong: #d6cfc1;
27
+
28
+ /* Single accent, three intensities. */
29
+ --accent: #7a2331;
30
+ --accent-strong: #611a26;
31
+ --accent-wash: color-mix(in srgb, var(--accent) 6%, transparent);
32
+
33
+ --measure: 40rem;
34
+ --measure-wide: 46rem;
35
+ --gap: clamp(1.25rem, 4vw, 2.5rem);
36
+ --radius: 6px;
37
+ }
38
+
39
+ @media (prefers-color-scheme: dark) {
40
+ :root {
41
+ --bg: #141310;
42
+ --surface: #1c1b16;
43
+ --ink: #edeae3;
44
+ --ink-soft: #b6b1a7;
45
+ --ink-faint: #7f7b72;
46
+ --rule: #2c2a23;
47
+ --rule-strong: #3b382f;
48
+ --accent: #dc909b;
49
+ --accent-strong: #e8a7b0;
50
+ --accent-wash: color-mix(in srgb, var(--accent) 9%, transparent);
51
+ }
52
+ }
53
+
54
+ *,
55
+ *::before,
56
+ *::after {
57
+ box-sizing: border-box;
58
+ }
59
+
60
+ html {
61
+ -webkit-text-size-adjust: 100%;
62
+ }
63
+
64
+ body {
65
+ margin: 0;
66
+ background: var(--bg);
67
+ color: var(--ink);
68
+ font-family: var(--font-serif);
69
+ font-optical-sizing: auto;
70
+ font-size: 1.2rem;
71
+ line-height: 1.72;
72
+ font-feature-settings: 'kern', 'liga', 'onum';
73
+ text-rendering: optimizeLegibility;
74
+ -webkit-font-smoothing: antialiased;
75
+ }
76
+
77
+ .wrap {
78
+ max-width: var(--measure-wide);
79
+ margin: 0 auto;
80
+ padding: 0 var(--gap);
81
+ }
82
+
83
+ /* --- Utilities --- */
84
+ .visually-hidden {
85
+ position: absolute;
86
+ width: 1px;
87
+ height: 1px;
88
+ margin: -1px;
89
+ padding: 0;
90
+ overflow: hidden;
91
+ clip: rect(0 0 0 0);
92
+ white-space: nowrap;
93
+ border: 0;
94
+ }
95
+
96
+ /* --- Header / footer chrome --- */
97
+ .site-header {
98
+ border-bottom: 1px solid var(--rule);
99
+ margin-bottom: clamp(1.75rem, 4.5vw, 2.75rem);
100
+ }
101
+
102
+ .site-header .wrap {
103
+ display: flex;
104
+ align-items: center;
105
+ justify-content: space-between;
106
+ gap: 1.5rem;
107
+ /* Keep horizontal gap so masthead lines up with main.wrap content. */
108
+ padding: 1.6rem var(--gap);
109
+ flex-wrap: wrap;
110
+ }
111
+
112
+ .site-id {
113
+ display: flex;
114
+ align-items: center;
115
+ min-width: 0;
116
+ }
117
+
118
+ .site-title {
119
+ display: inline-flex;
120
+ align-items: center;
121
+ gap: 0.85rem;
122
+ font-family: var(--font-serif);
123
+ font-size: 1.5rem;
124
+ font-weight: 600;
125
+ letter-spacing: -0.015em;
126
+ color: var(--ink);
127
+ text-decoration: none;
128
+ min-width: 0;
129
+ }
130
+
131
+ .site-logo {
132
+ display: block;
133
+ flex-shrink: 0;
134
+ height: 4.25rem;
135
+ width: auto;
136
+ }
137
+
138
+ /* Wordmark + tagline stack beside the logo (or alone when there is no mark). */
139
+ .site-brand-copy {
140
+ display: flex;
141
+ flex-direction: column;
142
+ justify-content: center;
143
+ gap: 0.2rem;
144
+ min-width: 0;
145
+ }
146
+
147
+ .site-wordmark {
148
+ line-height: 1.1;
149
+ }
150
+
151
+ /* The tagline is the site's thesis, carried on the masthead site-wide. */
152
+ .site-tagline {
153
+ margin: 0;
154
+ max-width: var(--measure);
155
+ color: var(--ink-soft);
156
+ font-style: italic;
157
+ font-size: 0.98rem;
158
+ line-height: 1.45;
159
+ }
160
+
161
+ .site-nav {
162
+ display: flex;
163
+ gap: 1.6rem;
164
+ font-family: var(--font-sans);
165
+ font-size: 0.74rem;
166
+ font-weight: 500;
167
+ text-transform: uppercase;
168
+ letter-spacing: 0.14em;
169
+ }
170
+
171
+ .site-nav a {
172
+ color: var(--ink-soft);
173
+ text-decoration: none;
174
+ }
175
+
176
+ .site-nav a:hover {
177
+ color: var(--accent);
178
+ }
179
+
180
+ .site-footer {
181
+ border-top: 1px solid var(--rule);
182
+ margin-top: clamp(2.5rem, 6vw, 4rem);
183
+ padding: 1.75rem 0 3rem;
184
+ }
185
+
186
+ .site-footer .wrap {
187
+ display: flex;
188
+ justify-content: space-between;
189
+ gap: 1rem;
190
+ flex-wrap: wrap;
191
+ font-family: var(--font-sans);
192
+ font-size: 0.78rem;
193
+ color: var(--ink-faint);
194
+ }
195
+
196
+ .site-footer .footer-links {
197
+ display: flex;
198
+ gap: 1.1rem;
199
+ }
200
+
201
+ .site-footer a {
202
+ color: var(--ink-faint);
203
+ text-decoration: none;
204
+ }
205
+
206
+ .site-footer a:hover {
207
+ color: var(--accent);
208
+ }
209
+
210
+ /* --- Links --- */
211
+ a {
212
+ color: var(--accent);
213
+ text-decoration: underline;
214
+ text-underline-offset: 2px;
215
+ text-decoration-thickness: 1px;
216
+ text-decoration-color: color-mix(in srgb, var(--accent) 45%, transparent);
217
+ }
218
+
219
+ a:hover {
220
+ color: var(--accent-strong);
221
+ text-decoration-color: currentColor;
222
+ }
223
+
224
+ /* --- Meta labels (dates, tags, bylines) --- */
225
+ .meta {
226
+ font-family: var(--font-sans);
227
+ font-size: 0.78rem;
228
+ letter-spacing: 0.04em;
229
+ color: var(--ink-faint);
230
+ }
231
+
232
+ .meta a {
233
+ color: var(--ink-soft);
234
+ }
235
+
236
+ .eyebrow {
237
+ font-family: var(--font-sans);
238
+ font-size: 0.72rem;
239
+ font-weight: 600;
240
+ text-transform: uppercase;
241
+ letter-spacing: 0.16em;
242
+ color: var(--accent);
243
+ }
244
+
245
+ .tag-list {
246
+ display: inline-flex;
247
+ flex-wrap: wrap;
248
+ gap: 0.5rem;
249
+ padding: 0;
250
+ margin: 0;
251
+ list-style: none;
252
+ }
253
+
254
+ .tag-list a,
255
+ .tag-list .tag {
256
+ font-family: var(--font-sans);
257
+ font-size: 0.72rem;
258
+ text-transform: uppercase;
259
+ letter-spacing: 0.1em;
260
+ text-decoration: none;
261
+ color: var(--ink-soft);
262
+ border: 1px solid var(--rule-strong);
263
+ border-radius: 999px;
264
+ padding: 0.14rem 0.62rem;
265
+ }
266
+
267
+ .tag-list a:hover {
268
+ color: var(--accent);
269
+ border-color: var(--accent);
270
+ background: var(--accent-wash);
271
+ }
272
+
273
+ /* --- Index hero (optional one-line lede under the masthead) --- */
274
+ .hero {
275
+ margin-bottom: clamp(1.5rem, 4vw, 2.25rem);
276
+ padding-bottom: clamp(1rem, 2.5vw, 1.4rem);
277
+ border-bottom: 1px solid var(--rule);
278
+ }
279
+
280
+ .hero-lede {
281
+ margin: 0;
282
+ max-width: var(--measure);
283
+ color: var(--ink-soft);
284
+ font-style: italic;
285
+ font-size: 1.28rem;
286
+ line-height: 1.5;
287
+ }
288
+
289
+ /* --- Featured post --- */
290
+ .featured {
291
+ margin-bottom: clamp(1.25rem, 3vw, 1.75rem);
292
+ padding-bottom: clamp(1.25rem, 3vw, 1.75rem);
293
+ border-bottom: 1px solid var(--rule);
294
+ }
295
+
296
+ .featured > .eyebrow {
297
+ margin: 0 0 0.55rem;
298
+ }
299
+
300
+ .featured .post-card {
301
+ padding: 0;
302
+ border-bottom: none;
303
+ }
304
+
305
+ .featured .post-title {
306
+ font-size: clamp(1.9rem, 4.5vw, 2.5rem);
307
+ line-height: 1.15;
308
+ }
309
+
310
+ .featured .excerpt {
311
+ font-size: 1.18rem;
312
+ }
313
+
314
+ /* --- Post listing (cards) --- */
315
+ .post-list {
316
+ list-style: none;
317
+ margin: 0;
318
+ padding: 0;
319
+ display: flex;
320
+ flex-direction: column;
321
+ }
322
+
323
+ .post-card {
324
+ padding: clamp(1.1rem, 3vw, 1.5rem) 0;
325
+ border-bottom: 1px solid var(--rule);
326
+ }
327
+
328
+ .post-list > .post-card:first-child {
329
+ padding-top: 0;
330
+ }
331
+
332
+ .post-title {
333
+ font-size: 1.55rem;
334
+ line-height: 1.2;
335
+ margin: 0.35rem 0 0.4rem;
336
+ letter-spacing: -0.015em;
337
+ font-weight: 600;
338
+ }
339
+
340
+ .post-title a {
341
+ color: var(--ink);
342
+ text-decoration: none;
343
+ }
344
+
345
+ .post-title a:hover {
346
+ color: var(--accent);
347
+ }
348
+
349
+ .byline {
350
+ font-style: normal;
351
+ }
352
+
353
+ .excerpt {
354
+ margin: 0.5rem 0 0;
355
+ color: var(--ink-soft);
356
+ }
357
+
358
+ .read-more {
359
+ display: inline-block;
360
+ margin-top: 0.7rem;
361
+ font-family: var(--font-sans);
362
+ font-size: 0.82rem;
363
+ font-weight: 500;
364
+ letter-spacing: 0.02em;
365
+ text-decoration: none;
366
+ }
367
+
368
+ .read-more:hover {
369
+ text-decoration: underline;
370
+ }
371
+
372
+ .card-tags {
373
+ margin-top: 0.75rem;
374
+ }
375
+
376
+ /* --- Pagination --- */
377
+ .pager {
378
+ display: flex;
379
+ justify-content: space-between;
380
+ align-items: center;
381
+ gap: 1rem;
382
+ margin-top: clamp(2rem, 5vw, 3rem);
383
+ font-family: var(--font-sans);
384
+ font-size: 0.85rem;
385
+ }
386
+
387
+ .pager a {
388
+ text-decoration: none;
389
+ font-weight: 500;
390
+ }
391
+
392
+ .pager .page-count {
393
+ color: var(--ink-faint);
394
+ }
395
+
396
+ /* --- Article --- */
397
+ .post-header,
398
+ .page-header {
399
+ margin-bottom: clamp(2rem, 5vw, 3rem);
400
+ }
401
+
402
+ .post-header h1,
403
+ .page-header h1 {
404
+ font-size: clamp(2rem, 5vw, 3rem);
405
+ line-height: 1.1;
406
+ letter-spacing: -0.022em;
407
+ margin: 0.4rem 0 0.85rem;
408
+ font-weight: 600;
409
+ }
410
+
411
+ .post-header .meta {
412
+ display: flex;
413
+ flex-wrap: wrap;
414
+ align-items: center;
415
+ gap: 0.5rem;
416
+ }
417
+
418
+ .draft-banner {
419
+ font-family: var(--font-sans);
420
+ font-size: 0.8rem;
421
+ letter-spacing: 0.02em;
422
+ border: 1px dashed var(--accent);
423
+ color: var(--accent);
424
+ background: var(--accent-wash);
425
+ border-radius: var(--radius);
426
+ padding: 0.55rem 0.9rem;
427
+ margin-bottom: 1.5rem;
428
+ }
429
+
430
+ .draft-label {
431
+ font-family: var(--font-sans);
432
+ font-size: 0.68rem;
433
+ font-weight: 600;
434
+ text-transform: uppercase;
435
+ letter-spacing: 0.12em;
436
+ color: var(--accent);
437
+ border: 1px dashed var(--accent);
438
+ border-radius: 999px;
439
+ padding: 0.1rem 0.5rem;
440
+ margin-right: 0.35rem;
441
+ }
442
+
443
+ /* --- Prose (rendered Markdown) --- */
444
+ .prose {
445
+ max-width: var(--measure);
446
+ }
447
+
448
+ .prose > * + * {
449
+ margin-top: 1.45em;
450
+ }
451
+
452
+ .prose h2,
453
+ .prose h3,
454
+ .prose h4 {
455
+ line-height: 1.25;
456
+ letter-spacing: -0.012em;
457
+ margin-top: 2.2em;
458
+ font-weight: 600;
459
+ }
460
+
461
+ .prose h2 {
462
+ font-size: 1.65rem;
463
+ }
464
+ .prose h3 {
465
+ font-size: 1.32rem;
466
+ }
467
+
468
+ .prose a.heading-anchor {
469
+ color: inherit;
470
+ text-decoration: none;
471
+ }
472
+
473
+ .prose a.heading-anchor:hover {
474
+ color: var(--accent);
475
+ }
476
+
477
+ /* First-paragraph lead-in for a touch of editorial polish. */
478
+ .prose > p:first-of-type {
479
+ font-size: 1.28rem;
480
+ line-height: 1.6;
481
+ color: var(--ink);
482
+ }
483
+
484
+ .prose img {
485
+ max-width: 100%;
486
+ height: auto;
487
+ border-radius: var(--radius);
488
+ }
489
+
490
+ .prose figure {
491
+ margin: 2rem 0;
492
+ text-align: center;
493
+ }
494
+
495
+ .prose figure img {
496
+ display: block;
497
+ margin: 0 auto;
498
+ }
499
+
500
+ .prose figcaption {
501
+ margin-top: 0.6rem;
502
+ font-family: var(--font-sans);
503
+ font-size: 0.8rem;
504
+ line-height: 1.5;
505
+ color: var(--ink-faint);
506
+ }
507
+
508
+ .prose blockquote {
509
+ margin-left: 0;
510
+ margin-right: 0;
511
+ padding-left: 1.3rem;
512
+ border-left: 3px solid var(--accent);
513
+ color: var(--ink-soft);
514
+ font-style: italic;
515
+ }
516
+
517
+ .prose code {
518
+ font-family: var(--font-mono);
519
+ font-size: 0.86em;
520
+ background: color-mix(in srgb, var(--ink) 7%, transparent);
521
+ padding: 0.12em 0.35em;
522
+ border-radius: 3px;
523
+ }
524
+
525
+ .prose pre {
526
+ background: var(--surface);
527
+ border: 1px solid var(--rule);
528
+ border-radius: var(--radius);
529
+ padding: 1rem 1.15rem;
530
+ overflow-x: auto;
531
+ font-size: 0.9rem;
532
+ line-height: 1.55;
533
+ }
534
+
535
+ .prose pre code {
536
+ background: none;
537
+ padding: 0;
538
+ font-size: inherit;
539
+ }
540
+
541
+ .prose table {
542
+ width: 100%;
543
+ border-collapse: collapse;
544
+ font-size: 0.95rem;
545
+ }
546
+
547
+ .prose th,
548
+ .prose td {
549
+ border: 1px solid var(--rule);
550
+ padding: 0.5rem 0.75rem;
551
+ text-align: left;
552
+ }
553
+
554
+ hr {
555
+ border: none;
556
+ border-top: 1px solid var(--rule);
557
+ margin: clamp(2rem, 5vw, 3rem) 0;
558
+ }
559
+
560
+ /* --- Prev / next --- */
561
+ .post-nav {
562
+ display: grid;
563
+ grid-template-columns: 1fr 1fr;
564
+ gap: 1.5rem;
565
+ margin-top: clamp(2.5rem, 6vw, 4rem);
566
+ padding-top: 1.75rem;
567
+ border-top: 1px solid var(--rule);
568
+ max-width: var(--measure);
569
+ }
570
+
571
+ .post-nav a {
572
+ text-decoration: none;
573
+ color: var(--ink);
574
+ }
575
+
576
+ .post-nav a:hover .post-nav-title {
577
+ color: var(--accent);
578
+ }
579
+
580
+ .post-nav .newer {
581
+ text-align: right;
582
+ }
583
+
584
+ .post-nav-label {
585
+ display: block;
586
+ font-family: var(--font-sans);
587
+ font-size: 0.72rem;
588
+ text-transform: uppercase;
589
+ letter-spacing: 0.12em;
590
+ color: var(--ink-faint);
591
+ margin-bottom: 0.3rem;
592
+ }
593
+
594
+ .post-nav-title {
595
+ font-weight: 600;
596
+ line-height: 1.25;
597
+ }
598
+
599
+ /* --- Newsletter CTA --- */
600
+ .newsletter {
601
+ margin: clamp(2.5rem, 6vw, 3.5rem) auto 0;
602
+ max-width: var(--measure);
603
+ padding: clamp(1.5rem, 4vw, 2.1rem);
604
+ border: 1px solid var(--rule-strong);
605
+ border-radius: var(--radius);
606
+ background: var(--accent-wash);
607
+ text-align: center;
608
+ }
609
+
610
+ .newsletter h2 {
611
+ margin: 0 0 0.4rem;
612
+ font-size: 1.35rem;
613
+ }
614
+
615
+ .newsletter p {
616
+ margin: 0 0 1.1rem;
617
+ color: var(--ink-soft);
618
+ font-size: 1.02rem;
619
+ }
620
+
621
+ .button {
622
+ display: inline-block;
623
+ font-family: var(--font-sans);
624
+ font-size: 0.85rem;
625
+ font-weight: 600;
626
+ letter-spacing: 0.02em;
627
+ text-decoration: none;
628
+ color: var(--surface);
629
+ background: var(--accent);
630
+ border: 1px solid var(--accent);
631
+ border-radius: var(--radius);
632
+ padding: 0.6rem 1.4rem;
633
+ }
634
+
635
+ .button:hover {
636
+ background: var(--accent-strong);
637
+ border-color: var(--accent-strong);
638
+ color: var(--surface);
639
+ }
640
+
641
+ @media (prefers-color-scheme: dark) {
642
+ .button {
643
+ color: #1a1917;
644
+ }
645
+ .button:hover {
646
+ color: #1a1917;
647
+ }
648
+ }
649
+
650
+ /* --- Topics / Explore --- */
651
+ .topic-group {
652
+ padding: clamp(1.5rem, 4vw, 2rem) 0;
653
+ border-bottom: 1px solid var(--rule);
654
+ }
655
+
656
+ .topic-group:first-of-type {
657
+ padding-top: 0;
658
+ }
659
+
660
+ .topic-group h2 {
661
+ font-size: 1.4rem;
662
+ margin: 0 0 0.9rem;
663
+ display: flex;
664
+ align-items: baseline;
665
+ justify-content: space-between;
666
+ gap: 1rem;
667
+ }
668
+
669
+ .topic-group h2 a {
670
+ color: var(--ink);
671
+ text-decoration: none;
672
+ }
673
+
674
+ .topic-group h2 a:hover {
675
+ color: var(--accent);
676
+ }
677
+
678
+ .topic-group .count {
679
+ font-family: var(--font-sans);
680
+ font-size: 0.78rem;
681
+ font-weight: 400;
682
+ color: var(--ink-faint);
683
+ }
684
+
685
+ .topic-posts {
686
+ list-style: none;
687
+ margin: 0;
688
+ padding: 0;
689
+ display: flex;
690
+ flex-direction: column;
691
+ gap: 0.55rem;
692
+ }
693
+
694
+ .topic-posts a {
695
+ color: var(--ink);
696
+ text-decoration: none;
697
+ }
698
+
699
+ .topic-posts a:hover {
700
+ color: var(--accent);
701
+ }
702
+
703
+ .topic-posts .date {
704
+ font-family: var(--font-sans);
705
+ font-size: 0.76rem;
706
+ color: var(--ink-faint);
707
+ margin-right: 0.6rem;
708
+ }
709
+
710
+ .empty-state {
711
+ color: var(--ink-soft);
712
+ font-style: italic;
713
+ padding: 3rem 0;
714
+ }
715
+
716
+ /* --- Minimal syntax highlighting (highlight.js token classes) --- */
717
+ .hljs-comment,
718
+ .hljs-quote {
719
+ color: var(--ink-faint);
720
+ font-style: italic;
721
+ }
722
+ .hljs-keyword,
723
+ .hljs-selector-tag,
724
+ .hljs-built_in,
725
+ .hljs-literal {
726
+ color: var(--accent);
727
+ }
728
+ .hljs-string,
729
+ .hljs-attr,
730
+ .hljs-template-tag {
731
+ color: #4c7a34;
732
+ }
733
+ .hljs-number,
734
+ .hljs-title,
735
+ .hljs-type {
736
+ color: #2f6b8f;
737
+ }
738
+ .hljs-name,
739
+ .hljs-tag {
740
+ color: var(--accent-strong);
741
+ }
742
+ @media (prefers-color-scheme: dark) {
743
+ .hljs-string,
744
+ .hljs-attr,
745
+ .hljs-template-tag {
746
+ color: #a3c98a;
747
+ }
748
+ .hljs-number,
749
+ .hljs-title,
750
+ .hljs-type {
751
+ color: #8fc1e0;
752
+ }
753
+ }
754
+
755
+ @media (max-width: 34rem) {
756
+ .post-nav {
757
+ grid-template-columns: 1fr;
758
+ }
759
+ .post-nav .newer {
760
+ text-align: left;
761
+ }
762
+ }