@invisibleloop/pulse 0.12.2 → 0.14.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 (170) hide show
  1. package/.claude/commands/verify.md +9 -5
  2. package/.claude/pulse-checklist.md +21 -1
  3. package/.github/workflows/publish.yml +1 -0
  4. package/.pulse-verified +1 -0
  5. package/CLAUDE.md +56 -15
  6. package/docs/.claude/commands/verify.md +4 -0
  7. package/docs/.claude/pulse-checklist.md +36 -2
  8. package/docs/public/.pulse-ui-version +1 -1
  9. package/docs/public/andypixel.css +237 -0
  10. package/docs/public/bookworms.css +422 -0
  11. package/docs/public/coutumes.css +584 -0
  12. package/docs/public/images/bookworms/hero-books.jpg +0 -0
  13. package/docs/public/images/bookworms/how-we-read-now.jpg +0 -0
  14. package/docs/public/images/bookworms/lantern-keepers.jpg +0 -0
  15. package/docs/public/images/bookworms/provincial-bookseller.jpg +0 -0
  16. package/docs/public/images/journey/178.jpg +0 -0
  17. package/docs/public/images/journey/29.jpg +0 -0
  18. package/docs/public/images/retrowheels/hero.jpg +0 -0
  19. package/docs/public/images/shop/devbook-pro.jpg +0 -0
  20. package/docs/public/images/suzukibento/1015.jpg +0 -0
  21. package/docs/public/images/suzukibento/1025.jpg +0 -0
  22. package/docs/public/images/suzukibento/1043.jpg +0 -0
  23. package/docs/public/images/suzukibento/1059.jpg +0 -0
  24. package/docs/public/images/suzukibento/1062.jpg +0 -0
  25. package/docs/public/images/suzukibento/1084.jpg +0 -0
  26. package/docs/public/images/swish/1469334031218-e382a71b716b.jpg +0 -0
  27. package/docs/public/images/swish/1483985988355-763728e1935b.jpg +0 -0
  28. package/docs/public/images/swish/1485518882345-15568b007407.jpg +0 -0
  29. package/docs/public/images/swish/1490481651871-ab68de25d43d.jpg +0 -0
  30. package/docs/public/images/swish/1496747611176-843222e1e57c.jpg +0 -0
  31. package/docs/public/images/swish/1502716119720-b23a93e5fe1b.jpg +0 -0
  32. package/docs/public/images/swish/1539109136881-3be0616acf4b.jpg +0 -0
  33. package/docs/public/images/swish/1551803091-e20673f15770.jpg +0 -0
  34. package/docs/public/images/swish/1558769132-cb1aea458c5e.jpg +0 -0
  35. package/docs/public/images/swish/1572804013309-59a88b7e92f1.jpg +0 -0
  36. package/docs/public/journey.css +528 -0
  37. package/docs/public/pulse-ui.css +1051 -25
  38. package/docs/public/pulse-ui.js +6 -0
  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/phone-frame.js +26 -7
  51. package/docs/src/pages/components/photo-card.js +74 -0
  52. package/docs/src/pages/components/section.js +40 -1
  53. package/docs/src/pages/meta.js +26 -0
  54. package/docs/src/pages/templates/andypixel.js +260 -0
  55. package/docs/src/pages/templates/bookworms.js +227 -0
  56. package/docs/src/pages/templates/coutumes.js +226 -0
  57. package/docs/src/pages/templates/journey.js +186 -0
  58. package/docs/src/pages/templates/retrowheels.js +342 -0
  59. package/docs/src/pages/templates/shop.js +257 -0
  60. package/docs/src/pages/templates/sparkys.js +249 -0
  61. package/docs/src/pages/templates/suzukibento.js +200 -0
  62. package/docs/src/pages/templates/swish.js +313 -0
  63. package/docs/src/pages/templates.js +313 -2
  64. package/docs/src/pages/testing.js +2 -1
  65. package/examples/andypixel.css +237 -0
  66. package/examples/andypixel.js +258 -0
  67. package/examples/bookworms.css +422 -0
  68. package/examples/bookworms.js +225 -0
  69. package/examples/contact.js +1 -0
  70. package/examples/counter.js +1 -0
  71. package/examples/dev.server.js +17 -0
  72. package/examples/journey.css +528 -0
  73. package/examples/journey.js +186 -0
  74. package/examples/pricing.js +1 -0
  75. package/examples/quiz.js +1 -0
  76. package/examples/retrowheels.css +402 -0
  77. package/examples/retrowheels.js +340 -0
  78. package/examples/shared.js +6 -0
  79. package/examples/suzukibento.css +513 -0
  80. package/examples/suzukibento.js +200 -0
  81. package/examples/swish.css +394 -0
  82. package/examples/swish.js +311 -0
  83. package/examples/todos.js +1 -0
  84. package/package.json +1 -1
  85. package/public/.pulse-ui-version +1 -1
  86. package/public/images/bookworms/hero-books.jpg +0 -0
  87. package/public/images/bookworms/how-we-read-now.jpg +0 -0
  88. package/public/images/bookworms/lantern-keepers.jpg +0 -0
  89. package/public/images/bookworms/provincial-bookseller.jpg +0 -0
  90. package/public/images/journey/178.jpg +0 -0
  91. package/public/images/journey/29.jpg +0 -0
  92. package/public/images/retrowheels/hero.jpg +0 -0
  93. package/public/images/shop/devbook-pro.jpg +0 -0
  94. package/public/images/suzukibento/1015.jpg +0 -0
  95. package/public/images/suzukibento/1025.jpg +0 -0
  96. package/public/images/suzukibento/1043.jpg +0 -0
  97. package/public/images/suzukibento/1059.jpg +0 -0
  98. package/public/images/suzukibento/1062.jpg +0 -0
  99. package/public/images/suzukibento/1084.jpg +0 -0
  100. package/public/images/swish/1469334031218-e382a71b716b.jpg +0 -0
  101. package/public/images/swish/1483985988355-763728e1935b.jpg +0 -0
  102. package/public/images/swish/1485518882345-15568b007407.jpg +0 -0
  103. package/public/images/swish/1490481651871-ab68de25d43d.jpg +0 -0
  104. package/public/images/swish/1496747611176-843222e1e57c.jpg +0 -0
  105. package/public/images/swish/1502716119720-b23a93e5fe1b.jpg +0 -0
  106. package/public/images/swish/1539109136881-3be0616acf4b.jpg +0 -0
  107. package/public/images/swish/1551803091-e20673f15770.jpg +0 -0
  108. package/public/images/swish/1558769132-cb1aea458c5e.jpg +0 -0
  109. package/public/images/swish/1572804013309-59a88b7e92f1.jpg +0 -0
  110. package/public/pulse-ui.css +1051 -22
  111. package/public/pulse-ui.js +6 -0
  112. package/scripts/build.js +72 -18
  113. package/src/agent/checklist.md +20 -2
  114. package/src/agent/commands/verify.md +4 -0
  115. package/src/agent/guide-components.md +146 -8
  116. package/src/agent/guide-design-gallery.md +288 -0
  117. package/src/agent/guide-design-references.md +172 -0
  118. package/src/agent/guide-explore.md +313 -0
  119. package/src/agent/guide-styles.md +197 -11
  120. package/src/agent/guide-templates.md +220 -10
  121. package/src/agent/guide.md +9 -4
  122. package/src/agent/workflow.md +208 -58
  123. package/src/cli/dev.js +39 -7
  124. package/src/cli/fmt.js +113 -0
  125. package/src/cli/index.js +63 -30
  126. package/src/cli/scaffold.js +2 -2
  127. package/src/html.js +6 -1
  128. package/src/mcp/server.js +2415 -101
  129. package/src/mcp/validate-worker.js +34 -5
  130. package/src/runtime/ssr.js +4 -1
  131. package/src/server/index.js +22 -2
  132. package/src/spec/schema.js +5 -0
  133. package/src/testing/html.js +130 -4
  134. package/src/testing/testing.test.js +58 -0
  135. package/src/ui/banner.js +24 -9
  136. package/src/ui/brand-statement.js +58 -0
  137. package/src/ui/button.js +12 -1
  138. package/src/ui/card.js +20 -1
  139. package/src/ui/colophon.js +29 -0
  140. package/src/ui/contact-row.js +41 -0
  141. package/src/ui/cta.js +18 -4
  142. package/src/ui/decorate.js +55 -0
  143. package/src/ui/display-heading.js +44 -0
  144. package/src/ui/drop-cap.js +29 -0
  145. package/src/ui/editorial-hero.js +63 -0
  146. package/src/ui/entry-list.js +36 -0
  147. package/src/ui/feature.js +20 -3
  148. package/src/ui/footer.js +78 -9
  149. package/src/ui/gallery.js +56 -0
  150. package/src/ui/hero.js +68 -18
  151. package/src/ui/image-pair.js +72 -0
  152. package/src/ui/index.js +20 -0
  153. package/src/ui/input.js +1 -0
  154. package/src/ui/marquee.js +48 -0
  155. package/src/ui/nav.js +19 -9
  156. package/src/ui/phone-frame.js +6 -5
  157. package/src/ui/photo-card.js +44 -0
  158. package/src/ui/product-card.js +65 -0
  159. package/src/ui/pullquote.js +14 -3
  160. package/src/ui/search.js +1 -0
  161. package/src/ui/section-label.js +38 -0
  162. package/src/ui/section.js +1 -1
  163. package/src/ui/service-card.js +47 -0
  164. package/src/ui/stat-grid.js +40 -0
  165. package/src/ui/stat.js +16 -2
  166. package/src/ui/testimonial.js +17 -6
  167. package/src/ui/textarea.js +1 -0
  168. package/src/ui/top-strip.js +47 -0
  169. package/src/ui/trust-list.js +46 -0
  170. 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;
@@ -230,6 +234,36 @@ html, body {
230
234
 
231
235
  .ui-card--flush .ui-card-body { padding: 0; }
232
236
 
237
+ /* Card variants */
238
+ .ui-card--elevated {
239
+ box-shadow: 0 4px 24px rgba(0,0,0,.22), 0 1px 4px rgba(0,0,0,.12);
240
+ border-color: transparent;
241
+ }
242
+ .ui-card--bordered {
243
+ box-shadow: none;
244
+ border-width: 2px;
245
+ }
246
+ .ui-card--flat {
247
+ box-shadow: none;
248
+ border-color: transparent;
249
+ }
250
+ .ui-card--glass {
251
+ background: rgba(255,255,255,.06);
252
+ border-color: rgba(255,255,255,.12);
253
+ box-shadow: 0 4px 24px rgba(0,0,0,.18);
254
+ backdrop-filter: blur(12px);
255
+ -webkit-backdrop-filter: blur(12px);
256
+ }
257
+ [data-theme="light"] .ui-card--glass {
258
+ background: rgba(255,255,255,.72);
259
+ border-color: rgba(0,0,0,.08);
260
+ }
261
+ .ui-card--tinted {
262
+ background: var(--ui-accent-dim, rgba(99,102,241,.1));
263
+ border-color: var(--ui-accent-dim, rgba(99,102,241,.2));
264
+ box-shadow: none;
265
+ }
266
+
233
267
  .ui-card-footer {
234
268
  padding: 1rem 1.25rem;
235
269
  border-top: 1px solid var(--ui-border);
@@ -527,6 +561,12 @@ html, body {
527
561
  .ui-stat--center { align-items: center; text-align: center; }
528
562
  .ui-stat--center .ui-stat-change { margin-inline: auto; }
529
563
 
564
+ /* Stat sizes */
565
+ .ui-stat--sm .ui-stat-value { font-size: 1.25rem; letter-spacing: -.01em; }
566
+ .ui-stat--sm .ui-stat-label { font-size: .72rem; }
567
+ .ui-stat--lg .ui-stat-value { font-size: 3.5rem; letter-spacing: -.04em; line-height: .9; }
568
+ .ui-stat--lg .ui-stat-label { font-size: .9rem; }
569
+
530
570
  /* ─── Avatar ─────────────────────────────────────────────────────────────── */
531
571
  .ui-avatar {
532
572
  display: inline-flex;
@@ -642,6 +682,24 @@ html, body {
642
682
  .ui-section--alt { background: var(--ui-surface, var(--surface, #111116)); }
643
683
  .ui-section--dark { background: var(--ui-surface-2, var(--surface-2, #18181f)); }
644
684
 
685
+ /* diagonal — tapered bottom edge; follow with a section that starts flush */
686
+ .ui-section--diagonal {
687
+ background: var(--ui-surface, var(--surface, #111116));
688
+ padding-bottom: calc(5rem + 3.5rem);
689
+ clip-path: polygon(0 0, 100% 0, 100% calc(100% - 3.5rem), 0 100%);
690
+ }
691
+ /* paper — subtle dot-grid texture on a raised surface */
692
+ .ui-section--paper {
693
+ position: relative;
694
+ background-color: var(--ui-surface, var(--surface, #111116));
695
+ background-image: radial-gradient(circle, var(--ui-border) 1px, transparent 0);
696
+ background-size: 22px 22px;
697
+ }
698
+ /* spotlight — accent glow from center top */
699
+ .ui-section--spotlight {
700
+ background: radial-gradient(ellipse 70% 60% at 50% 30%, var(--ui-accent-dim, rgba(155,141,255,.15)), var(--ui-surface, #111116) 80%);
701
+ }
702
+
645
703
  /* ─── Grid ───────────────────────────────────────────────────────────────── */
646
704
  .ui-grid { display: grid; gap: 2rem; }
647
705
 
@@ -732,6 +790,35 @@ hr.ui-divider {
732
790
  color: #ffc107;
733
791
  }
734
792
 
793
+ .ui-banner--bottom {
794
+ position: fixed;
795
+ bottom: 0;
796
+ left: 0;
797
+ right: 0;
798
+ z-index: 200;
799
+ display: flex;
800
+ align-items: center;
801
+ justify-content: center;
802
+ gap: 1rem;
803
+ }
804
+
805
+ .ui-banner-close {
806
+ position: absolute;
807
+ right: 1rem;
808
+ top: 50%;
809
+ transform: translateY(-50%);
810
+ background: none;
811
+ border: none;
812
+ cursor: pointer;
813
+ color: inherit;
814
+ font-size: 1.1rem;
815
+ line-height: 1;
816
+ padding: 0.25rem 0.5rem;
817
+ opacity: 0.7;
818
+ transition: opacity 0.15s;
819
+ }
820
+ .ui-banner-close:hover { opacity: 1; }
821
+
735
822
  /* ─── Media ──────────────────────────────────────────────────────────────── */
736
823
  .ui-media {
737
824
  display: grid;
@@ -814,7 +901,8 @@ hr.ui-divider {
814
901
  font-size: clamp(2.2rem, 6vw, 3.5rem);
815
902
  font-weight: 800;
816
903
  line-height: 1.1;
817
- letter-spacing: -.02em;
904
+ letter-spacing: var(--ui-letter-spacing-display, -.02em);
905
+ font-family: var(--ui-font-display, var(--ui-font));
818
906
  color: var(--ui-text, var(--text, #e2e2ea));
819
907
  margin: 0 0 1.25rem;
820
908
  }
@@ -834,10 +922,10 @@ hr.ui-divider {
834
922
  }
835
923
  .ui-hero--left .ui-hero-actions { justify-content: flex-start; }
836
924
 
837
- /* Split (text + image) */
925
+ /* Split (text + image, 50/50) */
838
926
  .ui-hero--split { text-align: left; }
839
927
  .ui-hero--split .ui-hero-inner {
840
- max-width: 1200px;
928
+ max-width: 1100px;
841
929
  margin: 0 auto;
842
930
  display: grid;
843
931
  grid-template-columns: 1fr 1fr;
@@ -849,6 +937,58 @@ hr.ui-divider {
849
937
  .ui-hero--split .ui-hero-media img { width: 100%; height: auto; border-radius: var(--ui-radius, 8px); display: block; }
850
938
  .ui-hero--media-left .ui-hero-media { order: -1; }
851
939
 
940
+ /* Asymmetric (text 60%, image 40%) */
941
+ .ui-hero--asymmetric { text-align: left; }
942
+ .ui-hero--asymmetric .ui-hero-inner {
943
+ max-width: 1100px;
944
+ margin: 0 auto;
945
+ display: grid;
946
+ grid-template-columns: 3fr 2fr;
947
+ gap: 3rem;
948
+ align-items: center;
949
+ }
950
+ .ui-hero--asymmetric .ui-hero-actions { justify-content: flex-start; }
951
+ .ui-hero--asymmetric .ui-hero-media { display: flex; align-items: center; justify-content: center; }
952
+ .ui-hero--asymmetric .ui-hero-media img { width: 100%; height: auto; border-radius: var(--ui-radius, 8px); display: block; }
953
+ .ui-hero--asymmetric.ui-hero--media-left .ui-hero-media { order: -1; }
954
+ .ui-hero--asymmetric.ui-hero--media-left .ui-hero-inner { grid-template-columns: 2fr 3fr; }
955
+
956
+ /* Overlap (image fills section, text overlaid) */
957
+ .ui-hero--overlap {
958
+ position: relative;
959
+ min-height: 60vh;
960
+ overflow: hidden;
961
+ padding: 0;
962
+ display: flex;
963
+ align-items: flex-end;
964
+ text-align: left;
965
+ }
966
+ .ui-hero--overlap .ui-hero-overlap-media {
967
+ position: absolute;
968
+ inset: 0;
969
+ z-index: 0;
970
+ }
971
+ .ui-hero--overlap .ui-hero-overlap-media img,
972
+ .ui-hero--overlap .ui-hero-overlap-media video {
973
+ width: 100%;
974
+ height: 100%;
975
+ object-fit: cover;
976
+ display: block;
977
+ }
978
+ .ui-hero--overlap .ui-hero-inner {
979
+ position: relative;
980
+ z-index: 1;
981
+ width: 100%;
982
+ max-width: none;
983
+ padding: 4rem 2rem;
984
+ background: linear-gradient(to top, rgba(0,0,0,.75) 0%, rgba(0,0,0,.3) 60%, transparent 100%);
985
+ }
986
+ .ui-hero--overlap .ui-hero-content { max-width: 600px; }
987
+ .ui-hero--overlap .ui-hero-eyebrow { color: rgba(255,255,255,.75); }
988
+ .ui-hero--overlap .ui-hero-title { color: #fff; text-shadow: 0 2px 8px rgba(0,0,0,.4); }
989
+ .ui-hero--overlap .ui-hero-subtitle{ color: rgba(255,255,255,.8); }
990
+ .ui-hero--overlap .ui-hero-actions { justify-content: flex-start; }
991
+
852
992
  /* ─── Testimonial ────────────────────────────────────────────────────────── */
853
993
  .ui-testimonial {
854
994
  background: var(--ui-surface, var(--surface, #111116));
@@ -900,6 +1040,8 @@ hr.ui-divider {
900
1040
 
901
1041
  /* ─── Feature ────────────────────────────────────────────────────────────── */
902
1042
  .ui-feature { display: flex; flex-direction: column; gap: .75rem; }
1043
+ .ui-feature-image { width: 100%; border-radius: var(--ui-radius-sm); overflow: hidden; }
1044
+ .ui-feature-image img { width: 100%; height: auto; display: block; }
903
1045
 
904
1046
  .ui-feature-icon {
905
1047
  display: block;
@@ -1248,6 +1390,7 @@ hr.ui-divider {
1248
1390
  position: relative;
1249
1391
  background: var(--ui-bg, var(--bg, #0d0d10));
1250
1392
  border-bottom: 1px solid var(--ui-border, var(--border, #222228));
1393
+ padding-inline: 1.5rem;
1251
1394
  }
1252
1395
 
1253
1396
  .ui-nav--sticky {
@@ -1262,7 +1405,7 @@ hr.ui-divider {
1262
1405
  .ui-nav-inner {
1263
1406
  max-width: 1100px;
1264
1407
  margin: 0 auto;
1265
- padding: .875rem 1.5rem;
1408
+ padding: .875rem 0;
1266
1409
  display: flex;
1267
1410
  align-items: center;
1268
1411
  gap: 2rem;
@@ -1281,6 +1424,7 @@ hr.ui-divider {
1281
1424
  .ui-nav-logo:hover { color: var(--ui-accent, var(--accent, #9b8dff)); }
1282
1425
 
1283
1426
  .ui-nav-links { display: flex; gap: 1.5rem; align-items: center; flex: 1; }
1427
+ .ui-nav-utility { flex: 0; margin-left: auto; }
1284
1428
 
1285
1429
  .ui-nav-link {
1286
1430
  font-size: .875rem;
@@ -1478,7 +1622,9 @@ hr.ui-divider {
1478
1622
  Prefix: u- (distinct from component classes which use ui-) */
1479
1623
 
1480
1624
  /* Screen-reader only — visually hidden but accessible to assistive tech */
1481
- .ui-sr-only {
1625
+ /* u-visually-hidden is an alias — use either */
1626
+ .ui-sr-only,
1627
+ .u-visually-hidden {
1482
1628
  position: absolute;
1483
1629
  width: 1px; height: 1px;
1484
1630
  padding: 0; margin: -1px;
@@ -1658,6 +1804,9 @@ hr.ui-divider {
1658
1804
  .u-opacity-50 { opacity: .5; }
1659
1805
  .u-opacity-75 { opacity: .75; }
1660
1806
 
1807
+ /* Numeric formatting */
1808
+ .u-tabular-nums { font-variant-numeric: tabular-nums; }
1809
+
1661
1810
  /* ─── Section header ─────────────────────────────────────────────────────────
1662
1811
  Optional heading block prepended to section() when eyebrow/title/subtitle
1663
1812
  props are supplied. Sits above the content slot. */
@@ -1678,11 +1827,13 @@ hr.ui-divider {
1678
1827
  }
1679
1828
 
1680
1829
  .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;
1830
+ font-size: 2rem;
1831
+ font-weight: 700;
1832
+ line-height: 1.15;
1833
+ letter-spacing: var(--ui-letter-spacing-display, -0.025em);
1834
+ font-family: var(--ui-font-display, var(--ui-font));
1835
+ color: var(--ui-text);
1836
+ margin: 0 0 1.25rem;
1686
1837
  }
1687
1838
 
1688
1839
  .ui-section-subtitle {
@@ -1884,6 +2035,21 @@ hr.ui-divider {
1884
2035
  border-radius: 2px;
1885
2036
  }
1886
2037
 
2038
+ /* Hover tilt — enabled by .ui-phone--tilt */
2039
+ .ui-phone--tilt {
2040
+ transform-style: preserve-3d;
2041
+ transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94), box-shadow 0.4s ease;
2042
+ will-change: transform;
2043
+ }
2044
+ .ui-phone--tilt:hover {
2045
+ transform: perspective(900px) rotateY(-8deg) rotateX(3deg);
2046
+ box-shadow:
2047
+ 0 0 0 1px rgba(0, 0, 0, 0.8),
2048
+ 0 2px 0 1px rgba(255, 255, 255, 0.07) inset,
2049
+ 8px 56px 120px rgba(0, 0, 0, 0.65),
2050
+ 4px 16px 40px rgba(0, 0, 0, 0.5);
2051
+ }
2052
+
1887
2053
  /* ─── Footer ─────────────────────────────────────────────────────────────────
1888
2054
  Site footer — logo, nav links, legal text. Stacks on mobile. */
1889
2055
  .ui-footer {
@@ -1949,6 +2115,104 @@ hr.ui-divider {
1949
2115
  .ui-footer-links { gap: 1rem; }
1950
2116
  }
1951
2117
 
2118
+ /* ─── Footer — Rich variant ─────────────────────────────────────────────── */
2119
+ .ui-footer--rich {
2120
+ padding: 0;
2121
+ border-top: none;
2122
+ }
2123
+
2124
+ .ui-footer--rich .ui-footer-top {
2125
+ display: flex;
2126
+ justify-content: space-between;
2127
+ align-items: flex-start;
2128
+ gap: 2rem;
2129
+ padding: 3rem 2rem 2rem;
2130
+ flex-wrap: wrap;
2131
+ }
2132
+
2133
+ .ui-footer--rich .ui-footer-logo {
2134
+ font-size: 1.5rem;
2135
+ }
2136
+
2137
+ .ui-footer--rich .ui-footer-subscribe {
2138
+ max-width: 480px;
2139
+ flex: 1;
2140
+ }
2141
+
2142
+ .ui-footer-columns {
2143
+ display: grid;
2144
+ grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
2145
+ gap: 2rem;
2146
+ padding: 2rem 2rem;
2147
+ border-top: 1px solid currentcolor;
2148
+ opacity-on-border: 0.15;
2149
+ }
2150
+
2151
+ .ui-footer-col-title {
2152
+ font-size: 0.6875rem;
2153
+ font-weight: 600;
2154
+ letter-spacing: 0.1em;
2155
+ text-transform: uppercase;
2156
+ margin: 0 0 1rem;
2157
+ color: var(--ui-muted, currentcolor);
2158
+ }
2159
+
2160
+ .ui-footer-col-links {
2161
+ list-style: none;
2162
+ margin: 0;
2163
+ padding: 0;
2164
+ display: flex;
2165
+ flex-direction: column;
2166
+ gap: 0.6rem;
2167
+ }
2168
+
2169
+ .ui-footer-col-links a {
2170
+ display: block;
2171
+ padding: 0.3rem 0;
2172
+ min-height: 24px;
2173
+ font-size: 0.9375rem;
2174
+ color: var(--ui-muted, inherit);
2175
+ text-decoration: none;
2176
+ transition: color 0.15s;
2177
+ }
2178
+ .ui-footer-col-links a:hover { color: var(--ui-text, currentcolor); }
2179
+
2180
+ .ui-footer-wordmark {
2181
+ padding: 1rem 1.5rem 0;
2182
+ overflow: hidden;
2183
+ }
2184
+ .ui-footer-wordmark::after {
2185
+ content: attr(data-wordmark);
2186
+ display: block;
2187
+ font-size: clamp(4rem, 16vw, 14rem);
2188
+ font-weight: 700;
2189
+ letter-spacing: -0.03em;
2190
+ line-height: 0.9;
2191
+ word-break: break-all;
2192
+ }
2193
+
2194
+ .ui-footer-legal-row {
2195
+ display: flex;
2196
+ gap: 1.5rem;
2197
+ align-items: center;
2198
+ flex-wrap: wrap;
2199
+ padding: 1.5rem 2rem;
2200
+ font-size: 0.8125rem;
2201
+ color: var(--ui-muted, currentcolor);
2202
+ border-top: 1px solid var(--ui-border, currentcolor);
2203
+ }
2204
+
2205
+ .ui-footer-legal-row a {
2206
+ color: inherit;
2207
+ text-decoration: none;
2208
+ }
2209
+ .ui-footer-legal-row a:hover { text-decoration: underline; }
2210
+
2211
+ @media (max-width: 640px) {
2212
+ .ui-footer--rich .ui-footer-top { flex-direction: column; }
2213
+ .ui-footer-columns { grid-template-columns: repeat(2, 1fr); }
2214
+ }
2215
+
1952
2216
  /* ─── Icon wrapper ───────────────────────────────────────────────────────────
1953
2217
  Wraps any icon in a tinted background shape.
1954
2218
  bg: 'circle' → border-radius: 50% | bg: 'square' → var(--ui-radius) */
@@ -2805,6 +3069,30 @@ fieldset[disabled] .ui-rating-stars .ui-rating-star:hover ~ .ui-rating-star { co
2805
3069
  font-style: normal;
2806
3070
  }
2807
3071
 
3072
+ /* Editorial variant — centred large serif, no left border */
3073
+ .ui-pullquote--editorial {
3074
+ border-left: none;
3075
+ padding: 4rem 1.5rem;
3076
+ text-align: center;
3077
+ }
3078
+ .ui-pullquote--editorial .ui-pullquote-text {
3079
+ font-size: clamp(1.5rem, 3.5vw, 2.25rem);
3080
+ line-height: 1.4;
3081
+ font-style: italic;
3082
+ font-weight: 400;
3083
+ max-width: 640px;
3084
+ margin-left: auto;
3085
+ margin-right: auto;
3086
+ }
3087
+ .ui-pullquote--editorial.ui-pullquote--lg .ui-pullquote-text {
3088
+ font-size: clamp(1.75rem, 4vw, 3rem);
3089
+ }
3090
+ .ui-pullquote--editorial .ui-pullquote-cite {
3091
+ text-align: center;
3092
+ margin-top: 1.25rem;
3093
+ font-size: 0.9375rem;
3094
+ }
3095
+
2808
3096
  /* ─── List ───────────────────────────────────────────────────────────────────
2809
3097
  Styled list component. Use instead of raw <ul>/<ol>. */
2810
3098
  .ui-list {
@@ -2887,7 +3175,7 @@ fieldset[disabled] .ui-rating-stars .ui-rating-star:hover ~ .ui-rating-star { co
2887
3175
 
2888
3176
  .ui-prose code {
2889
3177
  background: var(--ui-surface-2);
2890
- color: var(--ui-accent);
3178
+ color: var(--ui-code-color);
2891
3179
  padding: .1em .35em;
2892
3180
  border-radius: var(--ui-radius-sm);
2893
3181
  font-size: .875em;
@@ -2974,24 +3262,29 @@ fieldset[disabled] .ui-rating-stars .ui-rating-star:hover ~ .ui-rating-star { co
2974
3262
  All --ui-* tokens are overridden; component markup stays identical. */
2975
3263
  [data-theme="light"],
2976
3264
  .ui-theme-light {
2977
- --ui-bg: #ffffff;
2978
- --ui-surface: #f8f8fc;
2979
- --ui-surface-2: #f0f0f8;
2980
- --ui-border: #d8d8e8;
2981
- --ui-text: #111118;
2982
- --ui-muted: #60607a;
2983
- --ui-accent: #6b5ce7;
2984
- --ui-accent-hover: #5847d6;
2985
- --ui-accent-dim: rgba(107, 92, 231, .12);
2986
- --ui-green: #166534;
3265
+ --ui-bg: var(--bg, #ffffff);
3266
+ --ui-surface: var(--surface, #f8f8fc);
3267
+ --ui-surface-2: var(--surface-2, #f0f0f8);
3268
+ --ui-border: var(--border, #d8d8e8);
3269
+ --ui-text: var(--text, #111118);
3270
+ --ui-muted: var(--muted, #60607a);
3271
+ --ui-accent: var(--accent, #6b5ce7);
3272
+ --ui-accent-hover: var(--accent-hover, #5847d6);
3273
+ --ui-accent-dim: var(--accent-dim, rgba(107, 92, 231, .12));
3274
+ --ui-accent-text: var(--accent-text, #ffffff);
3275
+ /* Inline code uses a slightly darker accent so it passes WCAG AA on --ui-surface-2 */
3276
+ --ui-code-color: var(--code-color, #4a3db5);
3277
+ --ui-green: var(--green, #166534);
2987
3278
  --ui-green-dim: rgba(22, 163, 74, .12);
2988
- --ui-red: #991b1b;
3279
+ --ui-red: var(--red, #991b1b);
2989
3280
  --ui-red-dim: rgba(220, 38, 38, .12);
2990
3281
  --ui-yellow: #92400e;
2991
3282
  --ui-yellow-dim: rgba(217, 119, 6, .12);
2992
3283
  --ui-blue: #1e40af;
2993
3284
  --ui-blue-dim: rgba( 30, 64, 175, .12);
2994
3285
  --ui-nav-sticky-bg: rgba(255, 255, 255, .85);
3286
+ --ui-muted-bg: var(--muted-bg, rgba(0, 0, 0, .04));
3287
+ --ui-shadow: var(--shadow, 0 1px 3px rgba(0,0,0,.10), 0 1px 2px rgba(0,0,0,.06));
2995
3288
  --tok-kw: #7c3aed;
2996
3289
  --tok-str: #15803d;
2997
3290
  --tok-cmt: #6b7280;
@@ -3000,3 +3293,739 @@ fieldset[disabled] .ui-rating-stars .ui-rating-star:hover ~ .ui-rating-star { co
3000
3293
  --tok-op: #0891b2;
3001
3294
  --tok-punct: #6b7280;
3002
3295
  }
3296
+
3297
+ /* ─── Vibe presets ────────────────────────────────────────────────────────────
3298
+ meta.vibe sets data-vibe on <body>. Each vibe adjusts geometry and type
3299
+ tokens without touching colour — pair with a custom theme.css for full
3300
+ personality. All agents: set meta.vibe to get instant personality.
3301
+
3302
+ warm — rounded, friendly, inviting (local businesses, wellness)
3303
+ editorial — sharp, serif display, tight tracking (blogs, publications)
3304
+ playful — very rounded, loose, energetic (kids, lifestyle, food)
3305
+ minimal — razor-sharp, no shadows, refined (portfolios, luxury)
3306
+ bold — impact headings, strong presence (agencies, startups)
3307
+ brutalist — zero radius, oversized type, raw (art, fashion, counter-culture)
3308
+ retro — slab serif, medium radius, warm (vintage, craft, nostalgia)
3309
+ corporate — conservative geometry, tight tracking (B2B, finance, legal)
3310
+ neon — futuristic mono headline, glow-ready (tech, gaming, crypto)
3311
+ paper — organic radius, soft shadows, tactile (journals, bookshops) */
3312
+
3313
+ [data-vibe="warm"] {
3314
+ --ui-radius: 14px;
3315
+ --ui-radius-sm: 8px;
3316
+ --ui-letter-spacing-display: -0.01em;
3317
+ }
3318
+
3319
+ [data-vibe="editorial"] {
3320
+ --ui-radius: 0px;
3321
+ --ui-radius-sm: 0px;
3322
+ --ui-font-display: Georgia, 'Times New Roman', serif;
3323
+ --ui-letter-spacing-display: -0.04em;
3324
+ }
3325
+
3326
+ [data-vibe="playful"] {
3327
+ --ui-radius: 22px;
3328
+ --ui-radius-sm: 14px;
3329
+ --ui-letter-spacing-display: 0em;
3330
+ }
3331
+
3332
+ [data-vibe="minimal"] {
3333
+ --ui-radius: 2px;
3334
+ --ui-radius-sm: 1px;
3335
+ --ui-letter-spacing-display: 0.02em;
3336
+ }
3337
+
3338
+ [data-vibe="bold"] {
3339
+ --ui-radius: 6px;
3340
+ --ui-letter-spacing-display: -0.05em;
3341
+ }
3342
+
3343
+ [data-vibe="brutalist"] {
3344
+ --ui-radius: 0px;
3345
+ --ui-radius-sm: 0px;
3346
+ --ui-font-display: 'Arial Black', 'Impact', sans-serif;
3347
+ --ui-letter-spacing-display: -0.02em;
3348
+ }
3349
+
3350
+ [data-vibe="retro"] {
3351
+ --ui-radius: 8px;
3352
+ --ui-radius-sm: 4px;
3353
+ --ui-font-display: 'Rockwell', 'Courier New', Georgia, serif;
3354
+ --ui-letter-spacing-display: 0.01em;
3355
+ }
3356
+
3357
+ [data-vibe="corporate"] {
3358
+ --ui-radius: 4px;
3359
+ --ui-radius-sm: 2px;
3360
+ --ui-letter-spacing-display: -0.01em;
3361
+ }
3362
+
3363
+ [data-vibe="neon"] {
3364
+ --ui-radius: 3px;
3365
+ --ui-radius-sm: 2px;
3366
+ --ui-font-display: 'Courier New', 'Lucida Console', monospace;
3367
+ --ui-letter-spacing-display: -0.03em;
3368
+ }
3369
+
3370
+ [data-vibe="paper"] {
3371
+ --ui-radius: 10px;
3372
+ --ui-radius-sm: 6px;
3373
+ --ui-font-display: Georgia, 'Palatino Linotype', serif;
3374
+ --ui-letter-spacing-display: -0.02em;
3375
+ }
3376
+
3377
+ /* ─── Gallery ────────────────────────────────────────────────────────────── */
3378
+ .ui-gallery { display: grid; gap: 1rem; }
3379
+ .ui-gallery--cols-1 { grid-template-columns: 1fr; }
3380
+ .ui-gallery--cols-2 { grid-template-columns: repeat(2, 1fr); }
3381
+ .ui-gallery--cols-3 { grid-template-columns: repeat(3, 1fr); }
3382
+ .ui-gallery--cols-4 { grid-template-columns: repeat(4, 1fr); }
3383
+ .ui-gallery--gap-sm { gap: .5rem; }
3384
+ .ui-gallery--gap-lg { gap: 1.5rem; }
3385
+ .ui-gallery-item { margin: 0; }
3386
+ .ui-gallery-crop { overflow: hidden; }
3387
+ .ui-gallery-img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .3s ease; }
3388
+ .ui-gallery--rounded .ui-gallery-crop { border-radius: var(--ui-radius); }
3389
+ .ui-gallery-item:hover .ui-gallery-img { transform: scale(1.03); }
3390
+ .ui-gallery-link { display: block; }
3391
+ .ui-gallery-caption { font-size: .8125rem; color: var(--ui-muted); padding: .5rem 0 0; }
3392
+
3393
+ /* strip: horizontal scroll row */
3394
+ .ui-gallery--strip { grid-template-columns: none; display: flex; overflow-x: auto; gap: 1rem; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; }
3395
+ .ui-gallery--strip .ui-gallery-item { flex: 0 0 auto; width: 280px; scroll-snap-align: start; }
3396
+ .ui-gallery--strip .ui-gallery-crop { aspect-ratio: 3/4; }
3397
+
3398
+ /* masonry: CSS column layout */
3399
+ .ui-gallery--masonry { display: block; columns: 3; column-gap: 1rem; }
3400
+ .ui-gallery--masonry.ui-gallery--cols-2 { columns: 2; }
3401
+ .ui-gallery--masonry.ui-gallery--cols-4 { columns: 4; }
3402
+ .ui-gallery--masonry .ui-gallery-item { break-inside: avoid; margin-bottom: 1rem; }
3403
+ .ui-gallery--masonry .ui-gallery-crop { aspect-ratio: unset; }
3404
+ .ui-gallery--masonry .ui-gallery-img { height: auto; }
3405
+
3406
+ @media (max-width: 640px) {
3407
+ .ui-gallery--cols-3,
3408
+ .ui-gallery--cols-4 { grid-template-columns: repeat(2, 1fr); }
3409
+ .ui-gallery--masonry.ui-gallery--cols-3,
3410
+ .ui-gallery--masonry.ui-gallery--cols-4 { columns: 2; }
3411
+ }
3412
+
3413
+ /* ─── PhotoCard ──────────────────────────────────────────────────────────── */
3414
+ .ui-photo-card {
3415
+ margin: 0;
3416
+ display: inline-flex;
3417
+ flex-direction: column;
3418
+ background: var(--ui-surface);
3419
+ padding: .75rem .75rem .5rem;
3420
+ border: 1px solid var(--ui-border);
3421
+ box-shadow: 0 4px 12px rgba(0,0,0,.25);
3422
+ rotate: var(--photo-tilt, 0deg);
3423
+ transition: rotate .2s ease, box-shadow .2s ease;
3424
+ }
3425
+ .ui-photo-card:hover { box-shadow: 0 8px 24px rgba(0,0,0,.35); }
3426
+ .ui-photo-card--rounded .ui-photo-card-crop { border-radius: var(--ui-radius); overflow: hidden; }
3427
+ .ui-photo-card-crop { overflow: hidden; }
3428
+ .ui-photo-card-img { width: 100%; height: 100%; object-fit: cover; display: block; }
3429
+ .ui-photo-card-caption {
3430
+ font-size: .8125rem;
3431
+ color: var(--ui-muted);
3432
+ padding: .5rem .25rem .25rem;
3433
+ text-align: center;
3434
+ font-family: var(--ui-mono);
3435
+ }
3436
+
3437
+ /* ─── Marquee ────────────────────────────────────────────────────────────── */
3438
+ @keyframes ui-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
3439
+
3440
+ .ui-marquee { overflow: hidden; }
3441
+ .ui-marquee--fade {
3442
+ mask-image: linear-gradient(to right, transparent 0%, #000 10%, #000 90%, transparent 100%);
3443
+ -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 10%, #000 90%, transparent 100%);
3444
+ }
3445
+ .ui-marquee-track {
3446
+ display: flex;
3447
+ width: max-content;
3448
+ animation: ui-marquee var(--marquee-speed, 30s) linear infinite;
3449
+ animation-direction: var(--marquee-dir, normal);
3450
+ list-style: none;
3451
+ margin: 0;
3452
+ padding: 0;
3453
+ gap: 3rem;
3454
+ }
3455
+ .ui-marquee--gap-sm .ui-marquee-track { gap: 1rem; }
3456
+ .ui-marquee--gap-md .ui-marquee-track { gap: 2rem; }
3457
+ .ui-marquee--pause:hover .ui-marquee-track { animation-play-state: paused; }
3458
+ .ui-marquee-item { flex: 0 0 auto; display: flex; align-items: center; }
3459
+
3460
+ @media (prefers-reduced-motion: reduce) {
3461
+ .ui-marquee-track { animation: none; }
3462
+ .ui-marquee { overflow-x: auto; }
3463
+ }
3464
+
3465
+ /* ─── Decorate (SVG pattern overlay) ────────────────────────────────────── */
3466
+ .ui-decorate { pointer-events: none; }
3467
+
3468
+ /* ─── Product Card ───────────────────────────────────────────────────────── */
3469
+ .ui-product-card {
3470
+ position: relative;
3471
+ display: block;
3472
+ overflow: hidden;
3473
+ text-decoration: none;
3474
+ color: inherit;
3475
+ }
3476
+ .ui-product-card-crop {
3477
+ overflow: hidden;
3478
+ }
3479
+ .ui-product-card-img {
3480
+ width: 100%;
3481
+ height: 100%;
3482
+ object-fit: cover;
3483
+ display: block;
3484
+ transition: transform 0.45s cubic-bezier(0.25, 0.46, 0.45, 0.94);
3485
+ }
3486
+ .ui-product-card:hover .ui-product-card-img { transform: scale(1.04); }
3487
+ .ui-product-card:focus-visible { outline: 2px solid var(--ui-accent); outline-offset: 2px; }
3488
+
3489
+ .ui-product-card-overlay {
3490
+ position: absolute;
3491
+ inset: 0;
3492
+ display: flex;
3493
+ flex-direction: column;
3494
+ justify-content: flex-end;
3495
+ padding: 1.25rem;
3496
+ background: linear-gradient(to top, rgba(0,0,0,0.55) 0%, transparent 55%);
3497
+ opacity: 0;
3498
+ transition: opacity 0.3s ease;
3499
+ }
3500
+ .ui-product-card:hover .ui-product-card-overlay { opacity: 1; }
3501
+
3502
+ /* Always-visible overlay for reduced-motion users */
3503
+ @media (prefers-reduced-motion: reduce) {
3504
+ .ui-product-card-img { transition: none; }
3505
+ .ui-product-card-overlay { opacity: 1; }
3506
+ }
3507
+
3508
+ .ui-product-card-name {
3509
+ font-size: 1rem;
3510
+ font-weight: 500;
3511
+ color: #fff;
3512
+ margin: 0;
3513
+ letter-spacing: 0.02em;
3514
+ }
3515
+ .ui-product-card-price {
3516
+ font-size: 0.875rem;
3517
+ color: rgba(255,255,255,0.75);
3518
+ margin: 0.3rem 0 0;
3519
+ }
3520
+
3521
+ .ui-product-card-badge {
3522
+ position: absolute;
3523
+ top: 1rem;
3524
+ left: 1rem;
3525
+ background: var(--ui-text);
3526
+ color: var(--ui-bg);
3527
+ font-size: 0.6875rem;
3528
+ font-weight: 600;
3529
+ letter-spacing: 0.08em;
3530
+ text-transform: uppercase;
3531
+ padding: 0.2em 0.6em;
3532
+ z-index: 1;
3533
+ }
3534
+ .ui-product-card--badge-right .ui-product-card-badge { left: auto; right: 1rem; }
3535
+
3536
+ /* Below layout — caption sits under the image, always visible */
3537
+ .ui-product-card--below {
3538
+ overflow: visible;
3539
+ }
3540
+ .ui-product-card-caption {
3541
+ text-align: center;
3542
+ padding: 0.75rem 0.5rem 0.25rem;
3543
+ background: var(--ui-surface);
3544
+ }
3545
+ .ui-product-card--below .ui-product-card-name {
3546
+ font-size: 1rem;
3547
+ font-weight: 400;
3548
+ color: var(--ui-text);
3549
+ margin: 0 0 0.25rem;
3550
+ font-style: normal;
3551
+ }
3552
+ .ui-product-card--below .ui-product-card-price {
3553
+ font-size: 0.875rem;
3554
+ color: var(--ui-muted);
3555
+ margin: 0;
3556
+ }
3557
+
3558
+ /* ─── Editorial Hero ─────────────────────────────────────────────────────── */
3559
+ .ui-editorial-hero {
3560
+ position: relative;
3561
+ overflow: hidden;
3562
+ }
3563
+
3564
+ .ui-editorial-hero-heading {
3565
+ position: absolute;
3566
+ width: 1px;
3567
+ height: 1px;
3568
+ overflow: hidden;
3569
+ clip: rect(0,0,0,0);
3570
+ margin: -1px;
3571
+ padding: 0;
3572
+ border: 0;
3573
+ }
3574
+
3575
+ .ui-editorial-hero-images {
3576
+ display: grid;
3577
+ grid-template-columns: 1fr 1fr;
3578
+ width: 100%;
3579
+ }
3580
+ .ui-editorial-hero-img {
3581
+ overflow: hidden;
3582
+ }
3583
+ .ui-editorial-hero-img img {
3584
+ width: 100%;
3585
+ height: 100%;
3586
+ object-fit: cover;
3587
+ display: block;
3588
+ }
3589
+
3590
+ .ui-editorial-hero-overlay {
3591
+ position: absolute;
3592
+ inset: 0;
3593
+ display: flex;
3594
+ flex-direction: column;
3595
+ justify-content: center;
3596
+ align-items: center;
3597
+ pointer-events: none;
3598
+ color: var(--ui-accent);
3599
+ mix-blend-mode: multiply;
3600
+ }
3601
+ .ui-editorial-hero-overlay span {
3602
+ display: block;
3603
+ font-size: clamp(3rem, 12vw, 11rem);
3604
+ font-weight: 700;
3605
+ letter-spacing: -0.04em;
3606
+ line-height: 0.9;
3607
+ text-transform: uppercase;
3608
+ white-space: nowrap;
3609
+ }
3610
+
3611
+ .ui-editorial-hero-cta {
3612
+ position: absolute;
3613
+ bottom: 1.5rem;
3614
+ left: 50%;
3615
+ transform: translateX(-50%);
3616
+ font-size: 0.75rem;
3617
+ letter-spacing: 0.1em;
3618
+ text-transform: uppercase;
3619
+ text-decoration: none;
3620
+ color: var(--ui-text);
3621
+ white-space: nowrap;
3622
+ }
3623
+
3624
+ @media (max-width: 640px) {
3625
+ .ui-editorial-hero-overlay span { font-size: clamp(2rem, 10vw, 4rem); }
3626
+ }
3627
+
3628
+ /* ─── Image Pair ─────────────────────────────────────────────────────────── */
3629
+ .ui-image-pair {
3630
+ display: grid;
3631
+ grid-template-columns: 1fr 1fr;
3632
+ width: 100%;
3633
+ }
3634
+ .ui-image-pair--gap-sm { gap: 0.5rem; }
3635
+ .ui-image-pair--gap-md { gap: 1.5rem; }
3636
+
3637
+ .ui-image-pair-col { overflow: hidden; }
3638
+ .ui-image-pair-img { overflow: hidden; }
3639
+ .ui-image-pair-crop { overflow: hidden; }
3640
+ .ui-image-pair-crop img {
3641
+ width: 100%;
3642
+ height: 100%;
3643
+ object-fit: cover;
3644
+ display: block;
3645
+ }
3646
+ .ui-image-pair--captioned .ui-image-pair-col { overflow: visible; }
3647
+
3648
+ .ui-image-pair-link {
3649
+ display: inline-block;
3650
+ margin-top: 0.75rem;
3651
+ font-size: 0.9375rem;
3652
+ text-decoration: none;
3653
+ color: var(--ui-text);
3654
+ }
3655
+ .ui-image-pair-link:hover { opacity: 0.6; }
3656
+
3657
+ @media (max-width: 480px) {
3658
+ .ui-image-pair { grid-template-columns: 1fr; }
3659
+ }
3660
+
3661
+ /* ─── Brand Statement ────────────────────────────────────────────────────── */
3662
+ .ui-brand-statement {
3663
+ padding: 5rem 1.5rem;
3664
+ text-align: center;
3665
+ }
3666
+ .ui-brand-statement--left { text-align: left; }
3667
+ .ui-brand-statement--right { text-align: right; }
3668
+
3669
+ .ui-brand-statement-inner {
3670
+ max-width: 860px;
3671
+ margin: 0 auto;
3672
+ }
3673
+
3674
+ .ui-brand-statement-text {
3675
+ font-size: clamp(1.5rem, 4vw, 2.5rem);
3676
+ line-height: 1.25;
3677
+ font-weight: 400;
3678
+ font-style: italic;
3679
+ margin: 0;
3680
+ color: var(--ui-text);
3681
+ }
3682
+
3683
+ .ui-brand-statement--display .ui-brand-statement-text {
3684
+ font-style: normal;
3685
+ font-weight: 700;
3686
+ }
3687
+
3688
+ .ui-brand-statement--sans .ui-brand-statement-text {
3689
+ font-style: normal;
3690
+ }
3691
+
3692
+ .ui-brand-statement--sm .ui-brand-statement-text { font-size: clamp(1.1rem, 2.5vw, 1.5rem); }
3693
+ .ui-brand-statement--lg .ui-brand-statement-text { font-size: clamp(2rem, 5vw, 3.5rem); }
3694
+
3695
+ .ui-brand-statement-attr {
3696
+ margin-top: 1.5rem;
3697
+ font-size: 0.9375rem;
3698
+ color: var(--ui-muted);
3699
+ font-style: italic;
3700
+ }
3701
+
3702
+ /* ------------------------------------------------------------------ */
3703
+ /* ServiceCard */
3704
+ /* ------------------------------------------------------------------ */
3705
+
3706
+ .ui-service-card {
3707
+ display: block;
3708
+ padding: var(--ui-space-5, 1.25rem);
3709
+ border: 1px solid var(--ui-border);
3710
+ border-radius: var(--ui-radius-md);
3711
+ background: var(--ui-surface);
3712
+ text-decoration: none;
3713
+ color: inherit;
3714
+ }
3715
+
3716
+ .ui-service-card--linked {
3717
+ transition: border-color 0.15s, box-shadow 0.15s;
3718
+ }
3719
+
3720
+ .ui-service-card--linked:hover {
3721
+ border-color: var(--ui-accent);
3722
+ box-shadow: 0 2px 12px var(--ui-shadow, rgba(0,0,0,.08));
3723
+ }
3724
+
3725
+ .ui-service-card-icon {
3726
+ display: inline-flex;
3727
+ align-items: center;
3728
+ justify-content: center;
3729
+ width: 2.5rem;
3730
+ height: 2.5rem;
3731
+ border-radius: var(--ui-radius);
3732
+ background: var(--ui-accent-tint);
3733
+ color: var(--ui-accent);
3734
+ margin-block-end: var(--ui-space-4, 1rem);
3735
+ }
3736
+
3737
+ .ui-service-card-title {
3738
+ font-size: var(--ui-text-md, 1rem);
3739
+ font-weight: 600;
3740
+ margin: 0 0 var(--ui-space-2, 0.5rem);
3741
+ color: var(--ui-text);
3742
+ }
3743
+
3744
+ .ui-service-card-desc {
3745
+ margin: 0;
3746
+ font-size: var(--ui-text-sm, 0.875rem);
3747
+ color: var(--ui-muted);
3748
+ line-height: 1.6;
3749
+ }
3750
+
3751
+ /* ------------------------------------------------------------------ */
3752
+ /* ContactRow */
3753
+ /* ------------------------------------------------------------------ */
3754
+
3755
+ .ui-contact-row {
3756
+ display: flex;
3757
+ flex-wrap: wrap;
3758
+ gap: var(--ui-space-3, 0.75rem) var(--ui-space-6, 1.5rem);
3759
+ list-style: none;
3760
+ margin: 0;
3761
+ padding: 0;
3762
+ }
3763
+
3764
+ .ui-contact-row--sm { font-size: var(--ui-text-sm, 0.875rem); }
3765
+
3766
+ .ui-contact-row-item {
3767
+ display: flex;
3768
+ align-items: center;
3769
+ gap: var(--ui-space-2, 0.5rem);
3770
+ color: var(--ui-muted);
3771
+ }
3772
+
3773
+ .ui-contact-row-item a {
3774
+ color: inherit;
3775
+ text-decoration: none;
3776
+ }
3777
+
3778
+ .ui-contact-row-item a:hover { color: var(--ui-accent); }
3779
+
3780
+ .ui-contact-row-icon {
3781
+ flex-shrink: 0;
3782
+ color: var(--ui-accent);
3783
+ line-height: 0;
3784
+ }
3785
+
3786
+ /* ------------------------------------------------------------------ */
3787
+ /* TrustList */
3788
+ /* ------------------------------------------------------------------ */
3789
+
3790
+ .ui-trust-list {
3791
+ display: flex;
3792
+ flex-wrap: wrap;
3793
+ gap: var(--ui-space-2, 0.5rem) var(--ui-space-5, 1.25rem);
3794
+ list-style: none;
3795
+ margin: 0;
3796
+ padding: 0;
3797
+ }
3798
+
3799
+ .ui-trust-list--md { font-size: var(--ui-text-md, 1rem); }
3800
+
3801
+ .ui-trust-list-item {
3802
+ display: flex;
3803
+ align-items: center;
3804
+ gap: var(--ui-space-2, 0.5rem);
3805
+ font-size: var(--ui-text-sm, 0.875rem);
3806
+ color: var(--ui-muted);
3807
+ }
3808
+
3809
+ .ui-trust-list-icon {
3810
+ flex-shrink: 0;
3811
+ color: var(--ui-accent);
3812
+ line-height: 0;
3813
+ }
3814
+
3815
+ /* ------------------------------------------------------------------ */
3816
+ /* TopStrip */
3817
+ /* ------------------------------------------------------------------ */
3818
+
3819
+ .ui-top-strip {
3820
+ display: flex;
3821
+ align-items: center;
3822
+ justify-content: space-between;
3823
+ padding: var(--ui-space-2, 0.5rem) var(--ui-space-6, 1.5rem);
3824
+ background: var(--ui-surface-raised, var(--ui-surface));
3825
+ font-size: var(--ui-text-xs, 0.75rem);
3826
+ color: var(--ui-muted);
3827
+ gap: var(--ui-space-4, 1rem);
3828
+ }
3829
+
3830
+ .ui-top-strip-content,
3831
+ .ui-top-strip-left,
3832
+ .ui-top-strip-right {
3833
+ display: flex;
3834
+ align-items: center;
3835
+ gap: var(--ui-space-4, 1rem);
3836
+ flex-wrap: wrap;
3837
+ }
3838
+
3839
+ @media (max-width: 640px) {
3840
+ .ui-top-strip {
3841
+ flex-direction: column;
3842
+ align-items: flex-start;
3843
+ gap: var(--ui-space-1, 0.25rem);
3844
+ }
3845
+ }
3846
+
3847
+ /* ------------------------------------------------------------------ */
3848
+ /* StatGrid */
3849
+ /* ------------------------------------------------------------------ */
3850
+
3851
+ .ui-stat-grid {
3852
+ display: grid;
3853
+ gap: var(--ui-space-4, 1rem);
3854
+ grid-template-columns: repeat(var(--stat-cols, 2), 1fr);
3855
+ padding: var(--ui-space-6, 1.5rem);
3856
+ border-radius: var(--ui-radius-md);
3857
+ }
3858
+
3859
+ .ui-stat-grid .ui-stat-value {
3860
+ font-size: clamp(1.5rem, 3vw, 2.25rem);
3861
+ line-height: 1.1;
3862
+ }
3863
+
3864
+ /* Stat suffix — icon/rating appended to the value */
3865
+ .ui-stat-suffix {
3866
+ display: inline-flex;
3867
+ align-items: center;
3868
+ margin-left: 0.2em;
3869
+ font-size: 0.6em;
3870
+ vertical-align: middle;
3871
+ line-height: 0;
3872
+ }
3873
+
3874
+ /* ─── Editorial Components ───────────────────────────────────────────────────
3875
+ Components for editorial/typographic layouts: entryList, displayHeading,
3876
+ sectionLabel, dropCap, colophon. */
3877
+
3878
+ /* Entry List — tabular list for backlists, release notes, changelogs */
3879
+ .ui-entry-list {
3880
+ display: flex;
3881
+ flex-direction: column;
3882
+ gap: 1.5rem;
3883
+ }
3884
+
3885
+ .ui-entry-list-item {
3886
+ display: grid;
3887
+ grid-template-columns: minmax(4rem, max-content) 1fr;
3888
+ gap: 2rem;
3889
+ align-items: baseline;
3890
+ }
3891
+
3892
+ @media (max-width: 640px) {
3893
+ .ui-entry-list-item {
3894
+ grid-template-columns: 1fr;
3895
+ gap: 0.5rem;
3896
+ }
3897
+ }
3898
+
3899
+ .ui-entry-list-meta {
3900
+ font-size: 0.9rem;
3901
+ color: var(--ui-muted);
3902
+ font-weight: 600;
3903
+ text-transform: uppercase;
3904
+ letter-spacing: 0.05em;
3905
+ font-variant-numeric: tabular-nums;
3906
+ }
3907
+
3908
+ .ui-entry-list-content {
3909
+ display: flex;
3910
+ flex-direction: column;
3911
+ gap: 0.5rem;
3912
+ }
3913
+
3914
+ .ui-entry-list-title {
3915
+ font-size: 1.1rem;
3916
+ font-weight: 600;
3917
+ color: var(--ui-text);
3918
+ line-height: 1.4;
3919
+ margin: 0;
3920
+ }
3921
+
3922
+ .ui-entry-list-description {
3923
+ font-size: 0.9rem;
3924
+ color: var(--ui-muted);
3925
+ line-height: 1.6;
3926
+ margin: 0;
3927
+ }
3928
+
3929
+ /* Display Heading — large typographic moments */
3930
+ .ui-display-heading {
3931
+ font-size: clamp(2.5rem, 6vw, 5rem);
3932
+ font-weight: 800;
3933
+ color: var(--ui-text);
3934
+ line-height: 1.1;
3935
+ margin: 0;
3936
+ }
3937
+
3938
+ /* Section Label — eyebrow + h2 + rule */
3939
+ .ui-section-label {
3940
+ display: flex;
3941
+ flex-direction: column;
3942
+ gap: 0.75rem;
3943
+ margin-bottom: 2rem;
3944
+ }
3945
+
3946
+ .ui-section-label--center {
3947
+ align-items: center;
3948
+ text-align: center;
3949
+ }
3950
+
3951
+ .ui-section-label-eyebrow {
3952
+ font-size: 0.85rem;
3953
+ color: var(--ui-muted);
3954
+ font-weight: 600;
3955
+ text-transform: uppercase;
3956
+ letter-spacing: 0.08em;
3957
+ margin: 0;
3958
+ }
3959
+
3960
+ .ui-section-label-heading {
3961
+ font-size: 1.75rem;
3962
+ font-weight: 700;
3963
+ color: var(--ui-text);
3964
+ line-height: 1.2;
3965
+ margin: 0;
3966
+ }
3967
+
3968
+ .ui-section-label-rule {
3969
+ width: 100%;
3970
+ max-width: 8rem;
3971
+ height: 2px;
3972
+ background: var(--ui-border);
3973
+ border: none;
3974
+ margin: 0;
3975
+ }
3976
+
3977
+ .ui-section-label--center .ui-section-label-rule {
3978
+ margin-left: auto;
3979
+ margin-right: auto;
3980
+ }
3981
+
3982
+ /* Drop Cap — first-letter treatment */
3983
+ .ui-drop-cap {
3984
+ margin: 0;
3985
+ line-height: 1.75;
3986
+ color: var(--ui-text);
3987
+ }
3988
+
3989
+ .ui-drop-cap-letter {
3990
+ font-weight: 700;
3991
+ color: var(--ui-text);
3992
+ }
3993
+
3994
+ /* Colophon — minimal footer for editorial pages */
3995
+ .ui-colophon {
3996
+ padding: 3rem 1.5rem 2rem;
3997
+ border-top: 1px solid var(--ui-border);
3998
+ margin-top: 4rem;
3999
+ text-align: center;
4000
+ }
4001
+
4002
+ .ui-colophon--left {
4003
+ text-align: left;
4004
+ }
4005
+
4006
+ .ui-colophon-inner {
4007
+ max-width: 640px;
4008
+ margin: 0 auto;
4009
+ font-size: 0.85rem;
4010
+ color: var(--ui-muted);
4011
+ line-height: 1.6;
4012
+ }
4013
+
4014
+ .ui-colophon-inner p {
4015
+ margin: 0.5em 0;
4016
+ }
4017
+
4018
+ .ui-colophon-inner p:last-child {
4019
+ margin-bottom: 0;
4020
+ }
4021
+
4022
+ .ui-colophon-inner a {
4023
+ color: var(--ui-muted);
4024
+ text-decoration: underline;
4025
+ text-underline-offset: 2px;
4026
+ transition: color 0.15s;
4027
+ }
4028
+
4029
+ .ui-colophon-inner a:hover {
4030
+ color: var(--ui-text);
4031
+ }