@cogenta/theme-portfolio 1.1.2

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 (117) hide show
  1. package/LICENSE +373 -0
  2. package/dist/src/index.d.ts +17 -0
  3. package/dist/src/index.d.ts.map +1 -0
  4. package/dist/src/index.js +20 -0
  5. package/dist/src/index.js.map +1 -0
  6. package/dist/src/render/blocks/accordion.d.ts +4 -0
  7. package/dist/src/render/blocks/accordion.d.ts.map +1 -0
  8. package/dist/src/render/blocks/accordion.js +24 -0
  9. package/dist/src/render/blocks/accordion.js.map +1 -0
  10. package/dist/src/render/blocks/collection-list.d.ts +25 -0
  11. package/dist/src/render/blocks/collection-list.d.ts.map +1 -0
  12. package/dist/src/render/blocks/collection-list.js +77 -0
  13. package/dist/src/render/blocks/collection-list.js.map +1 -0
  14. package/dist/src/render/blocks/cta.d.ts +10 -0
  15. package/dist/src/render/blocks/cta.d.ts.map +1 -0
  16. package/dist/src/render/blocks/cta.js +16 -0
  17. package/dist/src/render/blocks/cta.js.map +1 -0
  18. package/dist/src/render/blocks/embed.d.ts +4 -0
  19. package/dist/src/render/blocks/embed.d.ts.map +1 -0
  20. package/dist/src/render/blocks/embed.js +88 -0
  21. package/dist/src/render/blocks/embed.js.map +1 -0
  22. package/dist/src/render/blocks/faq.d.ts +4 -0
  23. package/dist/src/render/blocks/faq.d.ts.map +1 -0
  24. package/dist/src/render/blocks/faq.js +23 -0
  25. package/dist/src/render/blocks/faq.js.map +1 -0
  26. package/dist/src/render/blocks/feature-grid.d.ts +4 -0
  27. package/dist/src/render/blocks/feature-grid.d.ts.map +1 -0
  28. package/dist/src/render/blocks/feature-grid.js +30 -0
  29. package/dist/src/render/blocks/feature-grid.js.map +1 -0
  30. package/dist/src/render/blocks/gallery.d.ts +18 -0
  31. package/dist/src/render/blocks/gallery.d.ts.map +1 -0
  32. package/dist/src/render/blocks/gallery.js +34 -0
  33. package/dist/src/render/blocks/gallery.js.map +1 -0
  34. package/dist/src/render/blocks/hero.d.ts +15 -0
  35. package/dist/src/render/blocks/hero.d.ts.map +1 -0
  36. package/dist/src/render/blocks/hero.js +31 -0
  37. package/dist/src/render/blocks/hero.js.map +1 -0
  38. package/dist/src/render/blocks/logo-strip.d.ts +4 -0
  39. package/dist/src/render/blocks/logo-strip.d.ts.map +1 -0
  40. package/dist/src/render/blocks/logo-strip.js +23 -0
  41. package/dist/src/render/blocks/logo-strip.js.map +1 -0
  42. package/dist/src/render/blocks/logos.d.ts +4 -0
  43. package/dist/src/render/blocks/logos.d.ts.map +1 -0
  44. package/dist/src/render/blocks/logos.js +28 -0
  45. package/dist/src/render/blocks/logos.js.map +1 -0
  46. package/dist/src/render/blocks/media-figure.d.ts +19 -0
  47. package/dist/src/render/blocks/media-figure.d.ts.map +1 -0
  48. package/dist/src/render/blocks/media-figure.js +36 -0
  49. package/dist/src/render/blocks/media-figure.js.map +1 -0
  50. package/dist/src/render/blocks/pricing-table.d.ts +4 -0
  51. package/dist/src/render/blocks/pricing-table.d.ts.map +1 -0
  52. package/dist/src/render/blocks/pricing-table.js +29 -0
  53. package/dist/src/render/blocks/pricing-table.js.map +1 -0
  54. package/dist/src/render/blocks/prose.d.ts +16 -0
  55. package/dist/src/render/blocks/prose.d.ts.map +1 -0
  56. package/dist/src/render/blocks/prose.js +17 -0
  57. package/dist/src/render/blocks/prose.js.map +1 -0
  58. package/dist/src/render/blocks/quote.d.ts +18 -0
  59. package/dist/src/render/blocks/quote.d.ts.map +1 -0
  60. package/dist/src/render/blocks/quote.js +31 -0
  61. package/dist/src/render/blocks/quote.js.map +1 -0
  62. package/dist/src/render/blocks/stat-counter.d.ts +4 -0
  63. package/dist/src/render/blocks/stat-counter.d.ts.map +1 -0
  64. package/dist/src/render/blocks/stat-counter.js +18 -0
  65. package/dist/src/render/blocks/stat-counter.js.map +1 -0
  66. package/dist/src/render/blocks/stats.d.ts +4 -0
  67. package/dist/src/render/blocks/stats.d.ts.map +1 -0
  68. package/dist/src/render/blocks/stats.js +22 -0
  69. package/dist/src/render/blocks/stats.js.map +1 -0
  70. package/dist/src/render/blocks/testimonial.d.ts +20 -0
  71. package/dist/src/render/blocks/testimonial.d.ts.map +1 -0
  72. package/dist/src/render/blocks/testimonial.js +29 -0
  73. package/dist/src/render/blocks/testimonial.js.map +1 -0
  74. package/dist/src/render/chrome.d.ts +3 -0
  75. package/dist/src/render/chrome.d.ts.map +1 -0
  76. package/dist/src/render/chrome.js +114 -0
  77. package/dist/src/render/chrome.js.map +1 -0
  78. package/dist/src/render/render-block.d.ts +12 -0
  79. package/dist/src/render/render-block.d.ts.map +1 -0
  80. package/dist/src/render/render-block.js +111 -0
  81. package/dist/src/render/render-block.js.map +1 -0
  82. package/dist/src/render/term-archive.d.ts +14 -0
  83. package/dist/src/render/term-archive.d.ts.map +1 -0
  84. package/dist/src/render/term-archive.js +36 -0
  85. package/dist/src/render/term-archive.js.map +1 -0
  86. package/dist/theme.config.d.ts +26 -0
  87. package/dist/theme.config.d.ts.map +1 -0
  88. package/dist/theme.config.js +56 -0
  89. package/dist/theme.config.js.map +1 -0
  90. package/package.json +57 -0
  91. package/src/index.ts +19 -0
  92. package/src/render/blocks/accordion.ts +64 -0
  93. package/src/render/blocks/collection-list.ts +181 -0
  94. package/src/render/blocks/cta.ts +37 -0
  95. package/src/render/blocks/embed.ts +111 -0
  96. package/src/render/blocks/faq.ts +54 -0
  97. package/src/render/blocks/feature-grid.ts +68 -0
  98. package/src/render/blocks/gallery.ts +55 -0
  99. package/src/render/blocks/hero.ts +55 -0
  100. package/src/render/blocks/logo-strip.ts +38 -0
  101. package/src/render/blocks/logos.ts +63 -0
  102. package/src/render/blocks/media-figure.ts +51 -0
  103. package/src/render/blocks/pricing-table.ts +69 -0
  104. package/src/render/blocks/prose.ts +22 -0
  105. package/src/render/blocks/quote.ts +48 -0
  106. package/src/render/blocks/stat-counter.ts +49 -0
  107. package/src/render/blocks/stats.ts +49 -0
  108. package/src/render/blocks/testimonial.ts +45 -0
  109. package/src/render/chrome.ts +142 -0
  110. package/src/render/render-block.ts +155 -0
  111. package/src/render/term-archive.ts +88 -0
  112. package/src/styles/archive.css +40 -0
  113. package/src/styles/base.css +1056 -0
  114. package/src/styles/blocks.css +1468 -0
  115. package/src/styles/theme.css +20 -0
  116. package/src/styles/tokens.css +363 -0
  117. package/tokens.json +36 -0
@@ -0,0 +1,1056 @@
1
+ /*
2
+ * Document, accessibility floor, page frame, chrome and the theme's one
3
+ * control (an action link). Everything here reads the design system from
4
+ * `tokens.css`; nothing here reads a `--cogenta-*` token directly.
5
+ */
6
+
7
+ /* --------------------------------------------------------------------------
8
+ Document
9
+ -------------------------------------------------------------------------- */
10
+
11
+ *,
12
+ *::before,
13
+ *::after {
14
+ box-sizing: border-box;
15
+ }
16
+
17
+ body {
18
+ margin: 0;
19
+ background: var(--cg-canvas);
20
+ color: var(--cg-ink);
21
+ font-family: var(--cogenta-font-sans);
22
+ font-size: var(--cg-text-md);
23
+ line-height: var(--cg-leading-normal);
24
+ font-kerning: normal;
25
+ font-variant-ligatures: common-ligatures;
26
+ text-underline-offset: 0.16em;
27
+ text-decoration-thickness: from-font;
28
+ -webkit-text-size-adjust: 100%;
29
+ }
30
+
31
+ ::selection {
32
+ background: var(--cg-accent);
33
+ color: var(--cg-accent-fg);
34
+ }
35
+
36
+ img,
37
+ video,
38
+ iframe {
39
+ max-width: 100%;
40
+ height: auto;
41
+ }
42
+
43
+ img {
44
+ display: block;
45
+ object-fit: cover;
46
+ }
47
+
48
+ :where(a) {
49
+ color: currentColor;
50
+ text-decoration-color: color-mix(in oklab, var(--cg-accent) 55%, transparent);
51
+ text-underline-offset: 0.2em;
52
+ transition:
53
+ color var(--cg-duration-fast) var(--cg-ease),
54
+ text-decoration-color var(--cg-duration-fast) var(--cg-ease);
55
+ }
56
+
57
+ :where(a:hover) {
58
+ color: var(--cg-accent);
59
+ text-decoration-color: currentColor;
60
+ }
61
+
62
+ :where(hr) {
63
+ block-size: 1px;
64
+ border: 0;
65
+ margin-block: var(--cg-s10);
66
+ background: var(--cg-line);
67
+ }
68
+
69
+ /* --------------------------------------------------------------------------
70
+ Focus, targets, motion — the three that decide whether AA holds
71
+ -------------------------------------------------------------------------- */
72
+
73
+ :where(a, button, summary, [tabindex]):focus-visible {
74
+ outline: 2px solid var(--cg-accent);
75
+ outline-offset: 3px;
76
+ border-radius: var(--cg-radius-xs);
77
+ }
78
+
79
+ :where(.cg-action, .cg-faq__question, .cg-logo__link) {
80
+ min-block-size: 44px;
81
+ min-inline-size: 44px;
82
+ }
83
+
84
+ /* The entrance sweep every block opts into. Scroll-driven, so a section
85
+ arrives as it is scrolled to rather than firing eleven times on load, and
86
+ wrapped in a feature query because the starting state is `opacity: 0`. */
87
+ @supports (animation-timeline: view()) {
88
+ @media (prefers-reduced-motion: no-preference) {
89
+ @keyframes cg-rise {
90
+ from {
91
+ opacity: 0;
92
+ transform: translateY(var(--cg-s5));
93
+ }
94
+
95
+ to {
96
+ opacity: 1;
97
+ transform: none;
98
+ }
99
+ }
100
+
101
+ .cg-block {
102
+ animation: cg-rise linear both;
103
+ animation-timeline: view();
104
+ animation-range: entry 0% cover 20%;
105
+ }
106
+ }
107
+ }
108
+
109
+ @media (prefers-reduced-motion: reduce) {
110
+ *,
111
+ *::before,
112
+ *::after {
113
+ animation-duration: 1ms !important;
114
+ animation-iteration-count: 1 !important;
115
+ transition-duration: 1ms !important;
116
+ scroll-behavior: auto !important;
117
+ }
118
+ }
119
+
120
+ .cg-skip-link {
121
+ position: absolute;
122
+ inset-inline-start: var(--cg-s3);
123
+ inset-block-start: calc(var(--cg-s3) * -8);
124
+ z-index: 20;
125
+ padding: var(--cg-s2) var(--cg-s4);
126
+ background: var(--cg-surface-inverted);
127
+ color: var(--cg-surface-inverted-fg);
128
+ border-radius: var(--cg-radius-xs);
129
+ font-weight: var(--cg-weight-bold);
130
+ text-decoration: none;
131
+ transition: inset-block-start var(--cg-duration) var(--cg-ease);
132
+ }
133
+
134
+ .cg-skip-link:focus-visible {
135
+ inset-block-start: var(--cg-s3);
136
+ }
137
+
138
+ /* --------------------------------------------------------------------------
139
+ Page frame
140
+ -------------------------------------------------------------------------- */
141
+
142
+ .cg-main {
143
+ display: flow-root;
144
+ /* Drives the "Fig. 01" plate number in `mediaFigure` — a page-wide count,
145
+ since a plate numbers *the page's* figures, not one block's own. Every
146
+ other numbered list (`featureGrid`, `gallery`) resets its own counter
147
+ locally in `blocks.css`, since those numbers restart per list. */
148
+ counter-reset: cg-figure;
149
+ /* The hero's negative-margin media overlap (`.cg-hero__media`) and a
150
+ full-bleed grid card's own offset-on-hover both read past the page's
151
+ own edge by design; `clip` — not `hidden` — cuts them at the viewport
152
+ without turning `<main>` into a scroll container of its own. */
153
+ overflow-x: clip;
154
+ }
155
+
156
+ /* The standard clip-to-1px technique — visible to assistive tech, invisible
157
+ on screen, unlike `display:none` (which removes it from both). Every
158
+ social icon link's own text label (`renderSocialLinks`, `@cogenta/
159
+ theme-kit`) depends on this class existing here: theme-kit ships no CSS
160
+ of its own (contract D, R5 — a theme owns its own stylesheet). */
161
+ .cg-visually-hidden {
162
+ position: absolute;
163
+ inline-size: 1px;
164
+ block-size: 1px;
165
+ padding: 0;
166
+ margin: -1px;
167
+ overflow: hidden;
168
+ clip: rect(0, 0, 0, 0);
169
+ white-space: nowrap;
170
+ }
171
+
172
+ .cg-block {
173
+ max-inline-size: var(--cg-page);
174
+ margin-inline: auto;
175
+ margin-block: var(--cg-section-y);
176
+ padding-inline: var(--cg-gutter);
177
+ }
178
+
179
+ /* --- per-block visual variant (blocks@2.0, RFC 0002) ----------------------
180
+ `withBlockVariant` (`@cogenta/theme-kit`) stamps one `data-variant-<axis>`
181
+ attribute per axis an author actually set — never all four, never one this
182
+ theme does not resolve. Two attribute selectors give every rule here the
183
+ same specificity, (0,2,0), which is what lets it override `.cg-block`
184
+ above regardless of source order, without `!important` anywhere.
185
+
186
+ `background: 'image'` has no theme mechanism to attach an actual image to
187
+ a block instance (RFC 0002 adds only the semantic token, not a media
188
+ field) — resolved to the same tinted surface as the closest honest
189
+ approximation this theme has, rather than silently doing nothing with a
190
+ stated author intent.
191
+ ------------------------------------------------------------------------- */
192
+
193
+ [data-block][data-variant-background="muted"],
194
+ [data-block][data-variant-background="image"] {
195
+ padding-block: var(--cg-s10);
196
+ border-radius: var(--cg-radius-lg);
197
+ }
198
+
199
+ [data-block][data-variant-background="muted"] {
200
+ background: var(--cg-surface-sunken);
201
+ }
202
+
203
+ [data-block][data-variant-background="image"] {
204
+ background: var(--cg-surface-tinted);
205
+ }
206
+
207
+ [data-block][data-variant-spacing="compact"] {
208
+ margin-block: calc(var(--cg-section-y) / 2);
209
+ }
210
+
211
+ /* No rule for "comfortable": it names this theme's existing default, so an
212
+ author who picks it explicitly gets exactly what an absent `variant`
213
+ already gave them — the whole point of "theme default" being a real,
214
+ selectable option and not only the absence of a choice. */
215
+
216
+ [data-block][data-variant-spacing="spacious"] {
217
+ margin-block: calc(var(--cg-section-y) * 1.6);
218
+ }
219
+
220
+ [data-block][data-variant-align="start"] {
221
+ text-align: start;
222
+ }
223
+
224
+ [data-block][data-variant-align="center"] {
225
+ text-align: center;
226
+ }
227
+
228
+ [data-block][data-variant-align="end"] {
229
+ text-align: end;
230
+ }
231
+
232
+ [data-block][data-variant-width="full"] {
233
+ max-inline-size: none;
234
+ padding-inline: 0;
235
+ }
236
+
237
+ .cg-page__title {
238
+ max-inline-size: var(--cg-page);
239
+ margin-inline: auto;
240
+ margin-block: var(--cg-section-y) var(--cg-s6);
241
+ padding-inline: var(--cg-gutter);
242
+ font-family: var(--cogenta-font-sans);
243
+ font-size: var(--cg-text-hero);
244
+ line-height: var(--cg-leading-tight);
245
+ letter-spacing: var(--cg-tracking-tighter);
246
+ font-weight: var(--cg-weight-black);
247
+ text-wrap: balance;
248
+ }
249
+
250
+ /* --------------------------------------------------------------------------
251
+ Entry header (`renderEntryHeader`, `@cogenta/theme-kit`, `theme@1.4`) — a
252
+ project page's own furniture: the display title at the same scale as
253
+ `.cg-page__title`, the excerpt (`summary`) in the reading measure, and a
254
+ full-bleed cover with a hard, flat frame — no scrim, this theme never
255
+ overlays text on an image.
256
+ -------------------------------------------------------------------------- */
257
+
258
+ .cg-entry-header {
259
+ max-inline-size: var(--cg-page);
260
+ margin-inline: auto;
261
+ margin-block: var(--cg-section-y) var(--cg-s10);
262
+ padding-inline: var(--cg-gutter);
263
+ }
264
+
265
+ .cg-entry-header__terms {
266
+ display: flex;
267
+ flex-wrap: wrap;
268
+ gap: var(--cg-s4);
269
+ margin: 0 0 var(--cg-s5);
270
+ padding: 0;
271
+ list-style: none;
272
+ color: var(--cg-ink-muted);
273
+ font-family: var(--cogenta-font-mono);
274
+ font-size: var(--cg-text-xs);
275
+ letter-spacing: var(--cg-tracking-wide);
276
+ text-transform: uppercase;
277
+ }
278
+
279
+ .cg-entry-header__terms a {
280
+ color: var(--cg-accent);
281
+ text-decoration: none;
282
+ }
283
+
284
+ .cg-entry-header__terms a:hover {
285
+ text-decoration: underline;
286
+ }
287
+
288
+ .cg-entry-header__title {
289
+ margin: 0;
290
+ font-family: var(--cogenta-font-sans);
291
+ font-size: var(--cg-text-hero);
292
+ line-height: var(--cg-leading-tight);
293
+ letter-spacing: var(--cg-tracking-tighter);
294
+ font-weight: var(--cg-weight-black);
295
+ text-wrap: balance;
296
+ }
297
+
298
+ .cg-entry-header__excerpt {
299
+ margin: var(--cg-s6) 0 0;
300
+ max-inline-size: var(--cg-measure-narrow);
301
+ font-size: var(--cg-text-lg);
302
+ line-height: var(--cg-leading-relaxed);
303
+ color: var(--cg-ink-muted);
304
+ text-wrap: pretty;
305
+ }
306
+
307
+ .cg-entry-header__meta {
308
+ display: flex;
309
+ flex-wrap: wrap;
310
+ align-items: center;
311
+ gap: var(--cg-s4);
312
+ margin: var(--cg-s6) 0 0;
313
+ padding-block-start: var(--cg-s4);
314
+ border-block-start: 1px solid var(--cg-line);
315
+ color: var(--cg-ink-subtle);
316
+ font-family: var(--cogenta-font-mono);
317
+ font-size: var(--cg-text-xs);
318
+ letter-spacing: var(--cg-tracking-wide);
319
+ text-transform: uppercase;
320
+ }
321
+
322
+ .cg-entry-header__author {
323
+ color: var(--cg-ink);
324
+ font-weight: var(--cg-weight-medium);
325
+ }
326
+
327
+ .cg-entry-header__reading-time::before {
328
+ content: "·";
329
+ margin-inline-end: var(--cg-s4);
330
+ color: var(--cg-line-strong);
331
+ }
332
+
333
+ .cg-entry-header__cover {
334
+ margin: var(--cg-s10) 0 0;
335
+ border: 1px solid var(--cg-line);
336
+ }
337
+
338
+ .cg-entry-header__cover img,
339
+ .cg-entry-header__cover video {
340
+ display: block;
341
+ inline-size: 100%;
342
+ aspect-ratio: 16 / 9;
343
+ object-fit: cover;
344
+ }
345
+
346
+ /* --------------------------------------------------------------------------
347
+ Chrome — a thin index bar and a footer that becomes a second headline.
348
+ -------------------------------------------------------------------------- */
349
+
350
+ .cg-site-header {
351
+ position: sticky;
352
+ inset-block-start: 0;
353
+ z-index: 10;
354
+ padding: var(--cg-s4) var(--cg-gutter);
355
+ /* Flat and fully opaque, not a frosted-glass blur: a plane above the
356
+ page, marked off by a hairline rather than a haze. */
357
+ background: var(--cg-canvas);
358
+ border-block-end: 1px solid var(--cg-line);
359
+ }
360
+
361
+ .cg-site-header__inner {
362
+ display: flex;
363
+ flex-wrap: wrap;
364
+ align-items: center;
365
+ gap: var(--cg-gap);
366
+ max-inline-size: var(--cg-page);
367
+ margin-inline: auto;
368
+ }
369
+
370
+ .cg-site-header__mark {
371
+ display: inline-flex;
372
+ align-items: center;
373
+ gap: var(--cg-s2);
374
+ color: var(--cg-ink);
375
+ font-family: var(--cogenta-font-sans);
376
+ font-weight: var(--cg-weight-black);
377
+ font-size: var(--cg-text-lg);
378
+ letter-spacing: var(--cg-tracking-snug);
379
+ text-decoration: none;
380
+ margin-inline-end: auto;
381
+ }
382
+
383
+ /* The uploaded site logo. Height-constrained, so a wordmark and a square
384
+ mark both leave the index bar exactly as tall as it was. */
385
+ .cg-site-header__logo {
386
+ display: block;
387
+ block-size: 1.75rem;
388
+ inline-size: auto;
389
+ max-inline-size: 14rem;
390
+ object-fit: contain;
391
+ }
392
+
393
+ .cg-site-header__glyph {
394
+ color: var(--cg-accent);
395
+ font-size: 1.2em;
396
+ transition: transform var(--cg-duration-slow) var(--cg-ease);
397
+ }
398
+
399
+ .cg-site-header__mark:hover .cg-site-header__glyph {
400
+ transform: rotate(90deg);
401
+ }
402
+
403
+ /* The mobile-menu checkbox: visually hidden the accessible way (still in
404
+ the tab order, still Space-operable) rather than `display: none` (which
405
+ would remove it from the tab order entirely). */
406
+ .cg-nav-toggle-input {
407
+ position: absolute;
408
+ inline-size: 1px;
409
+ block-size: 1px;
410
+ overflow: hidden;
411
+ clip-path: inset(50%);
412
+ white-space: nowrap;
413
+ }
414
+
415
+ .cg-nav-toggle-label {
416
+ display: none;
417
+ flex-direction: column;
418
+ justify-content: center;
419
+ gap: 0.3rem;
420
+ inline-size: 2.5rem;
421
+ block-size: 2.5rem;
422
+ padding: 0.5rem;
423
+ margin-inline-start: auto;
424
+ cursor: pointer;
425
+ }
426
+
427
+ .cg-nav-toggle-input:focus-visible ~ .cg-nav-toggle-label {
428
+ outline: 2px solid var(--cg-accent);
429
+ outline-offset: 2px;
430
+ border-radius: var(--cg-radius-xs);
431
+ }
432
+
433
+ .cg-nav-toggle-bar {
434
+ display: block;
435
+ inline-size: 100%;
436
+ block-size: 2px;
437
+ background: var(--cg-ink);
438
+ transition: transform var(--cg-duration) var(--cg-ease);
439
+ }
440
+
441
+ /* Shared by the header's nav list and the footer's own (`.cg-site-footer__row
442
+ .cg-nav`) — declared here, ahead of the chrome media query below, so a
443
+ later, more specific override (`.cg-site-header__nav .cg-nav` inside it)
444
+ never has to fight a same-or-lower-specificity rule declared after it. */
445
+ .cg-nav {
446
+ display: flex;
447
+ flex-wrap: wrap;
448
+ gap: var(--cg-s6);
449
+ margin: 0;
450
+ padding: 0;
451
+ list-style: none;
452
+ font-family: var(--cogenta-font-mono);
453
+ font-size: var(--cg-text-xs);
454
+ letter-spacing: var(--cg-tracking-wide);
455
+ text-transform: uppercase;
456
+ }
457
+
458
+ .cg-nav li {
459
+ display: inline-flex;
460
+ align-items: baseline;
461
+ gap: var(--cg-s1);
462
+ }
463
+
464
+ .cg-nav a,
465
+ .cg-nav span:not(.cg-nav__index) {
466
+ color: inherit;
467
+ text-decoration: none;
468
+ }
469
+
470
+ .cg-nav a {
471
+ padding-block: var(--cg-s1);
472
+ border-block-end: 1px solid transparent;
473
+ transition:
474
+ border-color var(--cg-duration) var(--cg-ease),
475
+ color var(--cg-duration) var(--cg-ease);
476
+ }
477
+
478
+ .cg-nav a:hover {
479
+ color: var(--cg-accent);
480
+ border-block-end-color: currentColor;
481
+ }
482
+
483
+ .cg-nav__index {
484
+ color: var(--cg-accent);
485
+ opacity: 0.85;
486
+ }
487
+
488
+ /* The desktop shape — a real, always-visible `<nav>` — declared *before* the
489
+ media query below, on purpose: both rules below share this selector's
490
+ exact specificity (0,1,0), so with equal specificity the later one in the
491
+ file wins regardless of which media query is active. Declaring the
492
+ override after the default is what makes "hidden below the breakpoint"
493
+ actually win at narrow widths, rather than this unconditional rule
494
+ silently overriding the media query's own `display: none` at every width
495
+ because it happened to be declared later in the file. */
496
+ .cg-site-header__nav {
497
+ display: flex;
498
+ align-items: center;
499
+ gap: var(--cg-s6);
500
+ }
501
+
502
+ .cg-site-header__action {
503
+ flex: none;
504
+ }
505
+
506
+ /* The manual light/dark/system control (`renderThemeToggle`, `@cogenta/
507
+ theme-kit`, L26) — a plain icon button in this theme's own hairline-bordered,
508
+ sharp-cornered register (`--cg-radius-xs`, not the pill canonical uses),
509
+ sized to match the mobile-menu toggle beside it (`.cg-nav-toggle-label`)
510
+ rather than a second, unrelated control size. */
511
+ .cg-theme-toggle {
512
+ display: inline-flex;
513
+ align-items: center;
514
+ justify-content: center;
515
+ block-size: 2.5rem;
516
+ inline-size: 2.5rem;
517
+ flex-shrink: 0;
518
+ margin-inline-start: var(--cg-s2);
519
+ border: 1px solid var(--cg-line);
520
+ border-radius: var(--cg-radius-xs);
521
+ background: transparent;
522
+ color: var(--cg-ink);
523
+ cursor: pointer;
524
+ transition:
525
+ color var(--cg-duration) var(--cg-ease),
526
+ border-color var(--cg-duration) var(--cg-ease);
527
+ }
528
+
529
+ .cg-theme-toggle:hover {
530
+ color: var(--cg-accent);
531
+ border-color: var(--cg-accent);
532
+ }
533
+
534
+ .cg-theme-toggle__icon {
535
+ block-size: 1.1rem;
536
+ inline-size: 1.1rem;
537
+ }
538
+
539
+ .cg-theme-toggle__icon--moon {
540
+ display: none;
541
+ }
542
+
543
+ /* Icon reflects the *current* effective appearance — sun while light, moon
544
+ while dark — from the same `[data-theme]`/`prefers-color-scheme` signals
545
+ `tokens.css` already keys this theme's whole (dark-by-default) palette on. */
546
+ @media (prefers-color-scheme: dark) {
547
+ :where(:root:not([data-theme="light"])) .cg-theme-toggle__icon--sun {
548
+ display: none;
549
+ }
550
+
551
+ :where(:root:not([data-theme="light"])) .cg-theme-toggle__icon--moon {
552
+ display: block;
553
+ }
554
+ }
555
+
556
+ :where(:root[data-theme="dark"]) .cg-theme-toggle__icon--sun {
557
+ display: none;
558
+ }
559
+
560
+ :where(:root[data-theme="dark"]) .cg-theme-toggle__icon--moon {
561
+ display: block;
562
+ }
563
+
564
+ /* Below the breakpoint: the toggle button appears, and the real nav becomes
565
+ a full-width panel anchored under the header, shown only while the
566
+ checkbox is checked — one selector, no JavaScript, and the same `<nav>`
567
+ desktop uses, never a second copy of the links. */
568
+ @media (max-width: 56rem) {
569
+ .cg-nav-toggle-label {
570
+ display: flex;
571
+ }
572
+
573
+ .cg-site-header__nav {
574
+ display: none;
575
+ position: absolute;
576
+ inset-inline: 0;
577
+ inset-block-start: 100%;
578
+ flex-direction: column;
579
+ align-items: stretch;
580
+ gap: var(--cg-s6);
581
+ padding: var(--cg-s6) var(--cg-gutter);
582
+ background: var(--cg-canvas);
583
+ border-block-end: 1px solid var(--cg-line);
584
+ }
585
+
586
+ .cg-nav-toggle-input:checked ~ .cg-site-header__nav {
587
+ display: flex;
588
+ }
589
+
590
+ .cg-nav-toggle-input:checked ~ .cg-nav-toggle-label .cg-nav-toggle-bar:nth-child(1) {
591
+ transform: translateY(0.45rem) rotate(45deg);
592
+ }
593
+
594
+ .cg-nav-toggle-input:checked ~ .cg-nav-toggle-label .cg-nav-toggle-bar:nth-child(2) {
595
+ opacity: 0;
596
+ }
597
+
598
+ .cg-nav-toggle-input:checked ~ .cg-nav-toggle-label .cg-nav-toggle-bar:nth-child(3) {
599
+ transform: translateY(-0.45rem) rotate(-45deg);
600
+ }
601
+
602
+ .cg-site-header__nav .cg-nav {
603
+ flex-direction: column;
604
+ align-items: flex-start;
605
+ gap: var(--cg-s5);
606
+ }
607
+
608
+ .cg-site-header__action {
609
+ align-self: flex-start;
610
+ }
611
+ }
612
+
613
+ .cg-site-footer {
614
+ padding: var(--cg-s24) var(--cg-gutter) var(--cg-s10);
615
+ background: var(--cg-surface-inverted);
616
+ color: var(--cg-surface-inverted-fg);
617
+ border-block-start: 1px solid var(--cg-line-strong);
618
+ }
619
+
620
+ .cg-site-footer__inner {
621
+ max-inline-size: var(--cg-page);
622
+ margin-inline: auto;
623
+ }
624
+
625
+ .cg-site-footer__statement {
626
+ display: block;
627
+ color: inherit;
628
+ font-family: var(--cogenta-font-sans);
629
+ font-weight: var(--cg-weight-black);
630
+ font-size: var(--cg-text-jumbo);
631
+ line-height: 0.92;
632
+ letter-spacing: var(--cg-tracking-tighter);
633
+ text-decoration: none;
634
+ text-wrap: balance;
635
+ word-break: break-word;
636
+ }
637
+
638
+ .cg-site-footer__statement:hover {
639
+ color: var(--cg-accent);
640
+ }
641
+
642
+ .cg-site-footer__row {
643
+ display: flex;
644
+ flex-wrap: wrap;
645
+ align-items: flex-end;
646
+ justify-content: space-between;
647
+ gap: var(--cg-s6);
648
+ margin-block-start: var(--cg-s16);
649
+ padding-block-start: var(--cg-s8);
650
+ border-block-start: 1px solid color-mix(in oklab, var(--cg-surface-inverted-fg) 25%, transparent);
651
+ }
652
+
653
+ .cg-site-footer__row .cg-nav {
654
+ color: inherit;
655
+ }
656
+
657
+ .cg-site-footer__row .cg-nav__index {
658
+ color: var(--cg-accent);
659
+ }
660
+
661
+ /* `general.tagline` (`theme@1.4`) — the second line of the closing
662
+ statement, in the same reading measure the hero's own subtitle uses. */
663
+ .cg-site-footer__tagline {
664
+ margin: var(--cg-s4) 0 0;
665
+ max-inline-size: var(--cg-measure-narrow);
666
+ color: color-mix(in oklab, var(--cg-surface-inverted-fg) 70%, transparent);
667
+ font-size: var(--cg-text-lg);
668
+ line-height: var(--cg-leading-relaxed);
669
+ text-wrap: pretty;
670
+ }
671
+
672
+ /* `general.socialLinks` (`theme@1.4`), rendered by `renderSocialLinks` as a
673
+ row of flat, hairline-ringed icon buttons — never a filled chip, so the
674
+ row reads as a set of quiet exits rather than a second set of actions. */
675
+ .cg-site-footer__social {
676
+ display: flex;
677
+ gap: var(--cg-s3);
678
+ margin: 0;
679
+ padding: 0;
680
+ list-style: none;
681
+ }
682
+
683
+ .cg-site-footer__social-item a {
684
+ display: inline-flex;
685
+ align-items: center;
686
+ justify-content: center;
687
+ inline-size: 2.5rem;
688
+ block-size: 2.5rem;
689
+ border: 1px solid color-mix(in oklab, var(--cg-surface-inverted-fg) 30%, transparent);
690
+ border-radius: var(--cg-radius-xs);
691
+ color: inherit;
692
+ transition:
693
+ color var(--cg-duration) var(--cg-ease),
694
+ border-color var(--cg-duration) var(--cg-ease),
695
+ transform var(--cg-duration-fast) var(--cg-ease);
696
+ }
697
+
698
+ .cg-site-footer__social-item a:hover {
699
+ color: var(--cg-accent);
700
+ border-color: var(--cg-accent);
701
+ transform: translate(-2px, -2px);
702
+ }
703
+
704
+ .cg-social__icon {
705
+ inline-size: 1.1rem;
706
+ block-size: 1.1rem;
707
+ }
708
+
709
+ .cg-site-footer__meta {
710
+ display: flex;
711
+ flex-direction: column;
712
+ align-items: flex-start;
713
+ gap: var(--cg-s3);
714
+ }
715
+
716
+ @media (min-width: 48rem) {
717
+ .cg-site-footer__meta {
718
+ align-items: flex-end;
719
+ text-align: end;
720
+ }
721
+ }
722
+
723
+ /* `general.footerNote` (`theme@1.4`) — a short site-wide line, set quietly
724
+ above the branding credit rather than as a fourth headline. */
725
+ .cg-site-footer__note {
726
+ margin: 0;
727
+ max-inline-size: 32ch;
728
+ color: color-mix(in oklab, var(--cg-surface-inverted-fg) 70%, transparent);
729
+ font-family: var(--cogenta-font-mono);
730
+ font-size: var(--cg-text-xs);
731
+ letter-spacing: var(--cg-tracking-wide);
732
+ }
733
+
734
+ .cg-site-footer__branding {
735
+ display: flex;
736
+ align-items: center;
737
+ }
738
+
739
+ .cg-site-footer__branding a {
740
+ display: flex;
741
+ align-items: center;
742
+ gap: 0.4em;
743
+ color: inherit;
744
+ opacity: 0.75;
745
+ font-family: var(--cogenta-font-mono);
746
+ font-size: var(--cg-text-xs);
747
+ transition: opacity var(--cg-duration) var(--cg-ease);
748
+ }
749
+
750
+ .cg-site-footer__version {
751
+ font-variant-numeric: tabular-nums;
752
+ font-size: 0.9em;
753
+ }
754
+
755
+ .cg-site-footer__branding a:hover,
756
+ .cg-site-footer__branding a:focus-visible {
757
+ opacity: 1;
758
+ }
759
+
760
+ .cg-site-footer__brand-logo {
761
+ display: block;
762
+ /* Explicit pixels, not `em`: relative sizing cascaded from the footer's
763
+ own small font-size and made the credit mark unreadably tiny. */
764
+ block-size: 25px;
765
+ inline-size: auto;
766
+ }
767
+
768
+ /* --------------------------------------------------------------------------
769
+ Actions — the theme's only control
770
+ -------------------------------------------------------------------------- */
771
+
772
+ .cg-actions {
773
+ display: flex;
774
+ flex-wrap: wrap;
775
+ gap: var(--cg-s3);
776
+ margin: var(--cg-s8) 0 0;
777
+ padding: 0;
778
+ list-style: none;
779
+ }
780
+
781
+ .cg-action {
782
+ display: inline-flex;
783
+ align-items: center;
784
+ justify-content: center;
785
+ gap: var(--cg-s2);
786
+ padding: var(--cg-s3) var(--cg-s6);
787
+ border: 1px solid transparent;
788
+ border-radius: var(--cg-radius-xs);
789
+ font-family: var(--cogenta-font-mono);
790
+ font-size: var(--cg-text-sm);
791
+ font-weight: var(--cg-weight-medium);
792
+ letter-spacing: var(--cg-tracking-normal);
793
+ line-height: var(--cg-leading-snug);
794
+ text-decoration: none;
795
+ white-space: nowrap;
796
+ transition:
797
+ background-color var(--cg-duration) var(--cg-ease),
798
+ color var(--cg-duration) var(--cg-ease),
799
+ border-color var(--cg-duration) var(--cg-ease),
800
+ box-shadow var(--cg-duration) var(--cg-ease),
801
+ transform var(--cg-duration-fast) var(--cg-ease);
802
+ }
803
+
804
+ .cg-action[data-emphasis="primary"] {
805
+ background: var(--cg-accent);
806
+ color: var(--cg-accent-fg);
807
+ box-shadow: var(--cg-elevation-1);
808
+ }
809
+
810
+ .cg-action[data-emphasis="primary"]:hover {
811
+ box-shadow: var(--cg-elevation-2);
812
+ transform: translate(-2px, -2px);
813
+ }
814
+
815
+ .cg-action[data-emphasis="secondary"] {
816
+ background: transparent;
817
+ color: currentColor;
818
+ border-color: currentColor;
819
+ }
820
+
821
+ .cg-action[data-emphasis="secondary"]:hover {
822
+ background: var(--cg-accent);
823
+ color: var(--cg-accent-fg);
824
+ border-color: var(--cg-accent);
825
+ transform: translate(-2px, -2px);
826
+ }
827
+
828
+ .cg-action:active {
829
+ transform: translate(0, 0);
830
+ box-shadow: none;
831
+ }
832
+
833
+ /* --------------------------------------------------------------------------
834
+ Shared block furniture
835
+ -------------------------------------------------------------------------- */
836
+
837
+ .cg-features__title,
838
+ .cg-faq__title,
839
+ .cg-stats__title,
840
+ .cg-logos__title,
841
+ .cg-collection__title {
842
+ margin: 0 0 var(--cg-s10);
843
+ font-family: var(--cogenta-font-sans);
844
+ font-size: var(--cg-text-2xl);
845
+ line-height: var(--cg-leading-snug);
846
+ letter-spacing: var(--cg-tracking-tight);
847
+ font-weight: var(--cg-weight-black);
848
+ text-wrap: balance;
849
+ }
850
+
851
+ .cg-features__items,
852
+ .cg-logos__items,
853
+ .cg-gallery__items,
854
+ .cg-faq__items {
855
+ margin: 0;
856
+ padding: 0;
857
+ list-style: none;
858
+ }
859
+
860
+ .cg-collection__items {
861
+ margin: 0;
862
+ padding: 0;
863
+ }
864
+
865
+ .cg-gallery__viewport,
866
+ .cg-collection__viewport {
867
+ overflow-x: auto;
868
+ scroll-snap-type: x mandatory;
869
+ scroll-behavior: smooth;
870
+ overscroll-behavior-inline: contain;
871
+ scrollbar-width: thin;
872
+ scrollbar-color: var(--cg-line-strong) transparent;
873
+ padding-block: var(--cg-s2);
874
+ margin-block: calc(var(--cg-s2) * -1);
875
+ }
876
+
877
+ /* --------------------------------------------------------------------------
878
+ Search, forms, comments — public utility pages and post-content furniture,
879
+ dressed in the same mono-labelled, hairline-ruled register as the blocks.
880
+ -------------------------------------------------------------------------- */
881
+
882
+ .cg-search__form,
883
+ .cg-search__results,
884
+ .cg-search__empty,
885
+ .cg-search__error,
886
+ .cg-form,
887
+ .cg-form__confirmation,
888
+ .cg-comments {
889
+ max-inline-size: var(--cg-page);
890
+ margin-inline: auto;
891
+ padding-inline: var(--cg-gutter);
892
+ }
893
+
894
+ .cg-search__form {
895
+ display: flex;
896
+ flex-wrap: wrap;
897
+ align-items: end;
898
+ gap: var(--cg-s3);
899
+ margin-block-end: var(--cg-s10);
900
+ }
901
+
902
+ .cg-search__form label,
903
+ .cg-form__field label,
904
+ .cg-comment__form label {
905
+ display: block;
906
+ font-family: var(--cogenta-font-mono);
907
+ font-size: var(--cg-text-xs);
908
+ letter-spacing: var(--cg-tracking-wide);
909
+ text-transform: uppercase;
910
+ font-weight: var(--cg-weight-medium);
911
+ margin-block-end: var(--cg-s2);
912
+ }
913
+
914
+ .cg-search__form input[type="search"] {
915
+ flex: 1 1 16rem;
916
+ }
917
+
918
+ .cg-search__results {
919
+ margin: 0 auto;
920
+ padding-inline-start: var(--cg-gutter);
921
+ display: grid;
922
+ gap: var(--cg-s5);
923
+ font-size: var(--cg-text-lg);
924
+ }
925
+
926
+ .cg-search__empty,
927
+ .cg-comments__empty,
928
+ .cg-comments__closed {
929
+ color: var(--cg-ink-muted);
930
+ }
931
+
932
+ .cg-search__error,
933
+ .cg-form__error,
934
+ .cg-form__field-error {
935
+ color: var(--cg-ink);
936
+ font-weight: var(--cg-weight-bold);
937
+ }
938
+
939
+ .cg-search__form input,
940
+ .cg-form__field input,
941
+ .cg-form__field select,
942
+ .cg-form__field textarea,
943
+ .cg-comment__form input,
944
+ .cg-comment__form textarea {
945
+ inline-size: 100%;
946
+ padding: var(--cg-s3) var(--cg-s4);
947
+ border: 1px solid var(--cg-line);
948
+ border-radius: var(--cg-radius-xs);
949
+ background: var(--cg-surface);
950
+ color: var(--cg-ink);
951
+ font: inherit;
952
+ }
953
+
954
+ .cg-form__field input[aria-invalid="true"],
955
+ .cg-form__field select[aria-invalid="true"],
956
+ .cg-form__field textarea[aria-invalid="true"] {
957
+ border-width: 2px;
958
+ border-color: var(--cg-line-strong);
959
+ }
960
+
961
+ .cg-search__form button,
962
+ .cg-form button[type="submit"],
963
+ .cg-comment__form button[type="submit"] {
964
+ padding: var(--cg-s3) var(--cg-s6);
965
+ border: 1px solid var(--cg-accent);
966
+ border-radius: var(--cg-radius-xs);
967
+ background: var(--cg-accent);
968
+ color: var(--cg-accent-fg);
969
+ font-family: var(--cogenta-font-mono);
970
+ font-weight: var(--cg-weight-medium);
971
+ cursor: pointer;
972
+ transition: background-color var(--cg-duration) var(--cg-ease);
973
+ }
974
+
975
+ .cg-search__form button:hover,
976
+ .cg-form button[type="submit"]:hover,
977
+ .cg-comment__form button[type="submit"]:hover {
978
+ background: var(--cg-accent-hover);
979
+ }
980
+
981
+ .cg-form__field,
982
+ .cg-comment__form p {
983
+ display: block;
984
+ margin-block-end: var(--cg-s5);
985
+ }
986
+
987
+ .cg-form__help {
988
+ margin: var(--cg-s2) 0 0;
989
+ font-size: var(--cg-text-sm);
990
+ color: var(--cg-ink-muted);
991
+ }
992
+
993
+ .cg-form__choice {
994
+ display: inline-flex;
995
+ align-items: center;
996
+ gap: var(--cg-s2);
997
+ margin-inline-end: var(--cg-s4);
998
+ font-weight: normal;
999
+ }
1000
+
1001
+ .cg-form__honeypot,
1002
+ .cg-comment__honeypot {
1003
+ position: absolute;
1004
+ inline-size: 1px;
1005
+ block-size: 1px;
1006
+ overflow: hidden;
1007
+ inset-inline-start: -9999px;
1008
+ }
1009
+
1010
+ .cg-comments {
1011
+ margin-block-start: var(--cg-section-y);
1012
+ padding-block-start: var(--cg-s8);
1013
+ padding-block-end: var(--cg-section-y);
1014
+ border-block-start: 1px solid var(--cg-line);
1015
+ }
1016
+
1017
+ .cg-comments__list,
1018
+ .cg-comment__replies {
1019
+ margin: 0;
1020
+ padding: 0;
1021
+ list-style: none;
1022
+ }
1023
+
1024
+ .cg-comment {
1025
+ padding-block: var(--cg-s5);
1026
+ border-block-end: 1px solid var(--cg-line-soft);
1027
+ }
1028
+
1029
+ .cg-comment__replies {
1030
+ padding-block-start: var(--cg-s5);
1031
+ }
1032
+
1033
+ .cg-comment__replies .cg-comment {
1034
+ padding-inline-start: var(--cg-s6);
1035
+ border-inline-start: 2px solid var(--cg-line);
1036
+ border-block-end: 0;
1037
+ }
1038
+
1039
+ .cg-comment__meta {
1040
+ display: flex;
1041
+ gap: var(--cg-s3);
1042
+ align-items: baseline;
1043
+ font-family: var(--cogenta-font-mono);
1044
+ font-size: var(--cg-text-xs);
1045
+ color: var(--cg-ink-muted);
1046
+ margin-block-end: var(--cg-s2);
1047
+ }
1048
+
1049
+ .cg-comment__author {
1050
+ font-weight: var(--cg-weight-semibold);
1051
+ color: var(--cg-ink);
1052
+ }
1053
+
1054
+ .cg-comment__body {
1055
+ margin: 0;
1056
+ }