@cogenta/theme-canonical 0.2.1 → 1.1.1

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 (108) hide show
  1. package/dist/src/index.d.ts +4 -0
  2. package/dist/src/index.d.ts.map +1 -1
  3. package/dist/src/index.js +4 -0
  4. package/dist/src/index.js.map +1 -1
  5. package/dist/src/render/actions.d.ts +2 -16
  6. package/dist/src/render/actions.d.ts.map +1 -1
  7. package/dist/src/render/actions.js +2 -42
  8. package/dist/src/render/actions.js.map +1 -1
  9. package/dist/src/render/blocks/accordion.d.ts +5 -0
  10. package/dist/src/render/blocks/accordion.d.ts.map +1 -0
  11. package/dist/src/render/blocks/accordion.js +19 -0
  12. package/dist/src/render/blocks/accordion.js.map +1 -0
  13. package/dist/src/render/blocks/collection-list.d.ts +9 -7
  14. package/dist/src/render/blocks/collection-list.d.ts.map +1 -1
  15. package/dist/src/render/blocks/collection-list.js +8 -14
  16. package/dist/src/render/blocks/collection-list.js.map +1 -1
  17. package/dist/src/render/blocks/feature-grid.d.ts.map +1 -1
  18. package/dist/src/render/blocks/feature-grid.js +7 -2
  19. package/dist/src/render/blocks/feature-grid.js.map +1 -1
  20. package/dist/src/render/blocks/logo-strip.d.ts +5 -0
  21. package/dist/src/render/blocks/logo-strip.d.ts.map +1 -0
  22. package/dist/src/render/blocks/logo-strip.js +18 -0
  23. package/dist/src/render/blocks/logo-strip.js.map +1 -0
  24. package/dist/src/render/blocks/pricing-table.d.ts +5 -0
  25. package/dist/src/render/blocks/pricing-table.d.ts.map +1 -0
  26. package/dist/src/render/blocks/pricing-table.js +29 -0
  27. package/dist/src/render/blocks/pricing-table.js.map +1 -0
  28. package/dist/src/render/blocks/stat-counter.d.ts +5 -0
  29. package/dist/src/render/blocks/stat-counter.d.ts.map +1 -0
  30. package/dist/src/render/blocks/stat-counter.js +17 -0
  31. package/dist/src/render/blocks/stat-counter.js.map +1 -0
  32. package/dist/src/render/blocks/testimonial.d.ts +14 -0
  33. package/dist/src/render/blocks/testimonial.d.ts.map +1 -0
  34. package/dist/src/render/blocks/testimonial.js +24 -0
  35. package/dist/src/render/blocks/testimonial.js.map +1 -0
  36. package/dist/src/render/chrome.d.ts +3 -0
  37. package/dist/src/render/chrome.d.ts.map +1 -0
  38. package/dist/src/render/chrome.js +82 -0
  39. package/dist/src/render/chrome.js.map +1 -0
  40. package/dist/src/render/comments.d.ts +3 -0
  41. package/dist/src/render/comments.d.ts.map +1 -0
  42. package/dist/src/render/comments.js +3 -0
  43. package/dist/src/render/comments.js.map +1 -0
  44. package/dist/src/render/entry.d.ts +2 -6
  45. package/dist/src/render/entry.d.ts.map +1 -1
  46. package/dist/src/render/entry.js +2 -34
  47. package/dist/src/render/entry.js.map +1 -1
  48. package/dist/src/render/heading.d.ts +2 -13
  49. package/dist/src/render/heading.d.ts.map +1 -1
  50. package/dist/src/render/heading.js +2 -33
  51. package/dist/src/render/heading.js.map +1 -1
  52. package/dist/src/render/html.d.ts +2 -43
  53. package/dist/src/render/html.d.ts.map +1 -1
  54. package/dist/src/render/html.js +2 -95
  55. package/dist/src/render/html.js.map +1 -1
  56. package/dist/src/render/icons.d.ts +3 -0
  57. package/dist/src/render/icons.d.ts.map +1 -0
  58. package/dist/src/render/icons.js +3 -0
  59. package/dist/src/render/icons.js.map +1 -0
  60. package/dist/src/render/media.d.ts +2 -31
  61. package/dist/src/render/media.d.ts.map +1 -1
  62. package/dist/src/render/media.js +2 -41
  63. package/dist/src/render/media.js.map +1 -1
  64. package/dist/src/render/render-block.d.ts +11 -22
  65. package/dist/src/render/render-block.d.ts.map +1 -1
  66. package/dist/src/render/render-block.js +63 -62
  67. package/dist/src/render/render-block.js.map +1 -1
  68. package/dist/src/render/rich-text.d.ts +2 -4
  69. package/dist/src/render/rich-text.d.ts.map +1 -1
  70. package/dist/src/render/rich-text.js +2 -104
  71. package/dist/src/render/rich-text.js.map +1 -1
  72. package/dist/src/render/term-archive.d.ts +12 -0
  73. package/dist/src/render/term-archive.d.ts.map +1 -0
  74. package/dist/src/render/term-archive.js +39 -0
  75. package/dist/src/render/term-archive.js.map +1 -0
  76. package/dist/src/theme-contract.d.ts +10 -162
  77. package/dist/src/theme-contract.d.ts.map +1 -1
  78. package/dist/src/theme-contract.js +9 -17
  79. package/dist/src/theme-contract.js.map +1 -1
  80. package/dist/theme.config.d.ts +5 -16
  81. package/dist/theme.config.d.ts.map +1 -1
  82. package/dist/theme.config.js +16 -5
  83. package/dist/theme.config.js.map +1 -1
  84. package/package.json +4 -2
  85. package/src/index.ts +4 -0
  86. package/src/render/actions.ts +2 -57
  87. package/src/render/blocks/accordion.ts +44 -0
  88. package/src/render/blocks/collection-list.ts +9 -15
  89. package/src/render/blocks/feature-grid.ts +11 -2
  90. package/src/render/blocks/logo-strip.ts +38 -0
  91. package/src/render/blocks/pricing-table.ts +65 -0
  92. package/src/render/blocks/stat-counter.ts +34 -0
  93. package/src/render/blocks/testimonial.ts +41 -0
  94. package/src/render/chrome.ts +105 -0
  95. package/src/render/comments.ts +6 -0
  96. package/src/render/entry.ts +2 -40
  97. package/src/render/heading.ts +7 -44
  98. package/src/render/html.ts +15 -122
  99. package/src/render/icons.ts +2 -0
  100. package/src/render/media.ts +2 -62
  101. package/src/render/render-block.ts +85 -75
  102. package/src/render/rich-text.ts +2 -150
  103. package/src/render/term-archive.ts +85 -0
  104. package/src/styles/archive.css +40 -0
  105. package/src/styles/base.css +525 -3
  106. package/src/styles/blocks.css +344 -14
  107. package/src/styles/theme.css +1 -0
  108. package/src/theme-contract.ts +23 -155
@@ -176,6 +176,64 @@ img {
176
176
  padding-inline: var(--cg-gutter);
177
177
  }
178
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
+
179
237
  .cg-page__title {
180
238
  max-inline-size: var(--cg-page);
181
239
  margin-inline: auto;
@@ -188,15 +246,100 @@ img {
188
246
  text-wrap: balance;
189
247
  }
190
248
 
249
+ /* --------------------------------------------------------------------------
250
+ Entry header (`renderEntryHeader`, `@cogenta/theme-kit`, contract D
251
+ `theme@1.4`) — the article furniture (`PageContent.entry`) a `richText`
252
+ entry carries beyond its blocks: an eyebrow of taxonomy terms, the title
253
+ (replacing the bare `.cg-page__title` this same page would otherwise get
254
+ — `render-block.ts` renders exactly one of the two), an excerpt, a meta
255
+ line, a cover 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-s6);
262
+ padding-inline: var(--cg-gutter);
263
+ }
264
+
265
+ .cg-entry-header__terms {
266
+ display: flex;
267
+ flex-wrap: wrap;
268
+ gap: var(--cg-s2);
269
+ margin: 0 0 var(--cg-s3);
270
+ padding: 0;
271
+ list-style: none;
272
+ font-size: var(--cg-text-sm);
273
+ font-weight: var(--cg-weight-semibold);
274
+ letter-spacing: var(--cg-tracking-wide);
275
+ text-transform: uppercase;
276
+ color: var(--cg-accent);
277
+ }
278
+
279
+ .cg-entry-header__terms a {
280
+ color: inherit;
281
+ text-decoration: none;
282
+ }
283
+
284
+ .cg-entry-header__terms li:not(:last-child)::after {
285
+ content: "·";
286
+ margin-inline-start: var(--cg-s2);
287
+ color: var(--cg-ink-muted);
288
+ }
289
+
290
+ .cg-entry-header__title {
291
+ margin: 0 0 var(--cg-s4);
292
+ font-size: var(--cg-text-3xl);
293
+ line-height: var(--cg-leading-tight);
294
+ letter-spacing: var(--cg-tracking-tight);
295
+ font-weight: var(--cg-weight-bold);
296
+ text-wrap: balance;
297
+ }
298
+
299
+ .cg-entry-header__excerpt {
300
+ margin: 0 0 var(--cg-s4);
301
+ font-size: var(--cg-text-lg);
302
+ color: var(--cg-ink-muted);
303
+ text-wrap: pretty;
304
+ }
305
+
306
+ .cg-entry-header__meta {
307
+ display: flex;
308
+ flex-wrap: wrap;
309
+ gap: var(--cg-s3);
310
+ margin: 0 0 var(--cg-s6);
311
+ font-size: var(--cg-text-sm);
312
+ color: var(--cg-ink-muted);
313
+ }
314
+
315
+ .cg-entry-header__meta > :not(:last-child)::after {
316
+ content: "·";
317
+ margin-inline-start: var(--cg-s3);
318
+ color: var(--cg-line-strong);
319
+ }
320
+
321
+ .cg-entry-header__cover {
322
+ margin: 0;
323
+ }
324
+
325
+ .cg-entry-header__cover img,
326
+ .cg-entry-header__cover video {
327
+ inline-size: 100%;
328
+ aspect-ratio: 16 / 9;
329
+ border-radius: var(--cg-radius-lg);
330
+ object-fit: cover;
331
+ }
332
+
191
333
  .cg-site-header {
192
334
  position: sticky;
193
335
  inset-block-start: 0;
194
336
  z-index: 5;
195
337
  padding: var(--cg-s4) var(--cg-gutter);
196
- background: color-mix(in oklab, var(--cg-canvas) 82%, transparent);
338
+ /* Flat and fully opaque, not a frosted-glass blur: a sticky header reads
339
+ as a solid plane sitting above the page, with a hairline marking where
340
+ it ends. */
341
+ background: var(--cg-canvas);
197
342
  border-block-end: 1px solid var(--cg-line-soft);
198
- /* Progressive: a browser without it simply gets the translucent ground. */
199
- backdrop-filter: saturate(1.4) blur(12px);
200
343
  }
201
344
 
202
345
  .cg-site-header__inner,
@@ -218,6 +361,19 @@ img {
218
361
  margin-inline-end: auto;
219
362
  }
220
363
 
364
+ /* The uploaded site logo, when there is one. Height-constrained rather than
365
+ width-constrained: a wordmark logo and a square mark have wildly different
366
+ aspect ratios, and only a fixed height keeps the header bar the same
367
+ height for both. `max-inline-size` is the guard against a very wide
368
+ wordmark pushing the navigation off the row. */
369
+ .cg-site-header__logo {
370
+ display: block;
371
+ block-size: 2rem;
372
+ inline-size: auto;
373
+ max-inline-size: 14rem;
374
+ object-fit: contain;
375
+ }
376
+
221
377
  .cg-site-footer {
222
378
  padding: var(--cg-s12) var(--cg-gutter);
223
379
  background: var(--cg-surface-sunken);
@@ -226,6 +382,173 @@ img {
226
382
  font-size: var(--cg-text-sm);
227
383
  }
228
384
 
385
+ /* `theme@1.4` (L25 D2) — a button-styled link at the end of the header nav.
386
+ Reuses `.cg-action`'s own look (see "Actions" below) rather than a second
387
+ button style. */
388
+ .cg-site-header__action {
389
+ margin-inline-start: var(--cg-s2);
390
+ }
391
+
392
+ /* The manual light/dark/system control (`renderThemeToggle`, `@cogenta/
393
+ theme-kit`) — a plain icon button, not the block vocabulary's `.cg-action`
394
+ style, so it never reads as a call-to-action competing with a real one. */
395
+ .cg-theme-toggle {
396
+ display: inline-flex;
397
+ align-items: center;
398
+ justify-content: center;
399
+ block-size: 2.25rem;
400
+ inline-size: 2.25rem;
401
+ flex-shrink: 0;
402
+ border-radius: var(--cg-radius-pill);
403
+ border: 1px solid var(--cg-line-soft);
404
+ background: transparent;
405
+ color: var(--cg-ink);
406
+ cursor: pointer;
407
+ }
408
+
409
+ .cg-theme-toggle:hover {
410
+ background: var(--cg-surface-sunken);
411
+ }
412
+
413
+ .cg-theme-toggle__icon {
414
+ block-size: 1.1rem;
415
+ inline-size: 1.1rem;
416
+ }
417
+
418
+ .cg-theme-toggle__icon--moon {
419
+ display: none;
420
+ }
421
+
422
+ /* Icon reflects the *current* effective appearance — sun while light, moon
423
+ while dark — from the same `[data-theme]`/`prefers-color-scheme` signals
424
+ `tokens.css` already keys the whole palette on. */
425
+ @media (prefers-color-scheme: dark) {
426
+ :where(:root:not([data-theme='light'])) .cg-theme-toggle__icon--sun {
427
+ display: none;
428
+ }
429
+ :where(:root:not([data-theme='light'])) .cg-theme-toggle__icon--moon {
430
+ display: block;
431
+ }
432
+ }
433
+
434
+ :where(:root[data-theme='dark']) .cg-theme-toggle__icon--sun {
435
+ display: none;
436
+ }
437
+
438
+ :where(:root[data-theme='dark']) .cg-theme-toggle__icon--moon {
439
+ display: block;
440
+ }
441
+
442
+ /* `general.tagline`, shown right after the site name in the footer — prose,
443
+ never a heading, so it stays the same visual weight as the credit line
444
+ beside it. */
445
+ .cg-site-footer__tagline {
446
+ margin: var(--cg-s1) 0 0;
447
+ max-inline-size: 28rem;
448
+ }
449
+
450
+ .cg-site-footer__social {
451
+ display: flex;
452
+ flex-wrap: wrap;
453
+ gap: var(--cg-s3);
454
+ margin: var(--cg-s4) 0 0;
455
+ padding: 0;
456
+ list-style: none;
457
+ }
458
+
459
+ .cg-site-footer__social-item a {
460
+ display: flex;
461
+ align-items: center;
462
+ justify-content: center;
463
+ inline-size: 2.25rem;
464
+ block-size: 2.25rem;
465
+ border-radius: var(--cg-radius-md);
466
+ color: var(--cg-ink-muted);
467
+ background: var(--cg-surface);
468
+ box-shadow: var(--cg-ring);
469
+ transition:
470
+ color var(--cg-duration-fast) var(--cg-ease),
471
+ background-color var(--cg-duration-fast) var(--cg-ease);
472
+ }
473
+
474
+ .cg-site-footer__social-item a:hover,
475
+ .cg-site-footer__social-item a:focus-visible {
476
+ color: var(--cg-accent-fg);
477
+ background: var(--cg-accent);
478
+ }
479
+
480
+ .cg-social__icon {
481
+ inline-size: 1.1rem;
482
+ block-size: 1.1rem;
483
+ }
484
+
485
+ /* `general.footerNote` — a short "about" column, visually distinct from the
486
+ tagline (which sits with the site name) and from Cogenta's own credit
487
+ (which stays pinned to the far end of the row, see
488
+ `.cg-site-footer__branding` above). */
489
+ .cg-site-footer__about {
490
+ margin-inline-end: auto;
491
+ max-inline-size: 24rem;
492
+ }
493
+
494
+ .cg-site-footer__about p {
495
+ margin: 0;
496
+ }
497
+
498
+ /* The standard clip-to-1px technique — visible to assistive tech, invisible
499
+ on screen, unlike `display:none` (which removes it from both). Every
500
+ social icon link's own text label (`renderSocialLinks`,
501
+ `@cogenta/theme-kit`) depends on this class existing here: theme-kit ships
502
+ no CSS of its own (contract D, R5 — a theme owns its own stylesheet). */
503
+ .cg-visually-hidden {
504
+ position: absolute;
505
+ inline-size: 1px;
506
+ block-size: 1px;
507
+ padding: 0;
508
+ margin: -1px;
509
+ overflow: hidden;
510
+ clip: rect(0, 0, 0, 0);
511
+ white-space: nowrap;
512
+ border: 0;
513
+ }
514
+
515
+ /* Cogenta's own credit, or its white-label replacement (fiche L21 task 8) —
516
+ pushed to the far end of the footer row, after the site name and its own
517
+ navigation, never competing with either for space. */
518
+ .cg-site-footer__branding {
519
+ margin-inline-start: auto;
520
+ display: flex;
521
+ align-items: center;
522
+ }
523
+
524
+ .cg-site-footer__branding a {
525
+ display: flex;
526
+ align-items: center;
527
+ gap: 0.4em;
528
+ opacity: 0.7;
529
+ transition: opacity 0.15s ease;
530
+ }
531
+
532
+ /* `@cogenta/core`'s package version, next to the logo (fiche 22 tâche 8) — only rendered at all when Cogenta's own credit is on. */
533
+ .cg-site-footer__version {
534
+ font-variant-numeric: tabular-nums;
535
+ font-size: 0.9em;
536
+ }
537
+
538
+ .cg-site-footer__branding a:hover,
539
+ .cg-site-footer__branding a:focus-visible {
540
+ opacity: 1;
541
+ }
542
+
543
+ .cg-site-footer__brand-logo {
544
+ display: block;
545
+ /* Explicit pixels, not `em`: a footer's own small font-size cascaded into
546
+ this and made the credit mark unreadably tiny — the same class of bug
547
+ the admin shell's own logo had before its fix. */
548
+ block-size: 25px;
549
+ inline-size: auto;
550
+ }
551
+
229
552
  /* --------------------------------------------------------------------------
230
553
  Actions — the theme's only control
231
554
  -------------------------------------------------------------------------- */
@@ -339,3 +662,202 @@ img {
339
662
  padding-block: var(--cg-s2);
340
663
  margin-block: calc(var(--cg-s2) * -1);
341
664
  }
665
+
666
+ /* --------------------------------------------------------------------------
667
+ Search, forms, comments — public utility pages and post-content furniture
668
+
669
+ `/search` (`search-page.ts`), `/forms/{name}` (`forms-page.ts`) and the
670
+ comment thread appended after an entry page (`comments.ts`) all render
671
+ real markup with real `cg-*` classes, but none of it had a single rule
672
+ here (L20 audit, points 8-9) — the stylesheet loaded, the class names were
673
+ right there in the HTML, and nothing matched. These three follow the same
674
+ page-width measure `.cg-page__title` already sets, so a search result list
675
+ or a form lines up under its own heading rather than running edge to edge.
676
+ -------------------------------------------------------------------------- */
677
+
678
+ .cg-search__form,
679
+ .cg-search__results,
680
+ .cg-search__empty,
681
+ .cg-search__error,
682
+ .cg-form,
683
+ .cg-form__confirmation,
684
+ .cg-comments {
685
+ max-inline-size: var(--cg-page);
686
+ margin-inline: auto;
687
+ padding-inline: var(--cg-gutter);
688
+ }
689
+
690
+ .cg-search__form {
691
+ display: flex;
692
+ flex-wrap: wrap;
693
+ align-items: end;
694
+ gap: var(--cg-s3);
695
+ margin-block-end: var(--cg-s8);
696
+ }
697
+
698
+ .cg-search__form label {
699
+ display: block;
700
+ font-size: var(--cg-text-sm);
701
+ font-weight: var(--cg-weight-semibold);
702
+ margin-block-end: var(--cg-s2);
703
+ }
704
+
705
+ .cg-search__form input[type="search"] {
706
+ flex: 1 1 16rem;
707
+ }
708
+
709
+ .cg-search__results {
710
+ margin: 0 auto;
711
+ padding-inline-start: var(--cg-gutter);
712
+ display: grid;
713
+ gap: var(--cg-s4);
714
+ font-size: var(--cg-text-lg);
715
+ }
716
+
717
+ .cg-search__empty,
718
+ .cg-comments__empty,
719
+ .cg-comments__closed {
720
+ color: var(--cg-ink-muted);
721
+ }
722
+
723
+ /* No skin exposes a "danger" colour (contract D has none), so an error reads
724
+ through weight and a stronger border rather than a hue no skin can change —
725
+ the same constraint that keeps every other value in this file a function of
726
+ a token. */
727
+ .cg-search__error,
728
+ .cg-form__error,
729
+ .cg-form__field-error {
730
+ color: var(--cg-ink);
731
+ font-weight: var(--cg-weight-bold);
732
+ }
733
+
734
+ /* Text controls, shared by `/forms/{name}` and the comment form — the same
735
+ input surface `.cg-search__form input` above uses. */
736
+ .cg-search__form input,
737
+ .cg-form__field input,
738
+ .cg-form__field select,
739
+ .cg-form__field textarea,
740
+ .cg-comment__form input,
741
+ .cg-comment__form textarea {
742
+ inline-size: 100%;
743
+ padding: var(--cg-s3) var(--cg-s4);
744
+ border: 1px solid var(--cg-line);
745
+ border-radius: var(--cg-radius-md);
746
+ background: var(--cg-surface);
747
+ color: var(--cg-ink);
748
+ font: inherit;
749
+ }
750
+
751
+ .cg-form__field input[aria-invalid="true"],
752
+ .cg-form__field select[aria-invalid="true"],
753
+ .cg-form__field textarea[aria-invalid="true"] {
754
+ border-width: 2px;
755
+ border-color: var(--cg-line-strong);
756
+ }
757
+
758
+ .cg-search__form button,
759
+ .cg-form button[type="submit"],
760
+ .cg-comment__form button[type="submit"] {
761
+ padding: var(--cg-s3) var(--cg-s6);
762
+ border: 0;
763
+ border-radius: var(--cg-radius-md);
764
+ background: var(--cg-accent);
765
+ color: var(--cg-accent-fg);
766
+ font-weight: var(--cg-weight-semibold);
767
+ cursor: pointer;
768
+ transition: background-color var(--cg-duration) var(--cg-ease);
769
+ }
770
+
771
+ .cg-search__form button:hover,
772
+ .cg-form button[type="submit"]:hover,
773
+ .cg-comment__form button[type="submit"]:hover {
774
+ background: var(--cg-accent-hover);
775
+ }
776
+
777
+ .cg-form__field,
778
+ .cg-comment__form p {
779
+ display: block;
780
+ margin-block-end: var(--cg-s5);
781
+ }
782
+
783
+ .cg-form__field label,
784
+ .cg-comment__form label {
785
+ display: block;
786
+ font-size: var(--cg-text-sm);
787
+ font-weight: var(--cg-weight-semibold);
788
+ margin-block-end: var(--cg-s2);
789
+ }
790
+
791
+ .cg-form__help {
792
+ margin: var(--cg-s2) 0 0;
793
+ font-size: var(--cg-text-sm);
794
+ color: var(--cg-ink-muted);
795
+ }
796
+
797
+ .cg-form__choice {
798
+ display: inline-flex;
799
+ align-items: center;
800
+ gap: var(--cg-s2);
801
+ margin-inline-end: var(--cg-s4);
802
+ font-weight: normal;
803
+ }
804
+
805
+ /* Visually hidden, not `display:none`: the honeypot must stay reachable by a
806
+ naive bot's "fill every input" script while never appearing for anyone
807
+ else — the same reasoning the inline style already carried before this
808
+ class existed to do it declaratively for `comments.ts`. */
809
+ .cg-form__honeypot,
810
+ .cg-comment__honeypot {
811
+ position: absolute;
812
+ inline-size: 1px;
813
+ block-size: 1px;
814
+ overflow: hidden;
815
+ inset-inline-start: -9999px;
816
+ }
817
+
818
+ .cg-comments {
819
+ margin-block-start: var(--cg-section-y);
820
+ padding-block-start: var(--cg-s8);
821
+ padding-block-end: var(--cg-section-y);
822
+ border-block-start: 1px solid var(--cg-line-soft);
823
+ }
824
+
825
+ .cg-comments__list,
826
+ .cg-comment__replies {
827
+ margin: 0;
828
+ padding: 0;
829
+ list-style: none;
830
+ }
831
+
832
+ .cg-comment {
833
+ padding-block: var(--cg-s5);
834
+ border-block-end: 1px solid var(--cg-line-soft);
835
+ }
836
+
837
+ .cg-comment__replies {
838
+ padding-block-start: var(--cg-s5);
839
+ }
840
+
841
+ .cg-comment__replies .cg-comment {
842
+ padding-inline-start: var(--cg-s6);
843
+ border-inline-start: 2px solid var(--cg-line-soft);
844
+ border-block-end: 0;
845
+ }
846
+
847
+ .cg-comment__meta {
848
+ display: flex;
849
+ gap: var(--cg-s3);
850
+ align-items: baseline;
851
+ font-size: var(--cg-text-sm);
852
+ color: var(--cg-ink-muted);
853
+ margin-block-end: var(--cg-s2);
854
+ }
855
+
856
+ .cg-comment__author {
857
+ font-weight: var(--cg-weight-semibold);
858
+ color: var(--cg-ink);
859
+ }
860
+
861
+ .cg-comment__body {
862
+ margin: 0;
863
+ }