@commonpub/layer 0.62.0 → 0.63.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/nuxt.config.ts CHANGED
@@ -37,7 +37,7 @@ export default defineNuxtConfig({
37
37
  },
38
38
  {
39
39
  rel: 'stylesheet',
40
- href: 'https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300..900;1,9..144,300..900&family=Work+Sans:ital,wght@0,300..800;1,300..800&display=swap',
40
+ href: 'https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300..900;1,9..144,300..900&family=Newsreader:ital,opsz,wght@0,6..72,300..700;1,6..72,300..700&family=Work+Sans:ital,wght@0,300..800;1,300..800&display=swap',
41
41
  },
42
42
  ],
43
43
  },
@@ -48,6 +48,8 @@ export default defineNuxtConfig({
48
48
  uiTheme('generics.css'),
49
49
  uiTheme('agora.css'),
50
50
  uiTheme('agora-dark.css'),
51
+ uiTheme('stoa.css'),
52
+ uiTheme('stoa-dark.css'),
51
53
  uiTheme('components.css'),
52
54
  uiTheme('prose.css'),
53
55
  uiTheme('layouts.css'),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commonpub/layer",
3
- "version": "0.62.0",
3
+ "version": "0.63.0",
4
4
  "type": "module",
5
5
  "main": "./nuxt.config.ts",
6
6
  "files": [
@@ -55,14 +55,14 @@
55
55
  "zod": "^4.3.6",
56
56
  "@commonpub/auth": "0.8.0",
57
57
  "@commonpub/config": "0.19.0",
58
- "@commonpub/explainer": "0.7.15",
59
- "@commonpub/docs": "0.6.3",
60
- "@commonpub/protocol": "0.13.0",
61
- "@commonpub/server": "2.82.0",
62
58
  "@commonpub/editor": "0.7.11",
59
+ "@commonpub/schema": "0.35.0",
60
+ "@commonpub/docs": "0.6.3",
63
61
  "@commonpub/learning": "0.5.2",
64
- "@commonpub/ui": "0.9.2",
65
- "@commonpub/schema": "0.35.0"
62
+ "@commonpub/server": "2.82.0",
63
+ "@commonpub/ui": "0.10.0",
64
+ "@commonpub/protocol": "0.13.0",
65
+ "@commonpub/explainer": "0.7.15"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@testing-library/jest-dom": "^6.9.1",
@@ -29,8 +29,12 @@ let cacheTime = 0;
29
29
  async function loadThemeState(): Promise<CachedThemeState> {
30
30
  const db = useDB();
31
31
 
32
- // 1. Default theme ID
33
- let defaultTheme = 'base';
32
+ // 1. Default theme ID.
33
+ // Fallback is 'stoa' — the default CommonPub theme for fresh installs and any
34
+ // instance that has NOT explicitly set `theme.default` in the DB. Instances
35
+ // with an explicit setting (e.g. commonpub.io=agora-dark, branded instances)
36
+ // are unaffected; this only changes what an unconfigured instance shows.
37
+ let defaultTheme = 'stoa';
34
38
  try {
35
39
  const [row] = await db
36
40
  .select({ value: instanceSettings.value })
@@ -0,0 +1,153 @@
1
+ @layer commonpub {
2
+ /* ===========================================
3
+ CommonPub Stoa Theme — Dark
4
+ "The same walk, by lamplight."
5
+
6
+ Deep warm-black paper, brighter moss accent,
7
+ the same Fraunces / Newsreader / Work Sans
8
+ type and soft rounded geometry as Stoa Light.
9
+ Component overrides are shared from stoa.css
10
+ (both data-theme values are targeted there).
11
+ =========================================== */
12
+
13
+ [data-theme="stoa-dark"] {
14
+ /* === SURFACES (warm near-black) === */
15
+ --bg: #15130d;
16
+ --surface: #1f1c14;
17
+ --surface2: #28241a;
18
+ --surface3: #332d20;
19
+
20
+ --color-surface: var(--surface);
21
+ --color-surface-alt: var(--surface2);
22
+ --color-surface-raised: var(--surface);
23
+ --color-surface-overlay: rgba(0, 0, 0, 0.6);
24
+ --color-surface-overlay-light: rgba(0, 0, 0, 0.45);
25
+ --color-surface-scrim: rgba(21, 19, 13, 0.8);
26
+ --color-surface-hover: var(--surface2);
27
+ --color-bg-subtle: var(--bg);
28
+
29
+ /* === TEXT (warm paper ink) === */
30
+ --text: #f1ebda;
31
+ --text-dim: #bcb4a2;
32
+ --text-faint: #8d8675;
33
+
34
+ --color-text: var(--text);
35
+ --color-text-secondary: var(--text-dim);
36
+ --color-text-muted: var(--text-faint);
37
+ --color-text-inverse: #15130d;
38
+
39
+ /* === BORDERS (warm hairlines) === */
40
+ --border: rgba(241, 235, 218, 0.24);
41
+ --border2: rgba(241, 235, 218, 0.13);
42
+
43
+ --color-border: var(--border2);
44
+ --color-border-strong: var(--border);
45
+ --color-border-focus: var(--accent);
46
+
47
+ /* === ACCENT (brighter moss for dark) === */
48
+ --accent: #5aa784;
49
+ --accent-bg: rgba(90, 167, 132, 0.14);
50
+ --accent-bg-strong: rgba(90, 167, 132, 0.24);
51
+ --accent-bg-heavy: rgba(90, 167, 132, 0.42);
52
+ --accent-bg-solid: rgba(90, 167, 132, 0.6);
53
+ --accent-border: rgba(90, 167, 132, 0.34);
54
+ --accent-focus-ring: 0 0 0 3px rgba(90, 167, 132, 0.2);
55
+
56
+ --color-primary: var(--accent);
57
+ --color-primary-hover: #79c4a0;
58
+ --color-primary-text: #0f140f;
59
+ --color-on-primary: #0f140f;
60
+ --color-accent: var(--accent);
61
+ --color-accent-hover: #79c4a0;
62
+ --color-accent-text: #0f140f;
63
+ --color-on-accent: #0f140f;
64
+ --color-accent-bg: var(--accent-bg);
65
+ --color-accent-border: var(--accent-border);
66
+
67
+ /* === SEMANTIC COLORS (dark warm) === */
68
+ --green: #5aa784;
69
+ --green-bg: rgba(90, 167, 132, 0.14);
70
+ --green-border: rgba(90, 167, 132, 0.32);
71
+
72
+ --yellow: #d6a259;
73
+ --yellow-bg: rgba(214, 162, 89, 0.13);
74
+ --yellow-border: rgba(214, 162, 89, 0.3);
75
+
76
+ --red: #d07c69;
77
+ --red-bg: rgba(208, 124, 105, 0.13);
78
+ --red-border: rgba(208, 124, 105, 0.3);
79
+
80
+ --purple: #a98cbe;
81
+ --purple-bg: rgba(169, 140, 190, 0.13);
82
+ --purple-border: rgba(169, 140, 190, 0.3);
83
+
84
+ --teal: #5fb3a6;
85
+ --teal-bg: rgba(95, 179, 166, 0.13);
86
+ --teal-border: rgba(95, 179, 166, 0.3);
87
+
88
+ --pink: #cd8aa0;
89
+ --pink-bg: rgba(205, 138, 160, 0.13);
90
+ --pink-border: rgba(205, 138, 160, 0.3);
91
+
92
+ --color-success: var(--green);
93
+ --color-warning: var(--yellow);
94
+ --color-error: var(--red);
95
+ --color-info: #82a2c0;
96
+ --color-success-bg: var(--green-bg);
97
+ --color-warning-bg: var(--yellow-bg);
98
+ --color-error-bg: var(--red-bg);
99
+ --color-info-bg: rgba(130, 162, 192, 0.13);
100
+
101
+ /* === OVERLAYS === */
102
+ --color-badge-overlay: rgba(241, 235, 218, 0.85);
103
+
104
+ /* === INTERACTIVE === */
105
+ --color-link: #79c4a0;
106
+ --color-link-hover: #9bd6bb;
107
+
108
+ /* === TYPOGRAPHY (same as light) === */
109
+ --font-sans: 'Work Sans', system-ui, -apple-system, sans-serif;
110
+ --font-mono: 'JetBrains Mono', ui-monospace, monospace;
111
+ --font-display: 'Fraunces', Georgia, 'Times New Roman', serif;
112
+ --font-read: 'Newsreader', Georgia, 'Times New Roman', serif;
113
+
114
+ --font-heading: var(--font-display);
115
+ --font-body: var(--font-sans);
116
+
117
+ --text-label: 0.6875rem;
118
+
119
+ --leading-tight: 1.16;
120
+ --leading-snug: 1.36;
121
+ --leading-normal: 1.62;
122
+ --leading-relaxed: 1.78;
123
+
124
+ --tracking-tight: -0.015em;
125
+ --tracking-normal: 0;
126
+ --tracking-wide: 0.04em;
127
+ --tracking-wider: 0.08em;
128
+ --tracking-widest: 0.14em;
129
+
130
+ /* === SHAPE (same rounded geometry) === */
131
+ --radius: 12px;
132
+ --radius-sm: 8px;
133
+ --radius-md: 14px;
134
+ --radius-lg: 20px;
135
+ --radius-xl: 28px;
136
+ --radius-2xl: 28px;
137
+
138
+ /* === SHADOWS (deeper for dark) === */
139
+ --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
140
+ --shadow-md: 0 2px 8px rgba(0, 0, 0, 0.45), 0 10px 24px rgba(0, 0, 0, 0.32);
141
+ --shadow-lg: 0 16px 40px rgba(0, 0, 0, 0.52), 0 4px 12px rgba(0, 0, 0, 0.36);
142
+ --shadow-xl: 0 26px 60px rgba(0, 0, 0, 0.6), 0 8px 18px rgba(0, 0, 0, 0.4);
143
+ --shadow-accent: 0 6px 20px rgba(90, 167, 132, 0.4);
144
+
145
+ /* === TRANSITIONS === */
146
+ --transition-fast: 140ms cubic-bezier(0.22, 1, 0.36, 1);
147
+ --transition-default: 200ms cubic-bezier(0.22, 1, 0.36, 1);
148
+ --transition-slow: 320ms cubic-bezier(0.4, 0, 0.2, 1);
149
+
150
+ /* === FOCUS === */
151
+ --focus-ring: 0 0 0 3px rgba(90, 167, 132, 0.28);
152
+ }
153
+ } /* end @layer commonpub — token overrides only */
package/theme/stoa.css ADDED
@@ -0,0 +1,361 @@
1
+ @layer commonpub {
2
+ /* ===========================================
3
+ CommonPub Stoa Theme — Light
4
+ "A covered walk for thinking in public."
5
+
6
+ Warm paper grounds, moss-green accent,
7
+ Fraunces display + Newsreader reading serif,
8
+ Work Sans UI. Soft rounded corners, gentle
9
+ blurred lifts (not the brutalist offset
10
+ shadow), hairline warm borders. A calm,
11
+ bookish, daylight aesthetic.
12
+ =========================================== */
13
+
14
+ [data-theme="stoa"] {
15
+ /* === SURFACES (warm paper) === */
16
+ --bg: #f7f3ea;
17
+ --surface: #fffdf6;
18
+ --surface2: #eee8d9;
19
+ --surface3: #e4ddcb;
20
+
21
+ --color-surface: var(--surface);
22
+ --color-surface-alt: var(--surface2);
23
+ --color-surface-raised: var(--surface);
24
+ --color-surface-overlay: rgba(42, 38, 32, 0.5);
25
+ --color-surface-overlay-light: rgba(42, 38, 32, 0.4);
26
+ --color-surface-scrim: rgba(247, 243, 234, 0.78);
27
+ --color-surface-hover: var(--surface2);
28
+ --color-bg-subtle: var(--bg);
29
+
30
+ /* === TEXT (ink gradations) === */
31
+ --text: #2a2620;
32
+ --text-dim: #5a5247;
33
+ --text-faint: #8a8273;
34
+
35
+ --color-text: var(--text);
36
+ --color-text-secondary: var(--text-dim);
37
+ --color-text-muted: var(--text-faint);
38
+ --color-text-inverse: #fdfbf3;
39
+
40
+ /* === BORDERS (warm hairlines) === */
41
+ --border: rgba(42, 38, 32, 0.22);
42
+ --border2: rgba(42, 38, 32, 0.12);
43
+
44
+ --color-border: var(--border2);
45
+ --color-border-strong: var(--border);
46
+ --color-border-focus: var(--accent);
47
+
48
+ /* === ACCENT (moss green) === */
49
+ --accent: #3c8262;
50
+ --accent-bg: rgba(60, 130, 98, 0.12);
51
+ --accent-bg-strong: rgba(60, 130, 98, 0.2);
52
+ --accent-bg-heavy: rgba(60, 130, 98, 0.4);
53
+ --accent-bg-solid: rgba(60, 130, 98, 0.6);
54
+ --accent-border: rgba(60, 130, 98, 0.32);
55
+ --accent-focus-ring: 0 0 0 3px rgba(60, 130, 98, 0.18);
56
+
57
+ --color-primary: var(--accent);
58
+ --color-primary-hover: #2b6147;
59
+ --color-primary-text: #fdfbf3;
60
+ --color-on-primary: #fdfbf3;
61
+ --color-accent: var(--accent);
62
+ --color-accent-hover: #2b6147;
63
+ --color-accent-text: #fdfbf3;
64
+ --color-on-accent: #fdfbf3;
65
+ --color-accent-bg: var(--accent-bg);
66
+ --color-accent-border: var(--accent-border);
67
+
68
+ /* === SEMANTIC COLORS (warm) === */
69
+ --green: #3c8262;
70
+ --green-bg: rgba(60, 130, 98, 0.12);
71
+ --green-border: rgba(60, 130, 98, 0.3);
72
+
73
+ --yellow: #bd863a;
74
+ --yellow-bg: rgba(189, 134, 58, 0.1);
75
+ --yellow-border: rgba(189, 134, 58, 0.28);
76
+
77
+ --red: #b25a47;
78
+ --red-bg: rgba(178, 90, 71, 0.1);
79
+ --red-border: rgba(178, 90, 71, 0.28);
80
+
81
+ --purple: #7b6190;
82
+ --purple-bg: rgba(123, 97, 144, 0.1);
83
+ --purple-border: rgba(123, 97, 144, 0.28);
84
+
85
+ --teal: #3f8a80;
86
+ --teal-bg: rgba(63, 138, 128, 0.1);
87
+ --teal-border: rgba(63, 138, 128, 0.28);
88
+
89
+ --pink: #b0697f;
90
+ --pink-bg: rgba(176, 105, 127, 0.1);
91
+ --pink-border: rgba(176, 105, 127, 0.28);
92
+
93
+ /* Rank colors (warm-toned for contests) */
94
+ --gold: #c1923a;
95
+ --silver: #9a948a;
96
+ --bronze: #a8704a;
97
+
98
+ --color-success: var(--green);
99
+ --color-warning: var(--yellow);
100
+ --color-error: var(--red);
101
+ --color-info: #5d7e9c;
102
+ --color-success-bg: var(--green-bg);
103
+ --color-warning-bg: var(--yellow-bg);
104
+ --color-error-bg: var(--red-bg);
105
+ --color-info-bg: rgba(93, 126, 156, 0.1);
106
+
107
+ /* === OVERLAYS === */
108
+ --color-badge-overlay: rgba(42, 38, 32, 0.72);
109
+
110
+ /* === INTERACTIVE === */
111
+ --color-link: #2b6147;
112
+ --color-link-hover: #1f4a35;
113
+
114
+ /* === TYPOGRAPHY === */
115
+ --font-sans: 'Work Sans', system-ui, -apple-system, sans-serif;
116
+ --font-mono: 'JetBrains Mono', ui-monospace, monospace;
117
+ --font-display: 'Fraunces', Georgia, 'Times New Roman', serif;
118
+ --font-read: 'Newsreader', Georgia, 'Times New Roman', serif;
119
+
120
+ --font-heading: var(--font-display);
121
+ --font-body: var(--font-sans);
122
+
123
+ --text-label: 0.6875rem; /* 11px monospace UI labels */
124
+
125
+ --leading-tight: 1.16;
126
+ --leading-snug: 1.36;
127
+ --leading-normal: 1.62;
128
+ --leading-relaxed: 1.78;
129
+
130
+ --tracking-tight: -0.015em;
131
+ --tracking-normal: 0;
132
+ --tracking-wide: 0.04em;
133
+ --tracking-wider: 0.08em;
134
+ --tracking-widest: 0.14em;
135
+
136
+ /* === SHAPE (soft, rounded — Stoa's signature departure) === */
137
+ --radius: 12px;
138
+ --radius-sm: 8px;
139
+ --radius-md: 14px;
140
+ --radius-lg: 20px;
141
+ --radius-xl: 28px;
142
+ --radius-2xl: 28px;
143
+
144
+ /* === SHADOWS (soft blurred lifts, not offset) === */
145
+ --shadow-sm: 0 1px 2px rgba(38, 32, 24, 0.06), 0 1px 1px rgba(38, 32, 24, 0.04);
146
+ --shadow-md: 0 2px 6px rgba(38, 32, 24, 0.06), 0 8px 20px rgba(38, 32, 24, 0.06);
147
+ --shadow-lg: 0 12px 34px rgba(38, 32, 24, 0.11), 0 3px 10px rgba(38, 32, 24, 0.06);
148
+ --shadow-xl: 0 22px 50px rgba(38, 32, 24, 0.14), 0 6px 16px rgba(38, 32, 24, 0.08);
149
+ --shadow-accent: 0 6px 18px rgba(60, 130, 98, 0.28);
150
+
151
+ /* === TRANSITIONS (gentle, springy) === */
152
+ --transition-fast: 140ms cubic-bezier(0.22, 1, 0.36, 1);
153
+ --transition-default: 200ms cubic-bezier(0.22, 1, 0.36, 1);
154
+ --transition-slow: 320ms cubic-bezier(0.4, 0, 0.2, 1);
155
+
156
+ /* === FOCUS (moss glow) === */
157
+ --focus-ring: 0 0 0 3px rgba(60, 130, 98, 0.22);
158
+ }
159
+ } /* end @layer commonpub — token overrides only */
160
+
161
+
162
+ /* ═══════════════════════════════════════════
163
+ COMPONENT OVERRIDES (outside @layer so they
164
+ beat Vue scoped styles). Shared by the light
165
+ AND dark variants — both data-theme values
166
+ are targeted so stoa-dark inherits them.
167
+ ═══════════════════════════════════════════ */
168
+
169
+ /* Display serif for headings */
170
+ [data-theme="stoa"] h1,
171
+ [data-theme="stoa"] h2,
172
+ [data-theme="stoa"] h3,
173
+ [data-theme="stoa-dark"] h1,
174
+ [data-theme="stoa-dark"] h2,
175
+ [data-theme="stoa-dark"] h3,
176
+ [data-theme="stoa"] .cpub-section-title-lg,
177
+ [data-theme="stoa"] .cpub-section-title-sm,
178
+ [data-theme="stoa-dark"] .cpub-section-title-lg,
179
+ [data-theme="stoa-dark"] .cpub-section-title-sm,
180
+ [data-theme="stoa"] .admin-page-title,
181
+ [data-theme="stoa-dark"] .admin-page-title {
182
+ font-family: var(--font-display);
183
+ }
184
+
185
+ /* Wordmark + brand use the display serif */
186
+ [data-theme="stoa"] .cpub-logo-name,
187
+ [data-theme="stoa-dark"] .cpub-logo-name,
188
+ [data-theme="stoa"] .admin-brand,
189
+ [data-theme="stoa-dark"] .admin-brand {
190
+ font-family: var(--font-display);
191
+ font-weight: 600;
192
+ letter-spacing: -0.01em;
193
+ }
194
+
195
+ [data-theme="stoa"] .cpub-logo-bracket,
196
+ [data-theme="stoa-dark"] .cpub-logo-bracket {
197
+ color: var(--accent);
198
+ }
199
+
200
+ /* Focus: moss glow instead of a hard outline */
201
+ [data-theme="stoa"] :focus-visible,
202
+ [data-theme="stoa-dark"] :focus-visible {
203
+ outline: none;
204
+ box-shadow: var(--focus-ring);
205
+ }
206
+
207
+ /* Buttons: gentle lift on hover */
208
+ [data-theme="stoa"] .cpub-btn,
209
+ [data-theme="stoa-dark"] .cpub-btn {
210
+ transition: transform var(--transition-fast), box-shadow var(--transition-fast), background var(--transition-fast);
211
+ }
212
+
213
+ [data-theme="stoa"] .cpub-btn:hover,
214
+ [data-theme="stoa-dark"] .cpub-btn:hover {
215
+ transform: translateY(-1px);
216
+ box-shadow: var(--shadow-sm);
217
+ }
218
+
219
+ [data-theme="stoa"] .cpub-btn:active,
220
+ [data-theme="stoa-dark"] .cpub-btn:active {
221
+ transform: translateY(0);
222
+ box-shadow: none;
223
+ }
224
+
225
+ [data-theme="stoa"] .cpub-btn-primary,
226
+ [data-theme="stoa-dark"] .cpub-btn-primary {
227
+ background: var(--accent);
228
+ border-color: var(--accent);
229
+ color: var(--color-on-accent);
230
+ }
231
+
232
+ [data-theme="stoa"] .cpub-btn-primary:hover,
233
+ [data-theme="stoa-dark"] .cpub-btn-primary:hover {
234
+ background: var(--color-primary-hover);
235
+ box-shadow: var(--shadow-accent);
236
+ transform: translateY(-1px);
237
+ }
238
+
239
+ [data-theme="stoa"] .cpub-btn-ghost,
240
+ [data-theme="stoa-dark"] .cpub-btn-ghost {
241
+ color: var(--accent);
242
+ border-color: transparent;
243
+ }
244
+
245
+ [data-theme="stoa"] .cpub-btn-ghost:hover,
246
+ [data-theme="stoa-dark"] .cpub-btn-ghost:hover {
247
+ background: var(--accent-bg);
248
+ }
249
+
250
+ /* Cards: soft lift on hover */
251
+ [data-theme="stoa"] .cpub-content-card,
252
+ [data-theme="stoa-dark"] .cpub-content-card,
253
+ [data-theme="stoa"] .cpub-sb-card,
254
+ [data-theme="stoa-dark"] .cpub-sb-card,
255
+ [data-theme="stoa"] .cpub-stat-card,
256
+ [data-theme="stoa-dark"] .cpub-stat-card {
257
+ transition: transform var(--transition-fast), box-shadow var(--transition-fast), border-color var(--transition-fast);
258
+ }
259
+
260
+ [data-theme="stoa"] .cpub-content-card:hover,
261
+ [data-theme="stoa-dark"] .cpub-content-card:hover {
262
+ transform: translateY(-2px);
263
+ box-shadow: var(--shadow-md);
264
+ }
265
+
266
+ /* Card titles use the display serif */
267
+ [data-theme="stoa"] .cpub-content-card h3,
268
+ [data-theme="stoa-dark"] .cpub-content-card h3,
269
+ [data-theme="stoa"] .cpub-card-title,
270
+ [data-theme="stoa-dark"] .cpub-card-title {
271
+ font-family: var(--font-display);
272
+ font-weight: 600;
273
+ }
274
+
275
+ /* Inputs: glow focus */
276
+ [data-theme="stoa"] .cpub-input:focus,
277
+ [data-theme="stoa-dark"] .cpub-input:focus,
278
+ [data-theme="stoa"] .cpub-textarea:focus,
279
+ [data-theme="stoa-dark"] .cpub-textarea:focus,
280
+ [data-theme="stoa"] .cpub-select:focus,
281
+ [data-theme="stoa-dark"] .cpub-select:focus {
282
+ border-color: var(--accent);
283
+ box-shadow: var(--focus-ring);
284
+ outline: none;
285
+ }
286
+
287
+ /* Links: moss with a soft underline */
288
+ [data-theme="stoa"] a:not(.cpub-btn):not(.cpub-topbar-link):not(.cpub-footer-link):not(.admin-nav-link),
289
+ [data-theme="stoa-dark"] a:not(.cpub-btn):not(.cpub-topbar-link):not(.cpub-footer-link):not(.admin-nav-link) {
290
+ text-decoration-color: var(--accent-border);
291
+ text-underline-offset: 2px;
292
+ }
293
+
294
+ [data-theme="stoa"] .cpub-topbar-link.router-link-active,
295
+ [data-theme="stoa-dark"] .cpub-topbar-link.router-link-active {
296
+ color: var(--accent);
297
+ }
298
+
299
+ /* Prose: Newsreader reading serif, moss blockquote */
300
+ [data-theme="stoa"] .cpub-prose,
301
+ [data-theme="stoa-dark"] .cpub-prose {
302
+ font-family: var(--font-read);
303
+ font-size: 1.0625rem;
304
+ line-height: var(--leading-relaxed);
305
+ }
306
+
307
+ [data-theme="stoa"] .cpub-prose h1,
308
+ [data-theme="stoa"] .cpub-prose h2,
309
+ [data-theme="stoa"] .cpub-prose h3,
310
+ [data-theme="stoa"] .cpub-prose h4,
311
+ [data-theme="stoa-dark"] .cpub-prose h1,
312
+ [data-theme="stoa-dark"] .cpub-prose h2,
313
+ [data-theme="stoa-dark"] .cpub-prose h3,
314
+ [data-theme="stoa-dark"] .cpub-prose h4 {
315
+ font-family: var(--font-display);
316
+ }
317
+
318
+ [data-theme="stoa"] .cpub-prose blockquote,
319
+ [data-theme="stoa-dark"] .cpub-prose blockquote {
320
+ border-left-color: var(--accent);
321
+ background: var(--accent-bg);
322
+ }
323
+
324
+ [data-theme="stoa"] .cpub-prose a,
325
+ [data-theme="stoa-dark"] .cpub-prose a {
326
+ color: var(--accent);
327
+ text-decoration-color: var(--accent-border);
328
+ }
329
+
330
+ /* Admin: active nav uses moss */
331
+ [data-theme="stoa"] .admin-nav-link.router-link-exact-active,
332
+ [data-theme="stoa-dark"] .admin-nav-link.router-link-exact-active {
333
+ color: var(--accent);
334
+ background: var(--accent-bg);
335
+ }
336
+
337
+ [data-theme="stoa"] .cpub-footer-col-title,
338
+ [data-theme="stoa-dark"] .cpub-footer-col-title {
339
+ letter-spacing: var(--tracking-widest);
340
+ }
341
+
342
+
343
+ /* ═══════════════════════════════════════════
344
+ LOGO SWITCH
345
+ Stoa shares Agora's Town Square mark (hide
346
+ the Classic logo, show the Agora wordmark +
347
+ hero aside).
348
+ ═══════════════════════════════════════════ */
349
+
350
+ [data-theme="stoa"] .cpub-logo-classic,
351
+ [data-theme="stoa-dark"] .cpub-logo-classic { display: none !important; }
352
+
353
+ [data-theme="stoa"] .cpub-logo-agora,
354
+ [data-theme="stoa-dark"] .cpub-logo-agora { display: flex !important; }
355
+
356
+ [data-theme="stoa"] .cpub-hero-logo-aside,
357
+ [data-theme="stoa-dark"] .cpub-hero-logo-aside {
358
+ display: flex !important;
359
+ align-items: center;
360
+ justify-content: center;
361
+ }
@@ -17,6 +17,8 @@ export const THEME_TO_FAMILY: Record<string, string> = {
17
17
  generics: 'generics',
18
18
  agora: 'agora',
19
19
  'agora-dark': 'agora',
20
+ stoa: 'stoa',
21
+ 'stoa-dark': 'stoa',
20
22
  };
21
23
 
22
24
  /** Light/dark variants for each family */
@@ -24,6 +26,7 @@ export const FAMILY_VARIANTS: Record<string, { light: string; dark: string }> =
24
26
  classic: { light: 'base', dark: 'dark' },
25
27
  agora: { light: 'agora', dark: 'agora-dark' },
26
28
  generics: { light: 'generics', dark: 'generics' },
29
+ stoa: { light: 'stoa', dark: 'stoa-dark' },
27
30
  };
28
31
 
29
32
  /** Whether a theme ID is a dark theme */
@@ -33,6 +36,8 @@ export const IS_DARK: Record<string, boolean> = {
33
36
  generics: true,
34
37
  agora: false,
35
38
  'agora-dark': true,
39
+ stoa: false,
40
+ 'stoa-dark': true,
36
41
  };
37
42
 
38
43
  /** All valid theme IDs */