@atelic-action/ui 0.17.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/README.md +428 -44
  2. package/bin/atelic-check-base-path.ts +5 -0
  3. package/bin/atelic-clean-sitemap.ts +5 -0
  4. package/bin/atelic-contact-canary.ts +5 -0
  5. package/bin/atelic-print-sheets.sh +1 -1
  6. package/bin/atelic-seal-document.mjs +1 -1
  7. package/bin/atelic-serve-static.ts +1 -1
  8. package/dist/gate/index.d.ts +1 -1
  9. package/dist/gate/index.js +1 -1
  10. package/dist/gate/middleware.d.ts +4 -1
  11. package/dist/gate/middleware.js +14 -5
  12. package/dist/gate/wall.d.ts +25 -0
  13. package/dist/gate/wall.js +41 -2
  14. package/dist/routing/basePath.d.ts +24 -0
  15. package/dist/routing/basePath.js +54 -0
  16. package/dist/vite/index.d.ts +3 -2
  17. package/dist/vite/index.js +3 -2
  18. package/dist/vite/marketing-config.d.ts +50 -0
  19. package/dist/vite/marketing-config.js +83 -0
  20. package/package.json +164 -5
  21. package/src/app/AppProvider.tsx +41 -0
  22. package/src/app/AuthGuard.tsx +27 -0
  23. package/src/app/CardErrorBoundary.tsx +39 -0
  24. package/src/app/ErrorFallback.tsx +39 -0
  25. package/src/app/GoogleGlyph.tsx +22 -0
  26. package/src/app/config.ts +54 -0
  27. package/src/app/configure.ts +18 -0
  28. package/src/app/context/AppContext.tsx +52 -0
  29. package/src/app/context/SidebarContext.tsx +124 -0
  30. package/src/app/context/UserContext.tsx +139 -0
  31. package/src/app/hooks/useDebounce.ts +12 -0
  32. package/src/app/hooks/useIsDesktop.ts +31 -0
  33. package/src/app/hooks/useWakeLock.ts +49 -0
  34. package/src/app/index.ts +64 -0
  35. package/src/app/layout/ActionButton.tsx +67 -0
  36. package/src/app/layout/AppLayout.tsx +110 -0
  37. package/src/app/layout/AppPage.tsx +268 -0
  38. package/src/app/layout/Breadcrumbs.tsx +57 -0
  39. package/src/app/layout/DesktopRail.tsx +168 -0
  40. package/src/app/layout/FormActionButtons.tsx +69 -0
  41. package/src/app/layout/MobileMenu.tsx +156 -0
  42. package/src/app/layout/MobileTabBar.tsx +43 -0
  43. package/src/app/layout/PageError.tsx +30 -0
  44. package/src/app/layout/PageSkeleton.tsx +65 -0
  45. package/src/app/layout/SectionCards.tsx +33 -0
  46. package/src/app/layout/UserMenu.tsx +104 -0
  47. package/src/app/layout/identity.ts +22 -0
  48. package/src/app/layout/index.ts +20 -0
  49. package/src/app/layout/nav.ts +52 -0
  50. package/src/app/lib/dates.ts +57 -0
  51. package/src/app/lib/env.ts +37 -0
  52. package/src/app/lib/format.ts +61 -0
  53. package/src/app/lib/http.ts +205 -0
  54. package/src/app/lib/lastAuth.ts +45 -0
  55. package/src/app/lib/oauth.ts +32 -0
  56. package/src/app/lib/publicRoutes.ts +33 -0
  57. package/src/app/lib/queryClient.ts +38 -0
  58. package/src/app/lib/search.ts +17 -0
  59. package/src/app/lib/session.ts +36 -0
  60. package/src/app/lib/tokenRefresh.ts +137 -0
  61. package/src/app/lib/transformKeys.ts +60 -0
  62. package/src/app/lib/units.ts +228 -0
  63. package/src/app/lib/unwrap.ts +48 -0
  64. package/src/app/lib/utils.ts +10 -0
  65. package/src/app/stores/authStore.ts +80 -0
  66. package/src/app/stores/storage.ts +40 -0
  67. package/src/app/stores/unitsStore.ts +54 -0
  68. package/src/app/types/index.ts +72 -0
  69. package/src/app/types/query.ts +11 -0
  70. package/src/app/ui/accordion.tsx +54 -0
  71. package/src/app/ui/alert-dialog.tsx +114 -0
  72. package/src/app/ui/alert.tsx +49 -0
  73. package/src/app/ui/avatar.tsx +45 -0
  74. package/src/app/ui/badge-list.tsx +51 -0
  75. package/src/app/ui/badge.tsx +33 -0
  76. package/src/app/ui/button.tsx +49 -0
  77. package/src/app/ui/callout.tsx +22 -0
  78. package/src/app/ui/card.tsx +55 -0
  79. package/src/app/ui/checkbox.tsx +26 -0
  80. package/src/app/ui/clear-button.tsx +42 -0
  81. package/src/app/ui/command.tsx +140 -0
  82. package/src/app/ui/country-dropdown.tsx +184 -0
  83. package/src/app/ui/dialog.tsx +104 -0
  84. package/src/app/ui/dropdown-menu.tsx +185 -0
  85. package/src/app/ui/form.tsx +172 -0
  86. package/src/app/ui/index.ts +36 -0
  87. package/src/app/ui/input.tsx +22 -0
  88. package/src/app/ui/label.tsx +19 -0
  89. package/src/app/ui/option-select.tsx +56 -0
  90. package/src/app/ui/pagination.tsx +90 -0
  91. package/src/app/ui/popover.tsx +29 -0
  92. package/src/app/ui/progress.tsx +25 -0
  93. package/src/app/ui/search-input.tsx +63 -0
  94. package/src/app/ui/select.tsx +151 -0
  95. package/src/app/ui/separator.tsx +24 -0
  96. package/src/app/ui/sheet.tsx +119 -0
  97. package/src/app/ui/slider.tsx +23 -0
  98. package/src/app/ui/switch.tsx +27 -0
  99. package/src/app/ui/textarea.tsx +21 -0
  100. package/src/app/ui/tooltip.tsx +32 -0
  101. package/src/artifact/ArtifactShell.tsx +2 -2
  102. package/src/artifact/Gate.tsx +2 -2
  103. package/src/artifact/Writeup.tsx +7 -7
  104. package/src/chrome/SiteShell.tsx +141 -0
  105. package/src/chrome/index.ts +2 -0
  106. package/src/components/Disclosure.tsx +80 -14
  107. package/src/components/GateLogin.tsx +1 -1
  108. package/src/components/Watermark.tsx +1 -1
  109. package/src/components/index.ts +6 -2
  110. package/src/gate/index.ts +1 -1
  111. package/src/gate/middleware.ts +17 -6
  112. package/src/gate/wall.ts +60 -2
  113. package/src/lib/basePathAudit.ts +96 -0
  114. package/src/lib/displayDate.ts +25 -0
  115. package/src/lib/honeypot.ts +11 -0
  116. package/src/lib/hours.ts +28 -0
  117. package/src/lib/sitemap.ts +34 -0
  118. package/src/lib/trackEvent.ts +26 -0
  119. package/src/lib/useOpenHeight.ts +28 -0
  120. package/src/primitives/Button.tsx +3 -1
  121. package/src/primitives/Chip.tsx +3 -1
  122. package/src/primitives/ImageFrame.tsx +48 -0
  123. package/src/primitives/ProseBlocks.tsx +67 -0
  124. package/src/primitives/QuoteCard.tsx +20 -0
  125. package/src/primitives/StarRow.tsx +37 -0
  126. package/src/primitives/TickList.tsx +24 -0
  127. package/src/primitives/index.ts +5 -0
  128. package/src/routing/BasePathProvider.tsx +47 -0
  129. package/src/routing/basePath.ts +57 -0
  130. package/src/routing/index.ts +8 -0
  131. package/src/scripts/check-base-path.ts +130 -0
  132. package/src/scripts/clean-sitemap.ts +27 -0
  133. package/src/scripts/contact-canary.ts +98 -0
  134. package/src/scripts/serve-static.ts +29 -3
  135. package/src/sections/BlogIndex.tsx +57 -0
  136. package/src/sections/BlogPost.tsx +65 -0
  137. package/src/sections/CTABanner.tsx +93 -0
  138. package/src/sections/CTASection.tsx +60 -0
  139. package/src/sections/ComparisonTable.tsx +114 -0
  140. package/src/sections/ContactForm.tsx +163 -0
  141. package/src/sections/ContactInfoCard.tsx +92 -0
  142. package/src/sections/CredentialList.tsx +28 -0
  143. package/src/sections/DeviceShowcase.tsx +39 -0
  144. package/src/sections/ExpandList.tsx +58 -0
  145. package/src/sections/FAQBrowser.tsx +283 -0
  146. package/src/sections/FeatureDetail.tsx +60 -0
  147. package/src/sections/FeatureGrid.tsx +83 -0
  148. package/src/sections/Gallery.tsx +84 -0
  149. package/src/sections/Hero.tsx +110 -0
  150. package/src/sections/HoursTable.tsx +23 -0
  151. package/src/sections/JumpRail.tsx +35 -0
  152. package/src/sections/LegalPage.tsx +43 -0
  153. package/src/sections/LocationSection.tsx +66 -0
  154. package/src/sections/Locations.tsx +71 -0
  155. package/src/sections/LogoMarquee.tsx +27 -0
  156. package/src/sections/MapEmbed.tsx +75 -0
  157. package/src/sections/MediaBand.tsx +20 -0
  158. package/src/sections/MenuList.tsx +44 -0
  159. package/src/sections/PageHero.tsx +25 -0
  160. package/src/sections/PressQuotes.tsx +51 -0
  161. package/src/sections/PricingBand.tsx +43 -0
  162. package/src/sections/PricingCard.tsx +36 -0
  163. package/src/sections/ProcessSteps.tsx +45 -0
  164. package/src/sections/ProfileCards.tsx +73 -0
  165. package/src/sections/RevealSlider.tsx +156 -0
  166. package/src/sections/ReviewCarousel.tsx +223 -0
  167. package/src/sections/ReviewWall.tsx +220 -0
  168. package/src/sections/SocialStrip.tsx +83 -0
  169. package/src/sections/SplitSection.tsx +56 -0
  170. package/src/sections/index.ts +76 -0
  171. package/src/sections/siteConfig.ts +201 -0
  172. package/src/styles/app.css +673 -0
  173. package/src/styles/artifact.css +472 -399
  174. package/src/styles/base.css +49 -47
  175. package/src/styles/chrome.css +71 -71
  176. package/src/styles/components.css +150 -140
  177. package/src/styles/gate.css +29 -29
  178. package/src/styles/primitives.css +190 -25
  179. package/src/styles/sections.css +2652 -0
  180. package/src/styles/star-rating.css +2 -2
  181. package/src/vite/index.ts +9 -2
  182. package/src/vite/marketing-config.ts +128 -0
  183. package/src/components/Collapsible.tsx +0 -43
  184. /package/src/scripts/{build-gate-payload.mjs → seal-document.mjs} +0 -0
@@ -1,10 +1,10 @@
1
1
  /* The access gate's two faces: the sign in screen (GateLogin) and the
2
2
  confidential watermark (Watermark). Every value is a theme token, so the
3
- gate recolors with the rest of a site. Scoped under `.mkt` or
4
- `.atelic-ui`, which also lifts each rule over the base heading and
5
- paragraph rules inside this layer. */
3
+ gate recolors with the rest of a site. Scoped under `.atelic-ui`,
4
+ which also lifts each rule over the base heading and paragraph rules
5
+ inside this layer. */
6
6
  @layer atelic-ui {
7
- :is(.mkt, .atelic-ui) .gate-screen {
7
+ .atelic-ui .gate-screen {
8
8
  min-height: 100vh;
9
9
  display: flex;
10
10
  flex-direction: column;
@@ -12,20 +12,20 @@
12
12
  color: var(--ink);
13
13
  }
14
14
 
15
- :is(.mkt, .atelic-ui) .gate-brand {
15
+ .atelic-ui .gate-brand {
16
16
  display: flex;
17
17
  align-items: baseline;
18
18
  gap: 10px;
19
19
  padding: 22px 32px;
20
20
  border-bottom: 1px solid var(--neutral-100);
21
21
  }
22
- :is(.mkt, .atelic-ui) .gate-brand-name {
22
+ .atelic-ui .gate-brand-name {
23
23
  font-family: var(--font-serif);
24
24
  font-style: italic;
25
25
  font-size: 24px;
26
26
  color: var(--ui-primary, var(--primary));
27
27
  }
28
- :is(.mkt, .atelic-ui) .gate-brand-sub {
28
+ .atelic-ui .gate-brand-sub {
29
29
  font-family: var(--font-mono);
30
30
  font-size: 11px;
31
31
  letter-spacing: 0.22em;
@@ -33,7 +33,7 @@
33
33
  color: var(--ink-soft);
34
34
  }
35
35
 
36
- :is(.mkt, .atelic-ui) .gate-body {
36
+ .atelic-ui .gate-body {
37
37
  flex: 1;
38
38
  display: flex;
39
39
  flex-direction: column;
@@ -42,7 +42,7 @@
42
42
  text-align: center;
43
43
  padding: 52px 24px 44px;
44
44
  }
45
- :is(.mkt, .atelic-ui) .gate-eyebrow {
45
+ .atelic-ui .gate-eyebrow {
46
46
  font-family: var(--font-mono);
47
47
  font-size: 12px;
48
48
  letter-spacing: 0.18em;
@@ -50,7 +50,7 @@
50
50
  color: var(--ui-primary, var(--primary));
51
51
  margin: 0 0 16px;
52
52
  }
53
- :is(.mkt, .atelic-ui) .gate-headline {
53
+ .atelic-ui .gate-headline {
54
54
  font-family: var(--font-serif);
55
55
  font-size: clamp(2rem, 4.4vw, 2.7rem);
56
56
  line-height: 1.08;
@@ -59,7 +59,7 @@
59
59
  text-wrap: balance;
60
60
  color: var(--ink);
61
61
  }
62
- :is(.mkt, .atelic-ui) .gate-lead {
62
+ .atelic-ui .gate-lead {
63
63
  font-size: 1.05rem;
64
64
  color: var(--ink-soft);
65
65
  max-width: 46ch;
@@ -67,7 +67,7 @@
67
67
  text-wrap: pretty;
68
68
  }
69
69
 
70
- :is(.mkt, .atelic-ui) .gate-form {
70
+ .atelic-ui .gate-form {
71
71
  display: flex;
72
72
  gap: 10px;
73
73
  width: 100%;
@@ -75,7 +75,7 @@
75
75
  flex-wrap: wrap;
76
76
  justify-content: center;
77
77
  }
78
- :is(.mkt, .atelic-ui) .gate-input {
78
+ .atelic-ui .gate-input {
79
79
  flex: 1;
80
80
  min-width: 220px;
81
81
  font-family: var(--font-sans);
@@ -89,29 +89,29 @@
89
89
  color: var(--ink);
90
90
  outline: none;
91
91
  }
92
- :is(.mkt, .atelic-ui) .gate-input:focus-visible {
92
+ .atelic-ui .gate-input:focus-visible {
93
93
  border-color: var(--ui-primary, var(--primary));
94
94
  box-shadow: 0 0 0 3px var(--primary-focus-ring);
95
95
  }
96
- :is(.mkt, .atelic-ui) .gate-input::placeholder {
96
+ .atelic-ui .gate-input::placeholder {
97
97
  color: var(--neutral-500);
98
98
  }
99
99
 
100
- :is(.mkt, .atelic-ui) .gate-fine {
100
+ .atelic-ui .gate-fine {
101
101
  margin: 26px 0 0;
102
102
  font-family: var(--font-mono);
103
103
  font-size: 11.5px;
104
104
  color: var(--neutral-500);
105
105
  }
106
- :is(.mkt, .atelic-ui) .gate-fine a {
106
+ .atelic-ui .gate-fine a {
107
107
  color: var(--ui-primary, var(--primary));
108
108
  text-decoration: none;
109
109
  }
110
- :is(.mkt, .atelic-ui) .gate-fine a:hover {
110
+ .atelic-ui .gate-fine a:hover {
111
111
  text-decoration: underline;
112
112
  }
113
113
 
114
- :is(.mkt, .atelic-ui) .gate-sent {
114
+ .atelic-ui .gate-sent {
115
115
  margin: 4px auto 0;
116
116
  max-width: 440px;
117
117
  border: 1px solid var(--neutral-100);
@@ -121,10 +121,10 @@
121
121
  text-align: left;
122
122
  color: var(--ink-soft);
123
123
  }
124
- :is(.mkt, .atelic-ui) .gate-sent strong {
124
+ .atelic-ui .gate-sent strong {
125
125
  color: var(--ink);
126
126
  }
127
- :is(.mkt, .atelic-ui) .gate-error {
127
+ .atelic-ui .gate-error {
128
128
  margin: 0 0 22px;
129
129
  max-width: 44ch;
130
130
  font-size: 0.95rem;
@@ -136,7 +136,7 @@
136
136
  }
137
137
 
138
138
  /* The "Start over" control reads as a link in the fine print. */
139
- :is(.mkt, .atelic-ui) .gate-link-button {
139
+ .atelic-ui .gate-link-button {
140
140
  background: none;
141
141
  border: none;
142
142
  color: var(--ui-primary, var(--primary));
@@ -145,13 +145,13 @@
145
145
  padding: 0;
146
146
  }
147
147
  /* The failure line under the form, where the default bottom margin is spare. */
148
- :is(.mkt, .atelic-ui) .gate-error.gate-error-after {
148
+ .atelic-ui .gate-error.gate-error-after {
149
149
  margin-top: 22px;
150
150
  margin-bottom: 0;
151
151
  }
152
152
 
153
153
  /* ---- Watermark ---- */
154
- :is(.mkt, .atelic-ui) .gate-wm {
154
+ .atelic-ui .gate-wm {
155
155
  position: fixed;
156
156
  right: 16px;
157
157
  bottom: 16px;
@@ -170,24 +170,24 @@
170
170
  box-shadow: var(--shadow-md, 0 8px 24px -10px rgba(0, 0, 0, 0.5));
171
171
  pointer-events: none;
172
172
  }
173
- :is(.mkt, .atelic-ui) .gate-wm b {
173
+ .atelic-ui .gate-wm b {
174
174
  color: #fff;
175
175
  font-weight: 600;
176
176
  }
177
- :is(.mkt, .atelic-ui) .gate-wm-dot {
177
+ .atelic-ui .gate-wm-dot {
178
178
  width: 7px;
179
179
  height: 7px;
180
180
  border-radius: 50%;
181
181
  background: var(--primary-bright);
182
182
  flex-shrink: 0;
183
183
  }
184
- :is(.mkt, .atelic-ui) .gate-wm-text {
184
+ .atelic-ui .gate-wm-text {
185
185
  overflow: hidden;
186
186
  text-overflow: ellipsis;
187
187
  white-space: nowrap;
188
188
  }
189
189
 
190
- :is(.mkt, .atelic-ui) .gate-wm-tile-layer {
190
+ .atelic-ui .gate-wm-tile-layer {
191
191
  position: fixed;
192
192
  inset: 0;
193
193
  z-index: 2147482000;
@@ -195,7 +195,7 @@
195
195
  overflow: hidden;
196
196
  opacity: 0.5;
197
197
  }
198
- :is(.mkt, .atelic-ui) .gate-wm-tile {
198
+ .atelic-ui .gate-wm-tile {
199
199
  position: absolute;
200
200
  font-family: var(--font-mono);
201
201
  font-size: 12px;
@@ -4,7 +4,7 @@
4
4
  writes outside a layer still wins over them whatever its specificity. */
5
5
  @layer atelic-ui {
6
6
  /* CTA buttons: primary (brand), dark (ink), ghost (outline). */
7
- :is(.mkt, .atelic-ui) .btn {
7
+ .atelic-ui .btn {
8
8
  display: inline-flex;
9
9
  align-items: center;
10
10
  justify-content: center;
@@ -22,56 +22,56 @@
22
22
  text-align: center;
23
23
  line-height: 1;
24
24
  }
25
- :is(.mkt, .atelic-ui) .btn:active {
25
+ .atelic-ui .btn:active {
26
26
  transform: translateY(1px) scale(0.99);
27
27
  }
28
- :is(.mkt, .atelic-ui) .btn-primary {
28
+ .atelic-ui .btn-primary {
29
29
  background: var(--primary);
30
30
  color: #fff;
31
31
  box-shadow: var(--primary-shadow);
32
32
  }
33
- :is(.mkt, .atelic-ui) .btn-primary:hover {
33
+ .atelic-ui .btn-primary:hover {
34
34
  background: var(--primary-deep);
35
35
  box-shadow: var(--primary-shadow-hover);
36
36
  transform: translateY(-1px);
37
37
  }
38
- :is(.mkt, .atelic-ui) .btn-dark {
38
+ .atelic-ui .btn-dark {
39
39
  background: var(--ink);
40
40
  color: var(--surface);
41
41
  }
42
- :is(.mkt, .atelic-ui) .btn-dark:hover {
42
+ .atelic-ui .btn-dark:hover {
43
43
  background: #000;
44
44
  transform: translateY(-1px);
45
45
  }
46
- :is(.mkt, .atelic-ui) .btn-ghost {
46
+ .atelic-ui .btn-ghost {
47
47
  background: transparent;
48
48
  color: var(--ink);
49
49
  box-shadow: inset 0 0 0 1.5px var(--neutral-300);
50
50
  }
51
- :is(.mkt, .atelic-ui) .btn-ghost:hover {
51
+ .atelic-ui .btn-ghost:hover {
52
52
  box-shadow: inset 0 0 0 1.5px var(--ink);
53
53
  }
54
- :is(.mkt, .atelic-ui) .btn-ghost.on-dark {
54
+ .atelic-ui .btn-ghost.on-dark {
55
55
  color: #fff;
56
56
  box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, 0.45);
57
57
  }
58
- :is(.mkt, .atelic-ui) .btn-ghost.on-dark:hover {
58
+ .atelic-ui .btn-ghost.on-dark:hover {
59
59
  box-shadow: inset 0 0 0 1.5px #fff;
60
60
  background: rgba(255, 255, 255, 0.08);
61
61
  }
62
- :is(.mkt, .atelic-ui) .btn-lg {
62
+ .atelic-ui .btn-lg {
63
63
  padding: 1.1em 2em;
64
64
  font-size: 1.08rem;
65
65
  }
66
- :is(.mkt, .atelic-ui) .btn .arrow {
66
+ .atelic-ui .btn .arrow {
67
67
  transition: transform 0.2s ease;
68
68
  }
69
- :is(.mkt, .atelic-ui) .btn:hover .arrow {
69
+ .atelic-ui .btn:hover .arrow {
70
70
  transform: translateX(3px);
71
71
  }
72
72
 
73
73
  /* Chip: small mono tag for credentials, feature tags, service areas. */
74
- :is(.mkt, .atelic-ui) .chip {
74
+ .atelic-ui .chip {
75
75
  display: inline-flex;
76
76
  align-items: center;
77
77
  gap: 0.5em;
@@ -86,16 +86,16 @@
86
86
  box-shadow: inset 0 0 0 1px var(--neutral-200);
87
87
  white-space: nowrap;
88
88
  }
89
- :is(.mkt, .atelic-ui) .chip .dot {
89
+ .atelic-ui .chip .dot {
90
90
  width: 6px;
91
91
  height: 6px;
92
92
  border-radius: 50%;
93
93
  background: var(--primary);
94
94
  }
95
- :is(.mkt, .atelic-ui) .chip svg {
95
+ .atelic-ui .chip svg {
96
96
  flex: none;
97
97
  }
98
- :is(.mkt, .atelic-ui) .chip.on-dark {
98
+ .atelic-ui .chip.on-dark {
99
99
  background: rgba(255, 255, 255, 0.08);
100
100
  color: #efe7da;
101
101
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.18);
@@ -104,7 +104,7 @@
104
104
  /* Eyebrow: mono uppercase kicker trailed by a bar that fades off to the
105
105
  right — the wordmark's signature, in the brand's primary. This fade is
106
106
  the only rendering; the old leading-dash mode is retired. */
107
- :is(.mkt, .atelic-ui) .eyebrow {
107
+ .atelic-ui .eyebrow {
108
108
  font-family: var(--font-mono);
109
109
  font-size: 0.74rem;
110
110
  font-weight: 500;
@@ -115,13 +115,13 @@
115
115
  align-items: center;
116
116
  gap: 0.6em;
117
117
  }
118
- :is(.mkt, .atelic-ui) .eyebrow.on-dark {
118
+ .atelic-ui .eyebrow.on-dark {
119
119
  color: var(--primary-bright);
120
120
  }
121
- :is(.mkt, .atelic-ui) .eyebrow.center {
121
+ .atelic-ui .eyebrow.center {
122
122
  justify-content: center;
123
123
  }
124
- :is(.mkt, .atelic-ui) .eyebrow::after {
124
+ .atelic-ui .eyebrow::after {
125
125
  content: "";
126
126
  width: 40px;
127
127
  height: 2px;
@@ -131,23 +131,188 @@
131
131
  background: linear-gradient(90deg, var(--rule-accent, currentColor), transparent);
132
132
  }
133
133
 
134
- :is(.mkt, .atelic-ui) .lead {
134
+ .atelic-ui .lead {
135
135
  font-size: clamp(1.05rem, 2vw, 1.3rem);
136
136
  color: var(--ink-soft);
137
137
  line-height: 1.55;
138
138
  }
139
139
 
140
- :is(.mkt, .atelic-ui) .section-head {
140
+ .atelic-ui .section-head {
141
141
  /* Width is constrained on the lead only: titles size to their section so
142
142
  they never wrap early into dead space (the 60ch cap here was measured
143
143
  at the body font size, boxing every title into ~510px). */
144
144
  max-width: none;
145
145
  }
146
- :is(.mkt, .atelic-ui) .section-head.center {
146
+ .atelic-ui .section-head.center {
147
147
  margin-inline: auto;
148
148
  }
149
- :is(.mkt, .atelic-ui) .section-head .section-head-lead {
149
+ .atelic-ui .section-head .section-head-lead {
150
150
  margin-top: 16px;
151
151
  max-width: 60ch;
152
152
  }
153
153
  }
154
+
155
+ /* The five primitives that came from template-marketing in 0.19.0 (ATE-643):
156
+ the image frame, the prose body, the quote card, the star row, and the tick
157
+ list. Scoped .atelic-ui alone. */
158
+ @layer atelic-ui {
159
+ /* ---- ImageFrame (image-frame.css) ---- */
160
+ /* Image frame: rounded, shadowed media container. Auto margins keep a
161
+ maxWidth-constrained frame centered when its column is wider than it is
162
+ (single-column mobile stacks); full-width frames are unaffected. */
163
+ .atelic-ui .imgframe {
164
+ border-radius: var(--radius-lg);
165
+ overflow: hidden;
166
+ box-shadow: var(--shadow-md);
167
+ background: var(--surface-inset);
168
+ position: relative;
169
+ margin-inline: auto;
170
+ }
171
+ /* Lifted variant. A photograph sitting on a warm band reads flat under the
172
+ default frame shadow; this raises it a step. Passed through `className`. */
173
+ .atelic-ui .imgframe.is-lifted {
174
+ box-shadow: var(--shadow-lg);
175
+ }
176
+ .atelic-ui .imgframe img {
177
+ width: 100%;
178
+ height: 100%;
179
+ object-fit: cover;
180
+ }
181
+
182
+ /* Placeholder block for imagery that does not exist yet. */
183
+ .atelic-ui .ph {
184
+ position: relative;
185
+ border-radius: var(--radius-lg);
186
+ background:
187
+ repeating-linear-gradient(
188
+ 135deg,
189
+ rgba(138, 124, 104, 0.1) 0 10px,
190
+ rgba(138, 124, 104, 0.04) 10px 20px
191
+ ),
192
+ var(--surface-inset);
193
+ display: flex;
194
+ align-items: center;
195
+ justify-content: center;
196
+ color: var(--neutral-600);
197
+ font-family: var(--font-mono);
198
+ font-size: 0.8rem;
199
+ letter-spacing: 0.08em;
200
+ text-transform: uppercase;
201
+ text-align: center;
202
+ padding: 20px;
203
+ box-shadow: inset 0 0 0 1px rgba(138, 124, 104, 0.2);
204
+ }
205
+
206
+ /* ---- ProseBlocks (prose-blocks.css) ---- */
207
+ /* Body prose for ported documents: legal pages and blog posts. Measure is
208
+ capped tighter than the marketing sections and the type steps down, because
209
+ this is read continuously rather than scanned. Tokens only. */
210
+
211
+ .atelic-ui .prose-blocks {
212
+ max-width: 70ch;
213
+ }
214
+
215
+ .atelic-ui .pb-heading {
216
+ margin: 40px 0 14px;
217
+ font-size: 1.15rem;
218
+ line-height: 1.3;
219
+ letter-spacing: 0.01em;
220
+ }
221
+
222
+ .atelic-ui .pb-heading:first-child {
223
+ margin-top: 0;
224
+ }
225
+
226
+ .atelic-ui .pb-body {
227
+ margin: 0 0 16px;
228
+ font-size: 1rem;
229
+ line-height: 1.75;
230
+ color: var(--neutral-700);
231
+ }
232
+
233
+ /* The base reset strips list markers site wide, which suits nav and card
234
+ lists but not running prose: a bulleted list of symptoms reads as a set of
235
+ orphaned sentences without them. Restore markers here only. */
236
+ .atelic-ui .pb-list {
237
+ margin: 0 0 16px;
238
+ padding-left: 22px;
239
+ list-style: disc outside;
240
+ color: var(--neutral-700);
241
+ }
242
+
243
+ .atelic-ui .pb-list li {
244
+ margin-bottom: 8px;
245
+ font-size: 1rem;
246
+ line-height: 1.75;
247
+ }
248
+
249
+ /* Ported prose carries long unbroken strings (URLs, citations, defined terms
250
+ in caps) that would otherwise push the column past the viewport on a phone. */
251
+ .atelic-ui .pb-body,
252
+ .atelic-ui .pb-list li {
253
+ overflow-wrap: break-word;
254
+ }
255
+
256
+ /* ---- QuoteCard (quote-card.css) ---- */
257
+ .atelic-ui .quote-card {
258
+ background: var(--card);
259
+ border-radius: var(--radius-lg);
260
+ padding: clamp(28px, 5vw, 52px);
261
+ box-shadow: var(--shadow-sm);
262
+ border: 1px solid var(--hairline);
263
+ margin: 0;
264
+ }
265
+ .atelic-ui .quote-card blockquote {
266
+ font-family: var(--font-serif);
267
+ font-style: italic;
268
+ font-size: clamp(1.4rem, 3.4vw, 2.1rem);
269
+ line-height: 1.34;
270
+ color: var(--ink);
271
+ margin: 0;
272
+ }
273
+ .atelic-ui .quote-card .by {
274
+ margin-top: 22px;
275
+ font-family: var(--font-mono);
276
+ font-size: 0.8rem;
277
+ letter-spacing: 0.06em;
278
+ text-transform: uppercase;
279
+ color: var(--neutral-600);
280
+ }
281
+
282
+ /* ---- StarRow (star-row.css) ---- */
283
+ /* Star row: filled rating stars, brand colored. Size comes from the
284
+ component's `size` prop so one row can be small in a card and larger
285
+ beside an aggregate score. */
286
+ .atelic-ui .star-row {
287
+ display: inline-flex;
288
+ align-items: center;
289
+ gap: 2px;
290
+ flex: 0 0 auto;
291
+ color: var(--primary);
292
+ }
293
+
294
+ /* ---- TickList (tick-list.css) ---- */
295
+ .atelic-ui ul.ticks {
296
+ display: flex;
297
+ flex-direction: column;
298
+ gap: 10px;
299
+ }
300
+ .atelic-ui ul.ticks li {
301
+ display: flex;
302
+ gap: 12px;
303
+ align-items: flex-start;
304
+ color: var(--ink-soft);
305
+ }
306
+ .atelic-ui ul.ticks li .tick {
307
+ flex: 0 0 auto;
308
+ width: 22px;
309
+ height: 22px;
310
+ border-radius: 50%;
311
+ margin-top: 1px;
312
+ background: var(--primary-tint);
313
+ color: var(--primary);
314
+ display: grid;
315
+ place-items: center;
316
+ box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--primary) 45%, transparent);
317
+ }
318
+ }