@invisibleloop/pulse 0.12.3 → 0.14.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 (181) hide show
  1. package/.claude/commands/build-page.md +5 -1
  2. package/.claude/commands/verify.md +31 -17
  3. package/.claude/pulse-checklist.md +21 -1
  4. package/.github/workflows/publish.yml +1 -0
  5. package/.pulse-verified +1 -0
  6. package/CLAUDE.md +56 -15
  7. package/docs/.claude/commands/verify.md +4 -0
  8. package/docs/.claude/pulse-checklist.md +20 -2
  9. package/docs/public/.pulse-ui-version +1 -1
  10. package/docs/public/andypixel.css +237 -0
  11. package/docs/public/bookworms.css +422 -0
  12. package/docs/public/coutumes.css +584 -0
  13. package/docs/public/images/bookworms/hero-books.jpg +0 -0
  14. package/docs/public/images/bookworms/how-we-read-now.jpg +0 -0
  15. package/docs/public/images/bookworms/lantern-keepers.jpg +0 -0
  16. package/docs/public/images/bookworms/provincial-bookseller.jpg +0 -0
  17. package/docs/public/images/journey/178.jpg +0 -0
  18. package/docs/public/images/journey/29.jpg +0 -0
  19. package/docs/public/images/retrowheels/hero.jpg +0 -0
  20. package/docs/public/images/shop/devbook-pro.jpg +0 -0
  21. package/docs/public/images/suzukibento/1015.jpg +0 -0
  22. package/docs/public/images/suzukibento/1025.jpg +0 -0
  23. package/docs/public/images/suzukibento/1043.jpg +0 -0
  24. package/docs/public/images/suzukibento/1059.jpg +0 -0
  25. package/docs/public/images/suzukibento/1062.jpg +0 -0
  26. package/docs/public/images/suzukibento/1084.jpg +0 -0
  27. package/docs/public/images/swish/1469334031218-e382a71b716b.jpg +0 -0
  28. package/docs/public/images/swish/1483985988355-763728e1935b.jpg +0 -0
  29. package/docs/public/images/swish/1485518882345-15568b007407.jpg +0 -0
  30. package/docs/public/images/swish/1490481651871-ab68de25d43d.jpg +0 -0
  31. package/docs/public/images/swish/1496747611176-843222e1e57c.jpg +0 -0
  32. package/docs/public/images/swish/1502716119720-b23a93e5fe1b.jpg +0 -0
  33. package/docs/public/images/swish/1539109136881-3be0616acf4b.jpg +0 -0
  34. package/docs/public/images/swish/1551803091-e20673f15770.jpg +0 -0
  35. package/docs/public/images/swish/1558769132-cb1aea458c5e.jpg +0 -0
  36. package/docs/public/images/swish/1572804013309-59a88b7e92f1.jpg +0 -0
  37. package/docs/public/journey.css +528 -0
  38. package/docs/public/pulse-ui.css +1036 -22
  39. package/docs/public/retrowheels.css +402 -0
  40. package/docs/public/shop.css +235 -0
  41. package/docs/public/sparkys.css +349 -0
  42. package/docs/public/suzukibento.css +517 -0
  43. package/docs/public/swish.css +394 -0
  44. package/docs/server.js +19 -4
  45. package/docs/src/lib/nav.js +14 -3
  46. package/docs/src/pages/components/decorate.js +74 -0
  47. package/docs/src/pages/components/gallery.js +102 -0
  48. package/docs/src/pages/components/hero.js +1 -0
  49. package/docs/src/pages/components/marquee.js +76 -0
  50. package/docs/src/pages/components/photo-card.js +74 -0
  51. package/docs/src/pages/components/section.js +40 -1
  52. package/docs/src/pages/meta.js +26 -0
  53. package/docs/src/pages/templates/andypixel.js +260 -0
  54. package/docs/src/pages/templates/bookworms.js +227 -0
  55. package/docs/src/pages/templates/coutumes.js +226 -0
  56. package/docs/src/pages/templates/journey.js +186 -0
  57. package/docs/src/pages/templates/retrowheels.js +342 -0
  58. package/docs/src/pages/templates/shop.js +257 -0
  59. package/docs/src/pages/templates/sparkys.js +249 -0
  60. package/docs/src/pages/templates/suzukibento.js +200 -0
  61. package/docs/src/pages/templates/swish.js +313 -0
  62. package/docs/src/pages/templates.js +313 -2
  63. package/docs/src/pages/testing.js +2 -1
  64. package/examples/.claude/commands/pulse-build.md +7 -0
  65. package/examples/.claude/commands/pulse-dev.md +7 -0
  66. package/examples/.claude/commands/pulse-start.md +9 -0
  67. package/examples/.claude/commands/pulse-stop.md +5 -0
  68. package/examples/.claude/commands/verify.md +84 -0
  69. package/examples/.claude/coverage-check.js +127 -0
  70. package/examples/.claude/pulse-checklist.md +177 -0
  71. package/examples/andypixel.css +237 -0
  72. package/examples/andypixel.js +258 -0
  73. package/examples/bookworms.css +422 -0
  74. package/examples/bookworms.js +225 -0
  75. package/examples/contact.js +1 -0
  76. package/examples/counter.js +1 -0
  77. package/examples/dev.server.js +17 -0
  78. package/examples/journey.css +528 -0
  79. package/examples/journey.js +186 -0
  80. package/examples/pricing.js +1 -0
  81. package/examples/public/.pulse-ui-version +1 -0
  82. package/examples/public/pulse-ui.css +4031 -0
  83. package/examples/public/pulse-ui.js +257 -0
  84. package/examples/quiz.js +1 -0
  85. package/examples/retrowheels.css +402 -0
  86. package/examples/retrowheels.js +340 -0
  87. package/examples/shared.js +6 -0
  88. package/examples/suzukibento.css +513 -0
  89. package/examples/suzukibento.js +200 -0
  90. package/examples/swish.css +394 -0
  91. package/examples/swish.js +311 -0
  92. package/examples/todos.js +1 -0
  93. package/package.json +2 -3
  94. package/public/.pulse-ui-version +1 -1
  95. package/public/images/bookworms/hero-books.jpg +0 -0
  96. package/public/images/bookworms/how-we-read-now.jpg +0 -0
  97. package/public/images/bookworms/lantern-keepers.jpg +0 -0
  98. package/public/images/bookworms/provincial-bookseller.jpg +0 -0
  99. package/public/images/journey/178.jpg +0 -0
  100. package/public/images/journey/29.jpg +0 -0
  101. package/public/images/retrowheels/hero.jpg +0 -0
  102. package/public/images/shop/devbook-pro.jpg +0 -0
  103. package/public/images/suzukibento/1015.jpg +0 -0
  104. package/public/images/suzukibento/1025.jpg +0 -0
  105. package/public/images/suzukibento/1043.jpg +0 -0
  106. package/public/images/suzukibento/1059.jpg +0 -0
  107. package/public/images/suzukibento/1062.jpg +0 -0
  108. package/public/images/suzukibento/1084.jpg +0 -0
  109. package/public/images/swish/1469334031218-e382a71b716b.jpg +0 -0
  110. package/public/images/swish/1483985988355-763728e1935b.jpg +0 -0
  111. package/public/images/swish/1485518882345-15568b007407.jpg +0 -0
  112. package/public/images/swish/1490481651871-ab68de25d43d.jpg +0 -0
  113. package/public/images/swish/1496747611176-843222e1e57c.jpg +0 -0
  114. package/public/images/swish/1502716119720-b23a93e5fe1b.jpg +0 -0
  115. package/public/images/swish/1539109136881-3be0616acf4b.jpg +0 -0
  116. package/public/images/swish/1551803091-e20673f15770.jpg +0 -0
  117. package/public/images/swish/1558769132-cb1aea458c5e.jpg +0 -0
  118. package/public/images/swish/1572804013309-59a88b7e92f1.jpg +0 -0
  119. package/public/pulse-ui.css +1080 -22
  120. package/scripts/build.js +72 -18
  121. package/src/agent/checklist.md +22 -2
  122. package/src/agent/commands/verify.md +4 -0
  123. package/src/agent/guide-components.md +146 -8
  124. package/src/agent/guide-design-gallery.md +288 -0
  125. package/src/agent/guide-design-references.md +172 -0
  126. package/src/agent/guide-explore.md +313 -0
  127. package/src/agent/guide-styles.md +212 -11
  128. package/src/agent/guide-templates.md +223 -10
  129. package/src/agent/guide.md +9 -4
  130. package/src/agent/identity.md +2 -1
  131. package/src/agent/skills/verify/SKILL.md +64 -28
  132. package/src/agent/workflow.md +247 -61
  133. package/src/cli/dev.js +39 -14
  134. package/src/cli/fmt.js +113 -0
  135. package/src/cli/index.js +60 -33
  136. package/src/cli/logger.js +145 -0
  137. package/src/cli/scaffold.js +57 -3
  138. package/src/html.js +6 -1
  139. package/src/mcp/server.js +2561 -99
  140. package/src/mcp/validate-worker.js +49 -5
  141. package/src/runtime/ssr.js +4 -1
  142. package/src/server/index.js +29 -5
  143. package/src/spec/schema.js +21 -0
  144. package/src/spec/schema.test.js +28 -0
  145. package/src/testing/html.js +130 -4
  146. package/src/testing/testing.test.js +58 -0
  147. package/src/ui/banner.js +24 -9
  148. package/src/ui/brand-statement.js +58 -0
  149. package/src/ui/button.js +13 -2
  150. package/src/ui/card.js +20 -1
  151. package/src/ui/colophon.js +29 -0
  152. package/src/ui/contact-row.js +41 -0
  153. package/src/ui/cta.js +18 -4
  154. package/src/ui/decorate.js +55 -0
  155. package/src/ui/display-heading.js +44 -0
  156. package/src/ui/drop-cap.js +29 -0
  157. package/src/ui/editorial-hero.js +63 -0
  158. package/src/ui/entry-list.js +36 -0
  159. package/src/ui/feature.js +20 -3
  160. package/src/ui/footer.js +78 -9
  161. package/src/ui/gallery.js +56 -0
  162. package/src/ui/hero.js +68 -18
  163. package/src/ui/image-pair.js +72 -0
  164. package/src/ui/index.js +20 -0
  165. package/src/ui/input.js +1 -0
  166. package/src/ui/marquee.js +48 -0
  167. package/src/ui/nav.js +19 -9
  168. package/src/ui/photo-card.js +44 -0
  169. package/src/ui/product-card.js +65 -0
  170. package/src/ui/pullquote.js +14 -3
  171. package/src/ui/search.js +1 -0
  172. package/src/ui/section-label.js +38 -0
  173. package/src/ui/section.js +1 -1
  174. package/src/ui/service-card.js +47 -0
  175. package/src/ui/stat-grid.js +40 -0
  176. package/src/ui/stat.js +16 -2
  177. package/src/ui/testimonial.js +17 -6
  178. package/src/ui/textarea.js +1 -0
  179. package/src/ui/top-strip.js +47 -0
  180. package/src/ui/trust-list.js +46 -0
  181. package/src/ui/ui.test.js +35 -0
@@ -25,6 +25,8 @@
25
25
  --ui-accent-hover: var(--accent-hover, #b5aaff);
26
26
  --ui-accent-dim: var(--accent-dim, rgba(155,141,255,.12));
27
27
  --ui-accent-text: var(--accent-text, #0d0a20);
28
+ /* Inline code in dark theme — accent passes WCAG AA on surface-2 by default */
29
+ --ui-code-color: var(--code-color, var(--ui-accent));
28
30
  --ui-green: var(--green, #3dd68c);
29
31
  --ui-green-dim: rgba(61, 214, 140, .12);
30
32
  --ui-red: var(--red, #ff6b6b);
@@ -36,7 +38,9 @@
36
38
  --ui-radius: var(--radius, 8px);
37
39
  --ui-radius-sm: 4px;
38
40
  --ui-font: var(--font, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
41
+ --ui-font-display: var(--font-display, var(--ui-font));
39
42
  --ui-mono: var(--mono, 'Fira Code', 'Cascadia Code', monospace);
43
+ --ui-letter-spacing-display: var(--letter-spacing-display, -0.025em);
40
44
 
41
45
  /* Touch target minimum per WCAG 2.5.5 */
42
46
  --ui-touch-min: 44px;
@@ -146,6 +150,20 @@ html, body {
146
150
  color: var(--ui-text);
147
151
  }
148
152
 
153
+ /* ghost-light — use on dark section backgrounds (e.g. navy hero, section--dark).
154
+ Renders with light/white text and a subtle light border so it remains legible
155
+ regardless of whether the surrounding theme is light or dark. */
156
+ .ui-btn--ghost-light {
157
+ background: transparent;
158
+ color: rgba(255, 255, 255, 0.9);
159
+ border: 1px solid rgba(255, 255, 255, 0.35);
160
+ }
161
+ .ui-btn--ghost-light:hover:not(.ui-btn--disabled):not([disabled]) {
162
+ background: rgba(255, 255, 255, 0.1);
163
+ border-color: rgba(255, 255, 255, 0.65);
164
+ color: #ffffff;
165
+ }
166
+
149
167
  .ui-btn--danger {
150
168
  background: var(--ui-red-dim);
151
169
  color: var(--ui-red);
@@ -230,6 +248,36 @@ html, body {
230
248
 
231
249
  .ui-card--flush .ui-card-body { padding: 0; }
232
250
 
251
+ /* Card variants */
252
+ .ui-card--elevated {
253
+ box-shadow: 0 4px 24px rgba(0,0,0,.22), 0 1px 4px rgba(0,0,0,.12);
254
+ border-color: transparent;
255
+ }
256
+ .ui-card--bordered {
257
+ box-shadow: none;
258
+ border-width: 2px;
259
+ }
260
+ .ui-card--flat {
261
+ box-shadow: none;
262
+ border-color: transparent;
263
+ }
264
+ .ui-card--glass {
265
+ background: rgba(255,255,255,.06);
266
+ border-color: rgba(255,255,255,.12);
267
+ box-shadow: 0 4px 24px rgba(0,0,0,.18);
268
+ backdrop-filter: blur(12px);
269
+ -webkit-backdrop-filter: blur(12px);
270
+ }
271
+ [data-theme="light"] .ui-card--glass {
272
+ background: rgba(255,255,255,.72);
273
+ border-color: rgba(0,0,0,.08);
274
+ }
275
+ .ui-card--tinted {
276
+ background: var(--ui-accent-dim, rgba(99,102,241,.1));
277
+ border-color: var(--ui-accent-dim, rgba(99,102,241,.2));
278
+ box-shadow: none;
279
+ }
280
+
233
281
  .ui-card-footer {
234
282
  padding: 1rem 1.25rem;
235
283
  border-top: 1px solid var(--ui-border);
@@ -527,6 +575,12 @@ html, body {
527
575
  .ui-stat--center { align-items: center; text-align: center; }
528
576
  .ui-stat--center .ui-stat-change { margin-inline: auto; }
529
577
 
578
+ /* Stat sizes */
579
+ .ui-stat--sm .ui-stat-value { font-size: 1.25rem; letter-spacing: -.01em; }
580
+ .ui-stat--sm .ui-stat-label { font-size: .72rem; }
581
+ .ui-stat--lg .ui-stat-value { font-size: 3.5rem; letter-spacing: -.04em; line-height: .9; }
582
+ .ui-stat--lg .ui-stat-label { font-size: .9rem; }
583
+
530
584
  /* ─── Avatar ─────────────────────────────────────────────────────────────── */
531
585
  .ui-avatar {
532
586
  display: inline-flex;
@@ -642,6 +696,54 @@ html, body {
642
696
  .ui-section--alt { background: var(--ui-surface, var(--surface, #111116)); }
643
697
  .ui-section--dark { background: var(--ui-surface-2, var(--surface-2, #18181f)); }
644
698
 
699
+ /* In light theme, --ui-surface-2 resolves to a light grey — not "dark".
700
+ Override so section--dark always renders a genuinely dark background that
701
+ contrasts with the page, regardless of the active theme. */
702
+ [data-theme="light"] .ui-section--dark,
703
+ .ui-theme-light .ui-section--dark {
704
+ background: var(--ui-dark-bg, #1a1a2e);
705
+ color: var(--ui-dark-text, #e2e2ea);
706
+ }
707
+ /* Ensure text children inherit the light colour in the dark section */
708
+ [data-theme="light"] .ui-section--dark .ui-section-title,
709
+ [data-theme="light"] .ui-section--dark .ui-section-eyebrow,
710
+ [data-theme="light"] .ui-section--dark .ui-section-subtitle,
711
+ .ui-theme-light .ui-section--dark .ui-section-title,
712
+ .ui-theme-light .ui-section--dark .ui-section-eyebrow,
713
+ .ui-theme-light .ui-section--dark .ui-section-subtitle {
714
+ color: inherit;
715
+ }
716
+ /* Ghost button inside a dark section on a light-theme page */
717
+ [data-theme="light"] .ui-section--dark .ui-btn--ghost,
718
+ .ui-theme-light .ui-section--dark .ui-btn--ghost {
719
+ color: var(--ui-dark-text, #e2e2ea);
720
+ border-color: rgba(226, 226, 234, 0.35);
721
+ }
722
+ [data-theme="light"] .ui-section--dark .ui-btn--ghost:hover:not(.ui-btn--disabled):not([disabled]),
723
+ .ui-theme-light .ui-section--dark .ui-btn--ghost:hover:not(.ui-btn--disabled):not([disabled]) {
724
+ background: rgba(226, 226, 234, 0.1);
725
+ border-color: rgba(226, 226, 234, 0.65);
726
+ color: var(--ui-dark-text, #e2e2ea);
727
+ }
728
+
729
+ /* diagonal — tapered bottom edge; follow with a section that starts flush */
730
+ .ui-section--diagonal {
731
+ background: var(--ui-surface, var(--surface, #111116));
732
+ padding-bottom: calc(5rem + 3.5rem);
733
+ clip-path: polygon(0 0, 100% 0, 100% calc(100% - 3.5rem), 0 100%);
734
+ }
735
+ /* paper — subtle dot-grid texture on a raised surface */
736
+ .ui-section--paper {
737
+ position: relative;
738
+ background-color: var(--ui-surface, var(--surface, #111116));
739
+ background-image: radial-gradient(circle, var(--ui-border) 1px, transparent 0);
740
+ background-size: 22px 22px;
741
+ }
742
+ /* spotlight — accent glow from center top */
743
+ .ui-section--spotlight {
744
+ background: radial-gradient(ellipse 70% 60% at 50% 30%, var(--ui-accent-dim, rgba(155,141,255,.15)), var(--ui-surface, #111116) 80%);
745
+ }
746
+
645
747
  /* ─── Grid ───────────────────────────────────────────────────────────────── */
646
748
  .ui-grid { display: grid; gap: 2rem; }
647
749
 
@@ -732,6 +834,35 @@ hr.ui-divider {
732
834
  color: #ffc107;
733
835
  }
734
836
 
837
+ .ui-banner--bottom {
838
+ position: fixed;
839
+ bottom: 0;
840
+ left: 0;
841
+ right: 0;
842
+ z-index: 200;
843
+ display: flex;
844
+ align-items: center;
845
+ justify-content: center;
846
+ gap: 1rem;
847
+ }
848
+
849
+ .ui-banner-close {
850
+ position: absolute;
851
+ right: 1rem;
852
+ top: 50%;
853
+ transform: translateY(-50%);
854
+ background: none;
855
+ border: none;
856
+ cursor: pointer;
857
+ color: inherit;
858
+ font-size: 1.1rem;
859
+ line-height: 1;
860
+ padding: 0.25rem 0.5rem;
861
+ opacity: 0.7;
862
+ transition: opacity 0.15s;
863
+ }
864
+ .ui-banner-close:hover { opacity: 1; }
865
+
735
866
  /* ─── Media ──────────────────────────────────────────────────────────────── */
736
867
  .ui-media {
737
868
  display: grid;
@@ -814,7 +945,8 @@ hr.ui-divider {
814
945
  font-size: clamp(2.2rem, 6vw, 3.5rem);
815
946
  font-weight: 800;
816
947
  line-height: 1.1;
817
- letter-spacing: -.02em;
948
+ letter-spacing: var(--ui-letter-spacing-display, -.02em);
949
+ font-family: var(--ui-font-display, var(--ui-font));
818
950
  color: var(--ui-text, var(--text, #e2e2ea));
819
951
  margin: 0 0 1.25rem;
820
952
  }
@@ -834,10 +966,10 @@ hr.ui-divider {
834
966
  }
835
967
  .ui-hero--left .ui-hero-actions { justify-content: flex-start; }
836
968
 
837
- /* Split (text + image) */
969
+ /* Split (text + image, 50/50) */
838
970
  .ui-hero--split { text-align: left; }
839
971
  .ui-hero--split .ui-hero-inner {
840
- max-width: 1200px;
972
+ max-width: 1100px;
841
973
  margin: 0 auto;
842
974
  display: grid;
843
975
  grid-template-columns: 1fr 1fr;
@@ -849,6 +981,58 @@ hr.ui-divider {
849
981
  .ui-hero--split .ui-hero-media img { width: 100%; height: auto; border-radius: var(--ui-radius, 8px); display: block; }
850
982
  .ui-hero--media-left .ui-hero-media { order: -1; }
851
983
 
984
+ /* Asymmetric (text 60%, image 40%) */
985
+ .ui-hero--asymmetric { text-align: left; }
986
+ .ui-hero--asymmetric .ui-hero-inner {
987
+ max-width: 1100px;
988
+ margin: 0 auto;
989
+ display: grid;
990
+ grid-template-columns: 3fr 2fr;
991
+ gap: 3rem;
992
+ align-items: center;
993
+ }
994
+ .ui-hero--asymmetric .ui-hero-actions { justify-content: flex-start; }
995
+ .ui-hero--asymmetric .ui-hero-media { display: flex; align-items: center; justify-content: center; }
996
+ .ui-hero--asymmetric .ui-hero-media img { width: 100%; height: auto; border-radius: var(--ui-radius, 8px); display: block; }
997
+ .ui-hero--asymmetric.ui-hero--media-left .ui-hero-media { order: -1; }
998
+ .ui-hero--asymmetric.ui-hero--media-left .ui-hero-inner { grid-template-columns: 2fr 3fr; }
999
+
1000
+ /* Overlap (image fills section, text overlaid) */
1001
+ .ui-hero--overlap {
1002
+ position: relative;
1003
+ min-height: 60vh;
1004
+ overflow: hidden;
1005
+ padding: 0;
1006
+ display: flex;
1007
+ align-items: flex-end;
1008
+ text-align: left;
1009
+ }
1010
+ .ui-hero--overlap .ui-hero-overlap-media {
1011
+ position: absolute;
1012
+ inset: 0;
1013
+ z-index: 0;
1014
+ }
1015
+ .ui-hero--overlap .ui-hero-overlap-media img,
1016
+ .ui-hero--overlap .ui-hero-overlap-media video {
1017
+ width: 100%;
1018
+ height: 100%;
1019
+ object-fit: cover;
1020
+ display: block;
1021
+ }
1022
+ .ui-hero--overlap .ui-hero-inner {
1023
+ position: relative;
1024
+ z-index: 1;
1025
+ width: 100%;
1026
+ max-width: none;
1027
+ padding: 4rem 2rem;
1028
+ background: linear-gradient(to top, rgba(0,0,0,.75) 0%, rgba(0,0,0,.3) 60%, transparent 100%);
1029
+ }
1030
+ .ui-hero--overlap .ui-hero-content { max-width: 600px; }
1031
+ .ui-hero--overlap .ui-hero-eyebrow { color: rgba(255,255,255,.75); }
1032
+ .ui-hero--overlap .ui-hero-title { color: #fff; text-shadow: 0 2px 8px rgba(0,0,0,.4); }
1033
+ .ui-hero--overlap .ui-hero-subtitle{ color: rgba(255,255,255,.8); }
1034
+ .ui-hero--overlap .ui-hero-actions { justify-content: flex-start; }
1035
+
852
1036
  /* ─── Testimonial ────────────────────────────────────────────────────────── */
853
1037
  .ui-testimonial {
854
1038
  background: var(--ui-surface, var(--surface, #111116));
@@ -900,6 +1084,8 @@ hr.ui-divider {
900
1084
 
901
1085
  /* ─── Feature ────────────────────────────────────────────────────────────── */
902
1086
  .ui-feature { display: flex; flex-direction: column; gap: .75rem; }
1087
+ .ui-feature-image { width: 100%; border-radius: var(--ui-radius-sm); overflow: hidden; }
1088
+ .ui-feature-image img { width: 100%; height: auto; display: block; }
903
1089
 
904
1090
  .ui-feature-icon {
905
1091
  display: block;
@@ -1248,6 +1434,7 @@ hr.ui-divider {
1248
1434
  position: relative;
1249
1435
  background: var(--ui-bg, var(--bg, #0d0d10));
1250
1436
  border-bottom: 1px solid var(--ui-border, var(--border, #222228));
1437
+ padding-inline: 1.5rem;
1251
1438
  }
1252
1439
 
1253
1440
  .ui-nav--sticky {
@@ -1262,7 +1449,7 @@ hr.ui-divider {
1262
1449
  .ui-nav-inner {
1263
1450
  max-width: 1100px;
1264
1451
  margin: 0 auto;
1265
- padding: .875rem 1.5rem;
1452
+ padding: .875rem 0;
1266
1453
  display: flex;
1267
1454
  align-items: center;
1268
1455
  gap: 2rem;
@@ -1281,6 +1468,7 @@ hr.ui-divider {
1281
1468
  .ui-nav-logo:hover { color: var(--ui-accent, var(--accent, #9b8dff)); }
1282
1469
 
1283
1470
  .ui-nav-links { display: flex; gap: 1.5rem; align-items: center; flex: 1; }
1471
+ .ui-nav-utility { flex: 0; margin-left: auto; }
1284
1472
 
1285
1473
  .ui-nav-link {
1286
1474
  font-size: .875rem;
@@ -1478,7 +1666,9 @@ hr.ui-divider {
1478
1666
  Prefix: u- (distinct from component classes which use ui-) */
1479
1667
 
1480
1668
  /* Screen-reader only — visually hidden but accessible to assistive tech */
1481
- .ui-sr-only {
1669
+ /* u-visually-hidden is an alias — use either */
1670
+ .ui-sr-only,
1671
+ .u-visually-hidden {
1482
1672
  position: absolute;
1483
1673
  width: 1px; height: 1px;
1484
1674
  padding: 0; margin: -1px;
@@ -1658,6 +1848,9 @@ hr.ui-divider {
1658
1848
  .u-opacity-50 { opacity: .5; }
1659
1849
  .u-opacity-75 { opacity: .75; }
1660
1850
 
1851
+ /* Numeric formatting */
1852
+ .u-tabular-nums { font-variant-numeric: tabular-nums; }
1853
+
1661
1854
  /* ─── Section header ─────────────────────────────────────────────────────────
1662
1855
  Optional heading block prepended to section() when eyebrow/title/subtitle
1663
1856
  props are supplied. Sits above the content slot. */
@@ -1678,11 +1871,13 @@ hr.ui-divider {
1678
1871
  }
1679
1872
 
1680
1873
  .ui-section-title {
1681
- font-size: 2rem;
1682
- font-weight: 700;
1683
- line-height: 1.15;
1684
- color: var(--ui-text);
1685
- margin: 0 0 1.25rem;
1874
+ font-size: 2rem;
1875
+ font-weight: 700;
1876
+ line-height: 1.15;
1877
+ letter-spacing: var(--ui-letter-spacing-display, -0.025em);
1878
+ font-family: var(--ui-font-display, var(--ui-font));
1879
+ color: var(--ui-text);
1880
+ margin: 0 0 1.25rem;
1686
1881
  }
1687
1882
 
1688
1883
  .ui-section-subtitle {
@@ -1964,6 +2159,104 @@ hr.ui-divider {
1964
2159
  .ui-footer-links { gap: 1rem; }
1965
2160
  }
1966
2161
 
2162
+ /* ─── Footer — Rich variant ─────────────────────────────────────────────── */
2163
+ .ui-footer--rich {
2164
+ padding: 0;
2165
+ border-top: none;
2166
+ }
2167
+
2168
+ .ui-footer--rich .ui-footer-top {
2169
+ display: flex;
2170
+ justify-content: space-between;
2171
+ align-items: flex-start;
2172
+ gap: 2rem;
2173
+ padding: 3rem 2rem 2rem;
2174
+ flex-wrap: wrap;
2175
+ }
2176
+
2177
+ .ui-footer--rich .ui-footer-logo {
2178
+ font-size: 1.5rem;
2179
+ }
2180
+
2181
+ .ui-footer--rich .ui-footer-subscribe {
2182
+ max-width: 480px;
2183
+ flex: 1;
2184
+ }
2185
+
2186
+ .ui-footer-columns {
2187
+ display: grid;
2188
+ grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
2189
+ gap: 2rem;
2190
+ padding: 2rem 2rem;
2191
+ border-top: 1px solid currentcolor;
2192
+ opacity-on-border: 0.15;
2193
+ }
2194
+
2195
+ .ui-footer-col-title {
2196
+ font-size: 0.6875rem;
2197
+ font-weight: 600;
2198
+ letter-spacing: 0.1em;
2199
+ text-transform: uppercase;
2200
+ margin: 0 0 1rem;
2201
+ color: var(--ui-muted, currentcolor);
2202
+ }
2203
+
2204
+ .ui-footer-col-links {
2205
+ list-style: none;
2206
+ margin: 0;
2207
+ padding: 0;
2208
+ display: flex;
2209
+ flex-direction: column;
2210
+ gap: 0.6rem;
2211
+ }
2212
+
2213
+ .ui-footer-col-links a {
2214
+ display: block;
2215
+ padding: 0.3rem 0;
2216
+ min-height: 24px;
2217
+ font-size: 0.9375rem;
2218
+ color: var(--ui-muted, inherit);
2219
+ text-decoration: none;
2220
+ transition: color 0.15s;
2221
+ }
2222
+ .ui-footer-col-links a:hover { color: var(--ui-text, currentcolor); }
2223
+
2224
+ .ui-footer-wordmark {
2225
+ padding: 1rem 1.5rem 0;
2226
+ overflow: hidden;
2227
+ }
2228
+ .ui-footer-wordmark::after {
2229
+ content: attr(data-wordmark);
2230
+ display: block;
2231
+ font-size: clamp(4rem, 16vw, 14rem);
2232
+ font-weight: 700;
2233
+ letter-spacing: -0.03em;
2234
+ line-height: 0.9;
2235
+ word-break: break-all;
2236
+ }
2237
+
2238
+ .ui-footer-legal-row {
2239
+ display: flex;
2240
+ gap: 1.5rem;
2241
+ align-items: center;
2242
+ flex-wrap: wrap;
2243
+ padding: 1.5rem 2rem;
2244
+ font-size: 0.8125rem;
2245
+ color: var(--ui-muted, currentcolor);
2246
+ border-top: 1px solid var(--ui-border, currentcolor);
2247
+ }
2248
+
2249
+ .ui-footer-legal-row a {
2250
+ color: inherit;
2251
+ text-decoration: none;
2252
+ }
2253
+ .ui-footer-legal-row a:hover { text-decoration: underline; }
2254
+
2255
+ @media (max-width: 640px) {
2256
+ .ui-footer--rich .ui-footer-top { flex-direction: column; }
2257
+ .ui-footer-columns { grid-template-columns: repeat(2, 1fr); }
2258
+ }
2259
+
1967
2260
  /* ─── Icon wrapper ───────────────────────────────────────────────────────────
1968
2261
  Wraps any icon in a tinted background shape.
1969
2262
  bg: 'circle' → border-radius: 50% | bg: 'square' → var(--ui-radius) */
@@ -2820,6 +3113,30 @@ fieldset[disabled] .ui-rating-stars .ui-rating-star:hover ~ .ui-rating-star { co
2820
3113
  font-style: normal;
2821
3114
  }
2822
3115
 
3116
+ /* Editorial variant — centred large serif, no left border */
3117
+ .ui-pullquote--editorial {
3118
+ border-left: none;
3119
+ padding: 4rem 1.5rem;
3120
+ text-align: center;
3121
+ }
3122
+ .ui-pullquote--editorial .ui-pullquote-text {
3123
+ font-size: clamp(1.5rem, 3.5vw, 2.25rem);
3124
+ line-height: 1.4;
3125
+ font-style: italic;
3126
+ font-weight: 400;
3127
+ max-width: 640px;
3128
+ margin-left: auto;
3129
+ margin-right: auto;
3130
+ }
3131
+ .ui-pullquote--editorial.ui-pullquote--lg .ui-pullquote-text {
3132
+ font-size: clamp(1.75rem, 4vw, 3rem);
3133
+ }
3134
+ .ui-pullquote--editorial .ui-pullquote-cite {
3135
+ text-align: center;
3136
+ margin-top: 1.25rem;
3137
+ font-size: 0.9375rem;
3138
+ }
3139
+
2823
3140
  /* ─── List ───────────────────────────────────────────────────────────────────
2824
3141
  Styled list component. Use instead of raw <ul>/<ol>. */
2825
3142
  .ui-list {
@@ -2902,7 +3219,7 @@ fieldset[disabled] .ui-rating-stars .ui-rating-star:hover ~ .ui-rating-star { co
2902
3219
 
2903
3220
  .ui-prose code {
2904
3221
  background: var(--ui-surface-2);
2905
- color: var(--ui-accent);
3222
+ color: var(--ui-code-color);
2906
3223
  padding: .1em .35em;
2907
3224
  border-radius: var(--ui-radius-sm);
2908
3225
  font-size: .875em;
@@ -2989,24 +3306,29 @@ fieldset[disabled] .ui-rating-stars .ui-rating-star:hover ~ .ui-rating-star { co
2989
3306
  All --ui-* tokens are overridden; component markup stays identical. */
2990
3307
  [data-theme="light"],
2991
3308
  .ui-theme-light {
2992
- --ui-bg: #ffffff;
2993
- --ui-surface: #f8f8fc;
2994
- --ui-surface-2: #f0f0f8;
2995
- --ui-border: #d8d8e8;
2996
- --ui-text: #111118;
2997
- --ui-muted: #60607a;
2998
- --ui-accent: #6b5ce7;
2999
- --ui-accent-hover: #5847d6;
3000
- --ui-accent-dim: rgba(107, 92, 231, .12);
3001
- --ui-green: #166534;
3309
+ --ui-bg: var(--bg, #ffffff);
3310
+ --ui-surface: var(--surface, #f8f8fc);
3311
+ --ui-surface-2: var(--surface-2, #f0f0f8);
3312
+ --ui-border: var(--border, #d8d8e8);
3313
+ --ui-text: var(--text, #111118);
3314
+ --ui-muted: var(--muted, #60607a);
3315
+ --ui-accent: var(--accent, #6b5ce7);
3316
+ --ui-accent-hover: var(--accent-hover, #5847d6);
3317
+ --ui-accent-dim: var(--accent-dim, rgba(107, 92, 231, .12));
3318
+ --ui-accent-text: var(--accent-text, #ffffff);
3319
+ /* Inline code uses a slightly darker accent so it passes WCAG AA on --ui-surface-2 */
3320
+ --ui-code-color: var(--code-color, #4a3db5);
3321
+ --ui-green: var(--green, #166534);
3002
3322
  --ui-green-dim: rgba(22, 163, 74, .12);
3003
- --ui-red: #991b1b;
3323
+ --ui-red: var(--red, #991b1b);
3004
3324
  --ui-red-dim: rgba(220, 38, 38, .12);
3005
3325
  --ui-yellow: #92400e;
3006
3326
  --ui-yellow-dim: rgba(217, 119, 6, .12);
3007
3327
  --ui-blue: #1e40af;
3008
3328
  --ui-blue-dim: rgba( 30, 64, 175, .12);
3009
3329
  --ui-nav-sticky-bg: rgba(255, 255, 255, .85);
3330
+ --ui-muted-bg: var(--muted-bg, rgba(0, 0, 0, .04));
3331
+ --ui-shadow: var(--shadow, 0 1px 3px rgba(0,0,0,.10), 0 1px 2px rgba(0,0,0,.06));
3010
3332
  --tok-kw: #7c3aed;
3011
3333
  --tok-str: #15803d;
3012
3334
  --tok-cmt: #6b7280;
@@ -3015,3 +3337,739 @@ fieldset[disabled] .ui-rating-stars .ui-rating-star:hover ~ .ui-rating-star { co
3015
3337
  --tok-op: #0891b2;
3016
3338
  --tok-punct: #6b7280;
3017
3339
  }
3340
+
3341
+ /* ─── Vibe presets ────────────────────────────────────────────────────────────
3342
+ meta.vibe sets data-vibe on <body>. Each vibe adjusts geometry and type
3343
+ tokens without touching colour — pair with a custom theme.css for full
3344
+ personality. All agents: set meta.vibe to get instant personality.
3345
+
3346
+ warm — rounded, friendly, inviting (local businesses, wellness)
3347
+ editorial — sharp, serif display, tight tracking (blogs, publications)
3348
+ playful — very rounded, loose, energetic (kids, lifestyle, food)
3349
+ minimal — razor-sharp, no shadows, refined (portfolios, luxury)
3350
+ bold — impact headings, strong presence (agencies, startups)
3351
+ brutalist — zero radius, oversized type, raw (art, fashion, counter-culture)
3352
+ retro — slab serif, medium radius, warm (vintage, craft, nostalgia)
3353
+ corporate — conservative geometry, tight tracking (B2B, finance, legal)
3354
+ neon — futuristic mono headline, glow-ready (tech, gaming, crypto)
3355
+ paper — organic radius, soft shadows, tactile (journals, bookshops) */
3356
+
3357
+ [data-vibe="warm"] {
3358
+ --ui-radius: 14px;
3359
+ --ui-radius-sm: 8px;
3360
+ --ui-letter-spacing-display: -0.01em;
3361
+ }
3362
+
3363
+ [data-vibe="editorial"] {
3364
+ --ui-radius: 0px;
3365
+ --ui-radius-sm: 0px;
3366
+ --ui-font-display: Georgia, 'Times New Roman', serif;
3367
+ --ui-letter-spacing-display: -0.04em;
3368
+ }
3369
+
3370
+ [data-vibe="playful"] {
3371
+ --ui-radius: 22px;
3372
+ --ui-radius-sm: 14px;
3373
+ --ui-letter-spacing-display: 0em;
3374
+ }
3375
+
3376
+ [data-vibe="minimal"] {
3377
+ --ui-radius: 2px;
3378
+ --ui-radius-sm: 1px;
3379
+ --ui-letter-spacing-display: 0.02em;
3380
+ }
3381
+
3382
+ [data-vibe="bold"] {
3383
+ --ui-radius: 6px;
3384
+ --ui-letter-spacing-display: -0.05em;
3385
+ }
3386
+
3387
+ [data-vibe="brutalist"] {
3388
+ --ui-radius: 0px;
3389
+ --ui-radius-sm: 0px;
3390
+ --ui-font-display: 'Arial Black', 'Impact', sans-serif;
3391
+ --ui-letter-spacing-display: -0.02em;
3392
+ }
3393
+
3394
+ [data-vibe="retro"] {
3395
+ --ui-radius: 8px;
3396
+ --ui-radius-sm: 4px;
3397
+ --ui-font-display: 'Rockwell', 'Courier New', Georgia, serif;
3398
+ --ui-letter-spacing-display: 0.01em;
3399
+ }
3400
+
3401
+ [data-vibe="corporate"] {
3402
+ --ui-radius: 4px;
3403
+ --ui-radius-sm: 2px;
3404
+ --ui-letter-spacing-display: -0.01em;
3405
+ }
3406
+
3407
+ [data-vibe="neon"] {
3408
+ --ui-radius: 3px;
3409
+ --ui-radius-sm: 2px;
3410
+ --ui-font-display: 'Courier New', 'Lucida Console', monospace;
3411
+ --ui-letter-spacing-display: -0.03em;
3412
+ }
3413
+
3414
+ [data-vibe="paper"] {
3415
+ --ui-radius: 10px;
3416
+ --ui-radius-sm: 6px;
3417
+ --ui-font-display: Georgia, 'Palatino Linotype', serif;
3418
+ --ui-letter-spacing-display: -0.02em;
3419
+ }
3420
+
3421
+ /* ─── Gallery ────────────────────────────────────────────────────────────── */
3422
+ .ui-gallery { display: grid; gap: 1rem; }
3423
+ .ui-gallery--cols-1 { grid-template-columns: 1fr; }
3424
+ .ui-gallery--cols-2 { grid-template-columns: repeat(2, 1fr); }
3425
+ .ui-gallery--cols-3 { grid-template-columns: repeat(3, 1fr); }
3426
+ .ui-gallery--cols-4 { grid-template-columns: repeat(4, 1fr); }
3427
+ .ui-gallery--gap-sm { gap: .5rem; }
3428
+ .ui-gallery--gap-lg { gap: 1.5rem; }
3429
+ .ui-gallery-item { margin: 0; }
3430
+ .ui-gallery-crop { overflow: hidden; }
3431
+ .ui-gallery-img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .3s ease; }
3432
+ .ui-gallery--rounded .ui-gallery-crop { border-radius: var(--ui-radius); }
3433
+ .ui-gallery-item:hover .ui-gallery-img { transform: scale(1.03); }
3434
+ .ui-gallery-link { display: block; }
3435
+ .ui-gallery-caption { font-size: .8125rem; color: var(--ui-muted); padding: .5rem 0 0; }
3436
+
3437
+ /* strip: horizontal scroll row */
3438
+ .ui-gallery--strip { grid-template-columns: none; display: flex; overflow-x: auto; gap: 1rem; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; }
3439
+ .ui-gallery--strip .ui-gallery-item { flex: 0 0 auto; width: 280px; scroll-snap-align: start; }
3440
+ .ui-gallery--strip .ui-gallery-crop { aspect-ratio: 3/4; }
3441
+
3442
+ /* masonry: CSS column layout */
3443
+ .ui-gallery--masonry { display: block; columns: 3; column-gap: 1rem; }
3444
+ .ui-gallery--masonry.ui-gallery--cols-2 { columns: 2; }
3445
+ .ui-gallery--masonry.ui-gallery--cols-4 { columns: 4; }
3446
+ .ui-gallery--masonry .ui-gallery-item { break-inside: avoid; margin-bottom: 1rem; }
3447
+ .ui-gallery--masonry .ui-gallery-crop { aspect-ratio: unset; }
3448
+ .ui-gallery--masonry .ui-gallery-img { height: auto; }
3449
+
3450
+ @media (max-width: 640px) {
3451
+ .ui-gallery--cols-3,
3452
+ .ui-gallery--cols-4 { grid-template-columns: repeat(2, 1fr); }
3453
+ .ui-gallery--masonry.ui-gallery--cols-3,
3454
+ .ui-gallery--masonry.ui-gallery--cols-4 { columns: 2; }
3455
+ }
3456
+
3457
+ /* ─── PhotoCard ──────────────────────────────────────────────────────────── */
3458
+ .ui-photo-card {
3459
+ margin: 0;
3460
+ display: inline-flex;
3461
+ flex-direction: column;
3462
+ background: var(--ui-surface);
3463
+ padding: .75rem .75rem .5rem;
3464
+ border: 1px solid var(--ui-border);
3465
+ box-shadow: 0 4px 12px rgba(0,0,0,.25);
3466
+ rotate: var(--photo-tilt, 0deg);
3467
+ transition: rotate .2s ease, box-shadow .2s ease;
3468
+ }
3469
+ .ui-photo-card:hover { box-shadow: 0 8px 24px rgba(0,0,0,.35); }
3470
+ .ui-photo-card--rounded .ui-photo-card-crop { border-radius: var(--ui-radius); overflow: hidden; }
3471
+ .ui-photo-card-crop { overflow: hidden; }
3472
+ .ui-photo-card-img { width: 100%; height: 100%; object-fit: cover; display: block; }
3473
+ .ui-photo-card-caption {
3474
+ font-size: .8125rem;
3475
+ color: var(--ui-muted);
3476
+ padding: .5rem .25rem .25rem;
3477
+ text-align: center;
3478
+ font-family: var(--ui-mono);
3479
+ }
3480
+
3481
+ /* ─── Marquee ────────────────────────────────────────────────────────────── */
3482
+ @keyframes ui-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
3483
+
3484
+ .ui-marquee { overflow: hidden; }
3485
+ .ui-marquee--fade {
3486
+ mask-image: linear-gradient(to right, transparent 0%, #000 10%, #000 90%, transparent 100%);
3487
+ -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 10%, #000 90%, transparent 100%);
3488
+ }
3489
+ .ui-marquee-track {
3490
+ display: flex;
3491
+ width: max-content;
3492
+ animation: ui-marquee var(--marquee-speed, 30s) linear infinite;
3493
+ animation-direction: var(--marquee-dir, normal);
3494
+ list-style: none;
3495
+ margin: 0;
3496
+ padding: 0;
3497
+ gap: 3rem;
3498
+ }
3499
+ .ui-marquee--gap-sm .ui-marquee-track { gap: 1rem; }
3500
+ .ui-marquee--gap-md .ui-marquee-track { gap: 2rem; }
3501
+ .ui-marquee--pause:hover .ui-marquee-track { animation-play-state: paused; }
3502
+ .ui-marquee-item { flex: 0 0 auto; display: flex; align-items: center; }
3503
+
3504
+ @media (prefers-reduced-motion: reduce) {
3505
+ .ui-marquee-track { animation: none; }
3506
+ .ui-marquee { overflow-x: auto; }
3507
+ }
3508
+
3509
+ /* ─── Decorate (SVG pattern overlay) ────────────────────────────────────── */
3510
+ .ui-decorate { pointer-events: none; }
3511
+
3512
+ /* ─── Product Card ───────────────────────────────────────────────────────── */
3513
+ .ui-product-card {
3514
+ position: relative;
3515
+ display: block;
3516
+ overflow: hidden;
3517
+ text-decoration: none;
3518
+ color: inherit;
3519
+ }
3520
+ .ui-product-card-crop {
3521
+ overflow: hidden;
3522
+ }
3523
+ .ui-product-card-img {
3524
+ width: 100%;
3525
+ height: 100%;
3526
+ object-fit: cover;
3527
+ display: block;
3528
+ transition: transform 0.45s cubic-bezier(0.25, 0.46, 0.45, 0.94);
3529
+ }
3530
+ .ui-product-card:hover .ui-product-card-img { transform: scale(1.04); }
3531
+ .ui-product-card:focus-visible { outline: 2px solid var(--ui-accent); outline-offset: 2px; }
3532
+
3533
+ .ui-product-card-overlay {
3534
+ position: absolute;
3535
+ inset: 0;
3536
+ display: flex;
3537
+ flex-direction: column;
3538
+ justify-content: flex-end;
3539
+ padding: 1.25rem;
3540
+ background: linear-gradient(to top, rgba(0,0,0,0.55) 0%, transparent 55%);
3541
+ opacity: 0;
3542
+ transition: opacity 0.3s ease;
3543
+ }
3544
+ .ui-product-card:hover .ui-product-card-overlay { opacity: 1; }
3545
+
3546
+ /* Always-visible overlay for reduced-motion users */
3547
+ @media (prefers-reduced-motion: reduce) {
3548
+ .ui-product-card-img { transition: none; }
3549
+ .ui-product-card-overlay { opacity: 1; }
3550
+ }
3551
+
3552
+ .ui-product-card-name {
3553
+ font-size: 1rem;
3554
+ font-weight: 500;
3555
+ color: #fff;
3556
+ margin: 0;
3557
+ letter-spacing: 0.02em;
3558
+ }
3559
+ .ui-product-card-price {
3560
+ font-size: 0.875rem;
3561
+ color: rgba(255,255,255,0.75);
3562
+ margin: 0.3rem 0 0;
3563
+ }
3564
+
3565
+ .ui-product-card-badge {
3566
+ position: absolute;
3567
+ top: 1rem;
3568
+ left: 1rem;
3569
+ background: var(--ui-text);
3570
+ color: var(--ui-bg);
3571
+ font-size: 0.6875rem;
3572
+ font-weight: 600;
3573
+ letter-spacing: 0.08em;
3574
+ text-transform: uppercase;
3575
+ padding: 0.2em 0.6em;
3576
+ z-index: 1;
3577
+ }
3578
+ .ui-product-card--badge-right .ui-product-card-badge { left: auto; right: 1rem; }
3579
+
3580
+ /* Below layout — caption sits under the image, always visible */
3581
+ .ui-product-card--below {
3582
+ overflow: visible;
3583
+ }
3584
+ .ui-product-card-caption {
3585
+ text-align: center;
3586
+ padding: 0.75rem 0.5rem 0.25rem;
3587
+ background: var(--ui-surface);
3588
+ }
3589
+ .ui-product-card--below .ui-product-card-name {
3590
+ font-size: 1rem;
3591
+ font-weight: 400;
3592
+ color: var(--ui-text);
3593
+ margin: 0 0 0.25rem;
3594
+ font-style: normal;
3595
+ }
3596
+ .ui-product-card--below .ui-product-card-price {
3597
+ font-size: 0.875rem;
3598
+ color: var(--ui-muted);
3599
+ margin: 0;
3600
+ }
3601
+
3602
+ /* ─── Editorial Hero ─────────────────────────────────────────────────────── */
3603
+ .ui-editorial-hero {
3604
+ position: relative;
3605
+ overflow: hidden;
3606
+ }
3607
+
3608
+ .ui-editorial-hero-heading {
3609
+ position: absolute;
3610
+ width: 1px;
3611
+ height: 1px;
3612
+ overflow: hidden;
3613
+ clip: rect(0,0,0,0);
3614
+ margin: -1px;
3615
+ padding: 0;
3616
+ border: 0;
3617
+ }
3618
+
3619
+ .ui-editorial-hero-images {
3620
+ display: grid;
3621
+ grid-template-columns: 1fr 1fr;
3622
+ width: 100%;
3623
+ }
3624
+ .ui-editorial-hero-img {
3625
+ overflow: hidden;
3626
+ }
3627
+ .ui-editorial-hero-img img {
3628
+ width: 100%;
3629
+ height: 100%;
3630
+ object-fit: cover;
3631
+ display: block;
3632
+ }
3633
+
3634
+ .ui-editorial-hero-overlay {
3635
+ position: absolute;
3636
+ inset: 0;
3637
+ display: flex;
3638
+ flex-direction: column;
3639
+ justify-content: center;
3640
+ align-items: center;
3641
+ pointer-events: none;
3642
+ color: var(--ui-accent);
3643
+ mix-blend-mode: multiply;
3644
+ }
3645
+ .ui-editorial-hero-overlay span {
3646
+ display: block;
3647
+ font-size: clamp(3rem, 12vw, 11rem);
3648
+ font-weight: 700;
3649
+ letter-spacing: -0.04em;
3650
+ line-height: 0.9;
3651
+ text-transform: uppercase;
3652
+ white-space: nowrap;
3653
+ }
3654
+
3655
+ .ui-editorial-hero-cta {
3656
+ position: absolute;
3657
+ bottom: 1.5rem;
3658
+ left: 50%;
3659
+ transform: translateX(-50%);
3660
+ font-size: 0.75rem;
3661
+ letter-spacing: 0.1em;
3662
+ text-transform: uppercase;
3663
+ text-decoration: none;
3664
+ color: var(--ui-text);
3665
+ white-space: nowrap;
3666
+ }
3667
+
3668
+ @media (max-width: 640px) {
3669
+ .ui-editorial-hero-overlay span { font-size: clamp(2rem, 10vw, 4rem); }
3670
+ }
3671
+
3672
+ /* ─── Image Pair ─────────────────────────────────────────────────────────── */
3673
+ .ui-image-pair {
3674
+ display: grid;
3675
+ grid-template-columns: 1fr 1fr;
3676
+ width: 100%;
3677
+ }
3678
+ .ui-image-pair--gap-sm { gap: 0.5rem; }
3679
+ .ui-image-pair--gap-md { gap: 1.5rem; }
3680
+
3681
+ .ui-image-pair-col { overflow: hidden; }
3682
+ .ui-image-pair-img { overflow: hidden; }
3683
+ .ui-image-pair-crop { overflow: hidden; }
3684
+ .ui-image-pair-crop img {
3685
+ width: 100%;
3686
+ height: 100%;
3687
+ object-fit: cover;
3688
+ display: block;
3689
+ }
3690
+ .ui-image-pair--captioned .ui-image-pair-col { overflow: visible; }
3691
+
3692
+ .ui-image-pair-link {
3693
+ display: inline-block;
3694
+ margin-top: 0.75rem;
3695
+ font-size: 0.9375rem;
3696
+ text-decoration: none;
3697
+ color: var(--ui-text);
3698
+ }
3699
+ .ui-image-pair-link:hover { opacity: 0.6; }
3700
+
3701
+ @media (max-width: 480px) {
3702
+ .ui-image-pair { grid-template-columns: 1fr; }
3703
+ }
3704
+
3705
+ /* ─── Brand Statement ────────────────────────────────────────────────────── */
3706
+ .ui-brand-statement {
3707
+ padding: 5rem 1.5rem;
3708
+ text-align: center;
3709
+ }
3710
+ .ui-brand-statement--left { text-align: left; }
3711
+ .ui-brand-statement--right { text-align: right; }
3712
+
3713
+ .ui-brand-statement-inner {
3714
+ max-width: 860px;
3715
+ margin: 0 auto;
3716
+ }
3717
+
3718
+ .ui-brand-statement-text {
3719
+ font-size: clamp(1.5rem, 4vw, 2.5rem);
3720
+ line-height: 1.25;
3721
+ font-weight: 400;
3722
+ font-style: italic;
3723
+ margin: 0;
3724
+ color: var(--ui-text);
3725
+ }
3726
+
3727
+ .ui-brand-statement--display .ui-brand-statement-text {
3728
+ font-style: normal;
3729
+ font-weight: 700;
3730
+ }
3731
+
3732
+ .ui-brand-statement--sans .ui-brand-statement-text {
3733
+ font-style: normal;
3734
+ }
3735
+
3736
+ .ui-brand-statement--sm .ui-brand-statement-text { font-size: clamp(1.1rem, 2.5vw, 1.5rem); }
3737
+ .ui-brand-statement--lg .ui-brand-statement-text { font-size: clamp(2rem, 5vw, 3.5rem); }
3738
+
3739
+ .ui-brand-statement-attr {
3740
+ margin-top: 1.5rem;
3741
+ font-size: 0.9375rem;
3742
+ color: var(--ui-muted);
3743
+ font-style: italic;
3744
+ }
3745
+
3746
+ /* ------------------------------------------------------------------ */
3747
+ /* ServiceCard */
3748
+ /* ------------------------------------------------------------------ */
3749
+
3750
+ .ui-service-card {
3751
+ display: block;
3752
+ padding: var(--ui-space-5, 1.25rem);
3753
+ border: 1px solid var(--ui-border);
3754
+ border-radius: var(--ui-radius-md);
3755
+ background: var(--ui-surface);
3756
+ text-decoration: none;
3757
+ color: inherit;
3758
+ }
3759
+
3760
+ .ui-service-card--linked {
3761
+ transition: border-color 0.15s, box-shadow 0.15s;
3762
+ }
3763
+
3764
+ .ui-service-card--linked:hover {
3765
+ border-color: var(--ui-accent);
3766
+ box-shadow: 0 2px 12px var(--ui-shadow, rgba(0,0,0,.08));
3767
+ }
3768
+
3769
+ .ui-service-card-icon {
3770
+ display: inline-flex;
3771
+ align-items: center;
3772
+ justify-content: center;
3773
+ width: 2.5rem;
3774
+ height: 2.5rem;
3775
+ border-radius: var(--ui-radius);
3776
+ background: var(--ui-accent-tint);
3777
+ color: var(--ui-accent);
3778
+ margin-block-end: var(--ui-space-4, 1rem);
3779
+ }
3780
+
3781
+ .ui-service-card-title {
3782
+ font-size: var(--ui-text-md, 1rem);
3783
+ font-weight: 600;
3784
+ margin: 0 0 var(--ui-space-2, 0.5rem);
3785
+ color: var(--ui-text);
3786
+ }
3787
+
3788
+ .ui-service-card-desc {
3789
+ margin: 0;
3790
+ font-size: var(--ui-text-sm, 0.875rem);
3791
+ color: var(--ui-muted);
3792
+ line-height: 1.6;
3793
+ }
3794
+
3795
+ /* ------------------------------------------------------------------ */
3796
+ /* ContactRow */
3797
+ /* ------------------------------------------------------------------ */
3798
+
3799
+ .ui-contact-row {
3800
+ display: flex;
3801
+ flex-wrap: wrap;
3802
+ gap: var(--ui-space-3, 0.75rem) var(--ui-space-6, 1.5rem);
3803
+ list-style: none;
3804
+ margin: 0;
3805
+ padding: 0;
3806
+ }
3807
+
3808
+ .ui-contact-row--sm { font-size: var(--ui-text-sm, 0.875rem); }
3809
+
3810
+ .ui-contact-row-item {
3811
+ display: flex;
3812
+ align-items: center;
3813
+ gap: var(--ui-space-2, 0.5rem);
3814
+ color: var(--ui-muted);
3815
+ }
3816
+
3817
+ .ui-contact-row-item a {
3818
+ color: inherit;
3819
+ text-decoration: none;
3820
+ }
3821
+
3822
+ .ui-contact-row-item a:hover { color: var(--ui-accent); }
3823
+
3824
+ .ui-contact-row-icon {
3825
+ flex-shrink: 0;
3826
+ color: var(--ui-accent);
3827
+ line-height: 0;
3828
+ }
3829
+
3830
+ /* ------------------------------------------------------------------ */
3831
+ /* TrustList */
3832
+ /* ------------------------------------------------------------------ */
3833
+
3834
+ .ui-trust-list {
3835
+ display: flex;
3836
+ flex-wrap: wrap;
3837
+ gap: var(--ui-space-2, 0.5rem) var(--ui-space-5, 1.25rem);
3838
+ list-style: none;
3839
+ margin: 0;
3840
+ padding: 0;
3841
+ }
3842
+
3843
+ .ui-trust-list--md { font-size: var(--ui-text-md, 1rem); }
3844
+
3845
+ .ui-trust-list-item {
3846
+ display: flex;
3847
+ align-items: center;
3848
+ gap: var(--ui-space-2, 0.5rem);
3849
+ font-size: var(--ui-text-sm, 0.875rem);
3850
+ color: var(--ui-muted);
3851
+ }
3852
+
3853
+ .ui-trust-list-icon {
3854
+ flex-shrink: 0;
3855
+ color: var(--ui-accent);
3856
+ line-height: 0;
3857
+ }
3858
+
3859
+ /* ------------------------------------------------------------------ */
3860
+ /* TopStrip */
3861
+ /* ------------------------------------------------------------------ */
3862
+
3863
+ .ui-top-strip {
3864
+ display: flex;
3865
+ align-items: center;
3866
+ justify-content: space-between;
3867
+ padding: var(--ui-space-2, 0.5rem) var(--ui-space-6, 1.5rem);
3868
+ background: var(--ui-surface-raised, var(--ui-surface));
3869
+ font-size: var(--ui-text-xs, 0.75rem);
3870
+ color: var(--ui-muted);
3871
+ gap: var(--ui-space-4, 1rem);
3872
+ }
3873
+
3874
+ .ui-top-strip-content,
3875
+ .ui-top-strip-left,
3876
+ .ui-top-strip-right {
3877
+ display: flex;
3878
+ align-items: center;
3879
+ gap: var(--ui-space-4, 1rem);
3880
+ flex-wrap: wrap;
3881
+ }
3882
+
3883
+ @media (max-width: 640px) {
3884
+ .ui-top-strip {
3885
+ flex-direction: column;
3886
+ align-items: flex-start;
3887
+ gap: var(--ui-space-1, 0.25rem);
3888
+ }
3889
+ }
3890
+
3891
+ /* ------------------------------------------------------------------ */
3892
+ /* StatGrid */
3893
+ /* ------------------------------------------------------------------ */
3894
+
3895
+ .ui-stat-grid {
3896
+ display: grid;
3897
+ gap: var(--ui-space-4, 1rem);
3898
+ grid-template-columns: repeat(var(--stat-cols, 2), 1fr);
3899
+ padding: var(--ui-space-6, 1.5rem);
3900
+ border-radius: var(--ui-radius-md);
3901
+ }
3902
+
3903
+ .ui-stat-grid .ui-stat-value {
3904
+ font-size: clamp(1.5rem, 3vw, 2.25rem);
3905
+ line-height: 1.1;
3906
+ }
3907
+
3908
+ /* Stat suffix — icon/rating appended to the value */
3909
+ .ui-stat-suffix {
3910
+ display: inline-flex;
3911
+ align-items: center;
3912
+ margin-left: 0.2em;
3913
+ font-size: 0.6em;
3914
+ vertical-align: middle;
3915
+ line-height: 0;
3916
+ }
3917
+
3918
+ /* ─── Editorial Components ───────────────────────────────────────────────────
3919
+ Components for editorial/typographic layouts: entryList, displayHeading,
3920
+ sectionLabel, dropCap, colophon. */
3921
+
3922
+ /* Entry List — tabular list for backlists, release notes, changelogs */
3923
+ .ui-entry-list {
3924
+ display: flex;
3925
+ flex-direction: column;
3926
+ gap: 1.5rem;
3927
+ }
3928
+
3929
+ .ui-entry-list-item {
3930
+ display: grid;
3931
+ grid-template-columns: minmax(4rem, max-content) 1fr;
3932
+ gap: 2rem;
3933
+ align-items: baseline;
3934
+ }
3935
+
3936
+ @media (max-width: 640px) {
3937
+ .ui-entry-list-item {
3938
+ grid-template-columns: 1fr;
3939
+ gap: 0.5rem;
3940
+ }
3941
+ }
3942
+
3943
+ .ui-entry-list-meta {
3944
+ font-size: 0.9rem;
3945
+ color: var(--ui-muted);
3946
+ font-weight: 600;
3947
+ text-transform: uppercase;
3948
+ letter-spacing: 0.05em;
3949
+ font-variant-numeric: tabular-nums;
3950
+ }
3951
+
3952
+ .ui-entry-list-content {
3953
+ display: flex;
3954
+ flex-direction: column;
3955
+ gap: 0.5rem;
3956
+ }
3957
+
3958
+ .ui-entry-list-title {
3959
+ font-size: 1.1rem;
3960
+ font-weight: 600;
3961
+ color: var(--ui-text);
3962
+ line-height: 1.4;
3963
+ margin: 0;
3964
+ }
3965
+
3966
+ .ui-entry-list-description {
3967
+ font-size: 0.9rem;
3968
+ color: var(--ui-muted);
3969
+ line-height: 1.6;
3970
+ margin: 0;
3971
+ }
3972
+
3973
+ /* Display Heading — large typographic moments */
3974
+ .ui-display-heading {
3975
+ font-size: clamp(2.5rem, 6vw, 5rem);
3976
+ font-weight: 800;
3977
+ color: var(--ui-text);
3978
+ line-height: 1.1;
3979
+ margin: 0;
3980
+ }
3981
+
3982
+ /* Section Label — eyebrow + h2 + rule */
3983
+ .ui-section-label {
3984
+ display: flex;
3985
+ flex-direction: column;
3986
+ gap: 0.75rem;
3987
+ margin-bottom: 2rem;
3988
+ }
3989
+
3990
+ .ui-section-label--center {
3991
+ align-items: center;
3992
+ text-align: center;
3993
+ }
3994
+
3995
+ .ui-section-label-eyebrow {
3996
+ font-size: 0.85rem;
3997
+ color: var(--ui-muted);
3998
+ font-weight: 600;
3999
+ text-transform: uppercase;
4000
+ letter-spacing: 0.08em;
4001
+ margin: 0;
4002
+ }
4003
+
4004
+ .ui-section-label-heading {
4005
+ font-size: 1.75rem;
4006
+ font-weight: 700;
4007
+ color: var(--ui-text);
4008
+ line-height: 1.2;
4009
+ margin: 0;
4010
+ }
4011
+
4012
+ .ui-section-label-rule {
4013
+ width: 100%;
4014
+ max-width: 8rem;
4015
+ height: 2px;
4016
+ background: var(--ui-border);
4017
+ border: none;
4018
+ margin: 0;
4019
+ }
4020
+
4021
+ .ui-section-label--center .ui-section-label-rule {
4022
+ margin-left: auto;
4023
+ margin-right: auto;
4024
+ }
4025
+
4026
+ /* Drop Cap — first-letter treatment */
4027
+ .ui-drop-cap {
4028
+ margin: 0;
4029
+ line-height: 1.75;
4030
+ color: var(--ui-text);
4031
+ }
4032
+
4033
+ .ui-drop-cap-letter {
4034
+ font-weight: 700;
4035
+ color: var(--ui-text);
4036
+ }
4037
+
4038
+ /* Colophon — minimal footer for editorial pages */
4039
+ .ui-colophon {
4040
+ padding: 3rem 1.5rem 2rem;
4041
+ border-top: 1px solid var(--ui-border);
4042
+ margin-top: 4rem;
4043
+ text-align: center;
4044
+ }
4045
+
4046
+ .ui-colophon--left {
4047
+ text-align: left;
4048
+ }
4049
+
4050
+ .ui-colophon-inner {
4051
+ max-width: 640px;
4052
+ margin: 0 auto;
4053
+ font-size: 0.85rem;
4054
+ color: var(--ui-muted);
4055
+ line-height: 1.6;
4056
+ }
4057
+
4058
+ .ui-colophon-inner p {
4059
+ margin: 0.5em 0;
4060
+ }
4061
+
4062
+ .ui-colophon-inner p:last-child {
4063
+ margin-bottom: 0;
4064
+ }
4065
+
4066
+ .ui-colophon-inner a {
4067
+ color: var(--ui-muted);
4068
+ text-decoration: underline;
4069
+ text-underline-offset: 2px;
4070
+ transition: color 0.15s;
4071
+ }
4072
+
4073
+ .ui-colophon-inner a:hover {
4074
+ color: var(--ui-text);
4075
+ }