@koehler8/cms 1.0.0-beta.5

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 (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +202 -0
  3. package/bin/cms-generate-public-assets.js +27 -0
  4. package/bin/cms-validate-extensions.js +18 -0
  5. package/bin/cms-validate-themes.js +7 -0
  6. package/extensions/manifest.schema.json +125 -0
  7. package/package.json +84 -0
  8. package/public/img/preloaders/preloader-black.svg +1 -0
  9. package/public/img/preloaders/preloader-white.svg +1 -0
  10. package/public/robots.txt +5 -0
  11. package/scripts/check-overflow.mjs +33 -0
  12. package/scripts/generate-public-assets.js +401 -0
  13. package/scripts/patch-lru-cache-tla.js +164 -0
  14. package/scripts/validate-extensions.mjs +392 -0
  15. package/scripts/validate-themes.mjs +64 -0
  16. package/src/App.vue +3 -0
  17. package/src/components/About.vue +481 -0
  18. package/src/components/AboutValue.vue +361 -0
  19. package/src/components/BackToTop.vue +42 -0
  20. package/src/components/ComingSoon.vue +411 -0
  21. package/src/components/ComingSoonModal.vue +230 -0
  22. package/src/components/Contact.vue +518 -0
  23. package/src/components/Footer.vue +65 -0
  24. package/src/components/FooterMinimal.vue +153 -0
  25. package/src/components/Header.vue +583 -0
  26. package/src/components/Hero.vue +327 -0
  27. package/src/components/Home.vue +144 -0
  28. package/src/components/Intro.vue +130 -0
  29. package/src/components/IntroGate.vue +444 -0
  30. package/src/components/Plan.vue +116 -0
  31. package/src/components/Portfolio.vue +459 -0
  32. package/src/components/Preloader.vue +20 -0
  33. package/src/components/Principles.vue +67 -0
  34. package/src/components/Spacer15.vue +9 -0
  35. package/src/components/Spacer30.vue +9 -0
  36. package/src/components/Spacer40.vue +9 -0
  37. package/src/components/Spacer60.vue +9 -0
  38. package/src/components/StickyCTA.vue +263 -0
  39. package/src/components/Team.vue +432 -0
  40. package/src/components/icons/IconLinkedIn.vue +22 -0
  41. package/src/components/icons/IconX.vue +22 -0
  42. package/src/components/ui/SbCard.vue +52 -0
  43. package/src/components/ui/SkeletonPulse.vue +117 -0
  44. package/src/components/ui/UnitChip.vue +69 -0
  45. package/src/composables/useComingSoonConfig.js +120 -0
  46. package/src/composables/useComingSoonInterstitial.js +27 -0
  47. package/src/composables/useComponentResolver.js +196 -0
  48. package/src/composables/useEngagementTracking.js +187 -0
  49. package/src/composables/useIntroGate.js +46 -0
  50. package/src/composables/useLazyImage.js +77 -0
  51. package/src/composables/usePageConfig.js +184 -0
  52. package/src/composables/usePageMeta.js +76 -0
  53. package/src/composables/usePromoBackgroundStyles.js +67 -0
  54. package/src/constants/locales.js +20 -0
  55. package/src/extensions/extensionLoader.js +512 -0
  56. package/src/main.js +175 -0
  57. package/src/router/index.js +112 -0
  58. package/src/styles/base.css +896 -0
  59. package/src/styles/layout.css +342 -0
  60. package/src/styles/theme-base.css +84 -0
  61. package/src/themes/themeLoader.js +124 -0
  62. package/src/themes/themeManager.js +257 -0
  63. package/src/themes/themeValidator.js +380 -0
  64. package/src/utils/analytics.js +100 -0
  65. package/src/utils/appInfo.js +9 -0
  66. package/src/utils/assetResolver.js +162 -0
  67. package/src/utils/componentRegistry.js +46 -0
  68. package/src/utils/contentRequirements.js +67 -0
  69. package/src/utils/cookieConsent.js +281 -0
  70. package/src/utils/ctaCopy.js +58 -0
  71. package/src/utils/formatNumber.js +115 -0
  72. package/src/utils/imageSources.js +179 -0
  73. package/src/utils/inflateFlatConfig.js +30 -0
  74. package/src/utils/loadConfig.js +271 -0
  75. package/src/utils/semver.js +49 -0
  76. package/src/utils/siteStyles.js +40 -0
  77. package/src/utils/themeColors.js +65 -0
  78. package/src/utils/trackingContext.js +142 -0
  79. package/src/utils/unwrapDefault.js +14 -0
  80. package/src/utils/useScrollReveal.js +48 -0
  81. package/templates/index.html +36 -0
  82. package/themes/base/README.md +23 -0
  83. package/themes/base/theme.config.js +214 -0
  84. package/vite-plugin.js +637 -0
@@ -0,0 +1,459 @@
1
+ <template>
2
+ <section
3
+ id="portfolio"
4
+ class="portfolio-section ui-section ui-section--stacked"
5
+ data-analytics-section="portfolio"
6
+ ref="portfolioSection"
7
+ >
8
+ <div class="container">
9
+ <span id="now"></span>
10
+
11
+ <header v-if="sectionTitle || sectionSubtitle" class="section-header text-center">
12
+ <div v-if="sectionTitle" class="section-heading">
13
+ <h2 class="display-heading">{{ sectionTitle }}</h2>
14
+ <span class="section-divider" aria-hidden="true"></span>
15
+ </div>
16
+ <p v-if="sectionSubtitle" class="section-description">{{ sectionSubtitle }}</p>
17
+ </header>
18
+
19
+ <div>
20
+ <!-- Start Portfolio Filter -->
21
+ <ul class="portfolio-filters" role="list">
22
+ <li role="listitem">
23
+ <button
24
+ type="button"
25
+ class="portfolio-filter"
26
+ :class="{ 'portfolio-filter--active': activeFilter === '*' }"
27
+ @click="setFilter('*')"
28
+ >
29
+ All
30
+ </button>
31
+ </li>
32
+ <li
33
+ v-for="tag in filters"
34
+ :key="tag"
35
+ role="listitem"
36
+ >
37
+ <button
38
+ type="button"
39
+ class="portfolio-filter"
40
+ :class="{ 'portfolio-filter--active': activeFilter === tag }"
41
+ @click="setFilter(tag)"
42
+ >
43
+ {{ tagLabelMap[tag] }}
44
+ </button>
45
+ </li>
46
+ </ul>
47
+ <!-- End Portfolio Filter -->
48
+ <!-- Start Portfolio Content -->
49
+ <transition-group
50
+ name="portfolio"
51
+ tag="div"
52
+ class="portfolio-grid"
53
+ @after-leave="handleAfterLeave"
54
+ >
55
+ <div
56
+ v-for="(project, idx) in displayedProjects"
57
+ :key="project.key"
58
+ class="portfolio-grid__item"
59
+ >
60
+ <div
61
+ class="portfolio-card ui-card ui-card-surface fade-up-in js-scroll-fade"
62
+ :style="{ '--fade-up-delay': `${Math.min(idx, 4) * 0.06}s` }"
63
+ >
64
+ <div class="portfolio-image-frame">
65
+ <img
66
+ class="portfolio-image"
67
+ :src="getImageSrc(project.img)"
68
+ :alt="project.title"
69
+ loading="lazy"
70
+ decoding="async"
71
+ >
72
+ </div>
73
+ <span class="portfolio-tag ui-label-sm">{{ project.displayTags || project.tags }}</span>
74
+ <h3 class="portfolio-title ui-title-md">{{ project.title }}</h3>
75
+ <a class="portfolio-card__link" :href="project.href" aria-label="Visit portfolio link"></a>
76
+ </div>
77
+ </div>
78
+ </transition-group>
79
+ <!-- End Portfolio Content -->
80
+ </div>
81
+ </div>
82
+ </section>
83
+ </template>
84
+
85
+ <script setup>
86
+ import { computed, inject, nextTick, ref, watch } from 'vue';
87
+ import { resolveAsset } from '../utils/assetResolver.js';
88
+ import { registerScrollReveal } from '../utils/useScrollReveal.js';
89
+
90
+ const DEFAULT_TITLE = 'Our Ventures';
91
+ const DEFAULT_SUBTITLE = 'Explore the companies and products we build across the studio.';
92
+
93
+ const projects = ref([]);
94
+ const portfolioSection = ref(null);
95
+ const sectionTitle = ref(DEFAULT_TITLE);
96
+ const sectionSubtitle = ref(DEFAULT_SUBTITLE);
97
+
98
+ const getImageSrc = (img) => {
99
+ if (!img) return '';
100
+ return resolveAsset(`img/portfolio-${img}.png`);
101
+ };
102
+
103
+ const activeFilter = ref('*');
104
+ const displayedProjects = ref([]);
105
+ const pendingProjects = ref(null);
106
+ const leavingCount = ref(0);
107
+
108
+ const ensureTagData = (project) => {
109
+ if (!project) return { entries: [], normalized: [], labels: '' };
110
+ if (Array.isArray(project.tagEntries) && Array.isArray(project.normalizedTags)) {
111
+ return {
112
+ entries: project.tagEntries,
113
+ normalized: project.normalizedTags,
114
+ labels: project.displayTags || project.tagEntries.map(({ label }) => label).join(', '),
115
+ };
116
+ }
117
+ const tagEntries = parseTagEntries(project.tags);
118
+ const normalizedTags = tagEntries.map(({ key }) => key);
119
+ const displayTags = tagEntries.map(({ label }) => label).join(', ');
120
+ project.tagEntries = tagEntries;
121
+ project.normalizedTags = normalizedTags;
122
+ project.displayTags = displayTags;
123
+ return { entries: tagEntries, normalized: normalizedTags, labels: displayTags };
124
+ };
125
+
126
+ const tagLabelMap = computed(() => {
127
+ const map = {};
128
+ projects.value.forEach((project) => {
129
+ const { entries } = ensureTagData(project);
130
+ entries.forEach(({ key, label }) => {
131
+ if (key && label && !map[key]) {
132
+ map[key] = label;
133
+ }
134
+ });
135
+ });
136
+ return map;
137
+ });
138
+
139
+ const filters = computed(() => Object.keys(tagLabelMap.value));
140
+
141
+ const filteredProjects = computed(() => {
142
+ const filter = activeFilter.value;
143
+ if (filter === '*' || !filter) {
144
+ return projects.value;
145
+ }
146
+ return projects.value.filter((project) => {
147
+ const { normalized } = ensureTagData(project);
148
+ if (!normalized.length) return false;
149
+ return normalized.includes(filter);
150
+ });
151
+ });
152
+
153
+ const pageContent = inject('pageContent', ref({}));
154
+
155
+ function parseTagEntries(tags) {
156
+ const entries = [];
157
+ if (Array.isArray(tags)) {
158
+ tags.forEach((tag) => {
159
+ if (typeof tag !== 'string') return;
160
+ const label = tag.trim();
161
+ const key = label.toLowerCase().replace(/\s+/g, '-');
162
+ if (key) {
163
+ entries.push({ key, label: label || key });
164
+ }
165
+ });
166
+ return entries;
167
+ }
168
+ if (typeof tags === 'string') {
169
+ return tags
170
+ .split(',')
171
+ .map((tag) => tag.trim())
172
+ .filter(Boolean)
173
+ .map((label) => ({
174
+ key: label.toLowerCase().replace(/\s+/g, '-'),
175
+ label,
176
+ }));
177
+ }
178
+ return entries;
179
+ }
180
+
181
+ function applyPortfolio(content = {}) {
182
+ const portfolioConfig = content?.portfolio;
183
+ let titleSet = false;
184
+ let subtitleSet = false;
185
+
186
+ sectionTitle.value = DEFAULT_TITLE;
187
+ sectionSubtitle.value = DEFAULT_SUBTITLE;
188
+
189
+ if (Array.isArray(portfolioConfig)) {
190
+ projects.value = portfolioConfig.map((project, index) => buildProjectRecord(project, index));
191
+ } else if (portfolioConfig && typeof portfolioConfig === 'object') {
192
+ if (Object.prototype.hasOwnProperty.call(portfolioConfig, 'title')) {
193
+ sectionTitle.value = portfolioConfig.title || '';
194
+ titleSet = true;
195
+ }
196
+ if (Object.prototype.hasOwnProperty.call(portfolioConfig, 'subtitle')) {
197
+ sectionSubtitle.value = portfolioConfig.subtitle || '';
198
+ subtitleSet = true;
199
+ }
200
+ projects.value = Array.isArray(portfolioConfig.items)
201
+ ? portfolioConfig.items.map((project, index) => buildProjectRecord(project, index))
202
+ : [];
203
+ } else {
204
+ projects.value = [];
205
+ }
206
+
207
+ if (Object.prototype.hasOwnProperty.call(content, 'portfolioTitle')) {
208
+ sectionTitle.value = content.portfolioTitle || '';
209
+ titleSet = true;
210
+ }
211
+ if (Object.prototype.hasOwnProperty.call(content, 'portfolioSubtitle')) {
212
+ sectionSubtitle.value = content.portfolioSubtitle || '';
213
+ subtitleSet = true;
214
+ }
215
+
216
+ if (!titleSet && content?.about?.topButton?.text) {
217
+ sectionTitle.value = content.about.topButton.text;
218
+ titleSet = true;
219
+ }
220
+ if (!subtitleSet && content?.promo?.tagLine) {
221
+ sectionSubtitle.value = content.promo.tagLine;
222
+ subtitleSet = true;
223
+ }
224
+
225
+ if (!sectionTitle.value) {
226
+ sectionTitle.value = DEFAULT_TITLE;
227
+ }
228
+ if (!sectionSubtitle.value) {
229
+ sectionSubtitle.value = DEFAULT_SUBTITLE;
230
+ }
231
+
232
+ activeFilter.value = '*';
233
+ }
234
+
235
+ function setFilter(filter) {
236
+ activeFilter.value = filter;
237
+ }
238
+
239
+ watch(
240
+ () => pageContent.value,
241
+ (content) => {
242
+ applyPortfolio(content || {});
243
+ },
244
+ { immediate: true }
245
+ );
246
+
247
+ watch(
248
+ filteredProjects,
249
+ (nextList) => {
250
+ stageDisplayedProjects(nextList);
251
+ },
252
+ { immediate: true }
253
+ );
254
+
255
+ watch(
256
+ displayedProjects,
257
+ () => {
258
+ nextTick(() => {
259
+ if (!portfolioSection.value) return;
260
+ const targets = portfolioSection.value.querySelectorAll('.js-scroll-fade');
261
+ if (targets.length) {
262
+ registerScrollReveal(targets);
263
+ }
264
+ });
265
+ },
266
+ { immediate: true }
267
+ );
268
+
269
+ function buildProjectRecord(project = {}, index = 0) {
270
+ const tagEntries = parseTagEntries(project?.tags);
271
+ const normalizedTags = tagEntries.map(({ key }) => key);
272
+ const displayTags = tagEntries.map(({ label }) => label).join(', ');
273
+ const baseKey =
274
+ project.id ||
275
+ project.key ||
276
+ project.slug ||
277
+ project.img ||
278
+ (project.title ? project.title.replace(/\s+/g, '-').toLowerCase() : `project-${index}`);
279
+ return {
280
+ ...project,
281
+ key: baseKey,
282
+ tagEntries,
283
+ normalizedTags,
284
+ displayTags,
285
+ };
286
+ }
287
+
288
+ function stageDisplayedProjects(nextList = []) {
289
+ pendingProjects.value = nextList;
290
+ const nextKeySet = new Set(nextList.map((project) => project.key));
291
+ const keepers = displayedProjects.value.filter((project) => nextKeySet.has(project.key));
292
+ const nextLeaving = displayedProjects.value.length - keepers.length;
293
+ leavingCount.value = nextLeaving;
294
+ if (nextLeaving > 0) {
295
+ displayedProjects.value = keepers;
296
+ return;
297
+ }
298
+ displayedProjects.value = nextList;
299
+ pendingProjects.value = null;
300
+ }
301
+
302
+ function handleAfterLeave() {
303
+ if (leavingCount.value <= 0) return;
304
+ leavingCount.value -= 1;
305
+ if (leavingCount.value === 0 && pendingProjects.value) {
306
+ displayedProjects.value = pendingProjects.value;
307
+ pendingProjects.value = null;
308
+ }
309
+ }
310
+ </script>
311
+
312
+ <style scoped>
313
+ .portfolio-section {
314
+ --section-divider-color: var(--portfolio-divider-color, var(--brand-border-highlight, rgba(79, 108, 240, 0.28)));
315
+ --section-description-color: var(--portfolio-subtitle-color, var(--ui-text-muted, rgba(31, 42, 68, 0.72)));
316
+ }
317
+
318
+ .portfolio-filters {
319
+ display: flex;
320
+ flex-wrap: wrap;
321
+ justify-content: center;
322
+ gap: 12px;
323
+ list-style: none;
324
+ margin: 0 0 clamp(16px, 4vw, 32px);
325
+ padding: 0;
326
+ }
327
+
328
+ .portfolio-filter {
329
+ padding: 6px 18px;
330
+ border-radius: 999px;
331
+ font-size: 0.78rem;
332
+ text-transform: uppercase;
333
+ letter-spacing: 0.16em;
334
+ cursor: pointer;
335
+ border: 1px solid var(--portfolio-filter-border, rgba(79, 108, 240, 0.24));
336
+ color: var(--portfolio-filter-color, var(--ui-text-primary, #1f2a44));
337
+ background: var(--portfolio-filter-bg, rgba(79, 108, 240, 0.05));
338
+ transition: color 0.2s ease, background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
339
+ }
340
+
341
+ .portfolio-filter:hover,
342
+ .portfolio-filter:focus-visible {
343
+ color: var(--portfolio-filter-hover-color, var(--brand-accent-electric, #4f6cf0));
344
+ border-color: var(--portfolio-filter-hover-border, rgba(79, 108, 240, 0.5));
345
+ transform: translateY(-1px);
346
+ }
347
+
348
+ .portfolio-filter--active {
349
+ color: var(--portfolio-filter-active-color, #fff);
350
+ background: var(--portfolio-filter-active-bg, var(--brand-accent-electric, #4f6cf0));
351
+ border-color: transparent;
352
+ }
353
+ .portfolio-filter--active:hover,
354
+ .portfolio-filter--active:focus-visible {
355
+ color: var(--portfolio-filter-active-color, #fff);
356
+ border-color: transparent;
357
+ transform: translateY(-1px);
358
+ box-shadow: 0 6px 18px color-mix(in srgb, var(--portfolio-filter-active-bg, #4f6cf0) 45%, transparent);
359
+ }
360
+
361
+ .portfolio-grid {
362
+ display: flex;
363
+ flex-wrap: wrap;
364
+ gap: clamp(18px, 4vw, 32px);
365
+ justify-content: center;
366
+ width: 100%;
367
+ max-width: min(1120px, 100%);
368
+ margin: 0 auto;
369
+ }
370
+
371
+ .portfolio-grid__item {
372
+ flex: 0 1 320px;
373
+ width: min(320px, 100%);
374
+ display: flex;
375
+ }
376
+
377
+ .portfolio-grid__item .portfolio-card {
378
+ width: 100%;
379
+ }
380
+
381
+ .portfolio-card {
382
+ padding: clamp(10px, 1.8vw, 16px);
383
+ text-align: center;
384
+ display: flex;
385
+ flex-direction: column;
386
+ gap: 8px;
387
+ transition: transform 0.25s ease, box-shadow 0.25s ease;
388
+ position: relative;
389
+ }
390
+
391
+ .portfolio-card:hover {
392
+ transform: translateY(-4px);
393
+ box-shadow: 0 22px 48px rgba(15, 23, 42, 0.18);
394
+ }
395
+
396
+ .portfolio-card__link {
397
+ position: absolute;
398
+ inset: 0;
399
+ z-index: 2;
400
+ text-indent: -9999px;
401
+ }
402
+
403
+ .portfolio-card__link:focus-visible {
404
+ outline: 2px solid var(--brand-accent-electric, #4f6cf0);
405
+ outline-offset: 4px;
406
+ }
407
+
408
+ .portfolio-image-frame {
409
+ width: 100%;
410
+ padding: clamp(8px, 2.2vw, 14px);
411
+ border-radius: var(--portfolio-image-radius, 18px);
412
+ background: var(--portfolio-image-bg, rgba(255, 255, 255, 0.08));
413
+ border: 1px solid var(--portfolio-image-border, rgba(255, 255, 255, 0.12));
414
+ display: flex;
415
+ align-items: center;
416
+ justify-content: center;
417
+ }
418
+
419
+ .portfolio-image {
420
+ width: 100%;
421
+ height: auto;
422
+ display: block;
423
+ object-fit: contain;
424
+ object-position: center;
425
+ filter: drop-shadow(0 8px 18px rgba(15, 23, 42, 0.18));
426
+ }
427
+
428
+ .portfolio-tag {
429
+ color: var(--portfolio-tag-color, var(--ui-text-muted, rgba(31, 42, 68, 0.72)));
430
+ }
431
+
432
+ .portfolio-title {
433
+ margin: 0;
434
+ color: var(--portfolio-title-color, var(--ui-text-primary, #1f2a44));
435
+ }
436
+
437
+ .portfolio-move {
438
+ transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
439
+ }
440
+
441
+ .portfolio-leave-active {
442
+ transition:
443
+ opacity 0.12s ease,
444
+ transform 0.12s cubic-bezier(0.22, 1, 0.36, 1);
445
+ }
446
+
447
+ .portfolio-enter-active {
448
+ transition:
449
+ opacity 0.2s ease 0.08s,
450
+ transform 0.2s cubic-bezier(0.22, 1, 0.36, 1) 0.08s;
451
+ }
452
+
453
+ .portfolio-enter-from,
454
+ .portfolio-leave-to {
455
+ opacity: 0;
456
+ transform: translateY(24px) scale(0.96);
457
+ }
458
+
459
+ </style>
@@ -0,0 +1,20 @@
1
+ <template>
2
+ <div id="loading">
3
+ <div id="loading-center">
4
+ <div id="loading-center-absolute">
5
+ <div class="object" id="object_four"></div>
6
+ <div class="object" id="object_three"></div>
7
+ <div class="object" id="object_two"></div>
8
+ <div class="object" id="object_one"></div>
9
+ </div>
10
+ </div>
11
+ </div>
12
+ </template>
13
+
14
+ <script setup>
15
+ // Add any header-specific logic here if needed.
16
+ </script>
17
+
18
+ <style scoped>
19
+ /* Add header-specific styles here. */
20
+ </style>
@@ -0,0 +1,67 @@
1
+ <template>
2
+ <section id="principles" class="section-shell principles-section" data-analytics-section="principles">
3
+ <div class="container">
4
+ <header class="section-header text-center">
5
+ <div class="section-heading">
6
+ <h2 class="display-heading principles-heading">{{ principlesTitle }}</h2>
7
+ <span class="section-divider" aria-hidden="true"></span>
8
+ </div>
9
+ <p v-if="principlesSubtitle" class="section-description">{{ principlesSubtitle }}</p>
10
+ </header>
11
+ <div class="principles-list-wrapper">
12
+ <ul class="principles-list">
13
+ <li
14
+ v-for="(principle, idx) in principlesList"
15
+ :key="idx"
16
+ class="principles-list__item"
17
+ >
18
+ {{ principle }}
19
+ </li>
20
+ </ul>
21
+ </div>
22
+ </div>
23
+ </section>
24
+ </template>
25
+
26
+ <script setup>
27
+ import { computed, inject, ref } from 'vue';
28
+
29
+ const pageContent = inject('pageContent', ref({}));
30
+
31
+ const principlesData = computed(() => pageContent.value?.principles || {});
32
+
33
+ const principlesTitle = computed(() => principlesData.value?.title || 'Principles');
34
+ const principlesSubtitle = computed(() => principlesData.value?.subtitle || '');
35
+ const principlesList = computed(() =>
36
+ Array.isArray(principlesData.value?.items) ? principlesData.value.items : []
37
+ );
38
+ </script>
39
+
40
+ <style scoped>
41
+ .principles-heading {
42
+ font-weight: 700;
43
+ letter-spacing: 0.18em;
44
+ }
45
+
46
+ .principles-list-wrapper {
47
+ display: flex;
48
+ justify-content: center;
49
+ }
50
+
51
+ .principles-list {
52
+ list-style: none;
53
+ margin: 0;
54
+ padding: 0;
55
+ max-width: 720px;
56
+ display: flex;
57
+ flex-direction: column;
58
+ gap: 18px;
59
+ font-size: 1rem;
60
+ line-height: 1.8;
61
+ color: var(--ui-text-primary, var(--brand-fg-100));
62
+ }
63
+
64
+ .principles-list__item {
65
+ padding-left: 0;
66
+ }
67
+ </style>
@@ -0,0 +1,9 @@
1
+ <template>
2
+ <div class="ui-spacer" aria-hidden="true" :style="spacerStyle"></div>
3
+ </template>
4
+
5
+ <script setup>
6
+ const spacerStyle = {
7
+ '--ui-spacer-size': '15px',
8
+ };
9
+ </script>
@@ -0,0 +1,9 @@
1
+ <template>
2
+ <div class="ui-spacer" aria-hidden="true" :style="spacerStyle"></div>
3
+ </template>
4
+
5
+ <script setup>
6
+ const spacerStyle = {
7
+ '--ui-spacer-size': '30px',
8
+ };
9
+ </script>
@@ -0,0 +1,9 @@
1
+ <template>
2
+ <div class="ui-spacer" aria-hidden="true" :style="spacerStyle"></div>
3
+ </template>
4
+
5
+ <script setup>
6
+ const spacerStyle = {
7
+ '--ui-spacer-size': '40px',
8
+ };
9
+ </script>
@@ -0,0 +1,9 @@
1
+ <template>
2
+ <div class="ui-spacer" aria-hidden="true" :style="spacerStyle"></div>
3
+ </template>
4
+
5
+ <script setup>
6
+ const spacerStyle = {
7
+ '--ui-spacer-size': '60px',
8
+ };
9
+ </script>