@eox/pages-theme-eox 2.0.0-alpha.1 → 2.0.0-alpha.10
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.
- package/.gitlab-ci.yml +4 -1
- package/README.md +75 -11
- package/cypress/support/mocks/authors.data.js +14 -0
- package/cypress/support/mocks/helpers.js +65 -4
- package/cypress.config.js +9 -0
- package/package.json +3 -1
- package/src/Layout.vue +14 -3
- package/src/components/BlogList.vue +74 -55
- package/src/components/BlogPost.vue +46 -24
- package/src/components/CTASection.vue +18 -4
- package/src/components/FeatureCard/Index.vue +113 -26
- package/src/components/FeatureCard/Poster.vue +25 -3
- package/src/components/FeatureSection.cy.js +16 -0
- package/src/components/FeatureSection.vue +3 -8
- package/src/components/FeaturesGallery.vue +34 -2
- package/src/components/Footer.cy.js +52 -2
- package/src/components/Footer.vue +17 -5
- package/src/components/HeroSection.cy.js +20 -0
- package/src/components/HeroSection.vue +139 -24
- package/src/components/ImageCarousel.vue +11 -2
- package/src/components/ImageElement.vue +16 -4
- package/src/components/ImageGallery.vue +29 -4
- package/src/components/LogoSection.vue +26 -3
- package/src/components/MobileNavDropdown.vue +13 -3
- package/src/components/NavBar.cy.js +15 -0
- package/src/components/NavBar.vue +149 -21
- package/src/components/NavDropdown.vue +12 -2
- package/src/components/OpenPositions.vue +292 -0
- package/src/components/TagList.vue +32 -14
- package/src/components/TeamPage.vue +34 -20
- package/src/components/TestimonialsSection.cy.js +2 -5
- package/src/components/TestimonialsSection.vue +119 -16
- package/src/components/TextSection.cy.js +115 -0
- package/src/components/TextSection.vue +396 -0
- package/src/helpers.js +182 -37
- package/src/helpers.test.js +181 -0
- package/src/index.js +4 -0
- package/src/pages/author/[author].md +1 -0
- package/src/pages/author/[author].paths.js +2 -2
- package/src/pages/blog.md +1 -0
- package/src/pages/tag/[tag].md +1 -0
- package/src/pages/tag.md +1 -0
- package/src/pages/team.md +2 -1
- package/src/posts.data.js +39 -1
- package/src/style.css +232 -11
- package/src/tagUtils.js +5 -5
- package/src/vitepressConfig.mjs +6 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<section class="testimonials-section full-width surface-container-
|
|
2
|
+
<section class="testimonials-section full-width surface-container-lowest">
|
|
3
3
|
<div class="holder">
|
|
4
4
|
<!-- Left Side: Headline -->
|
|
5
5
|
<div class="headline">
|
|
@@ -9,10 +9,27 @@
|
|
|
9
9
|
</div>
|
|
10
10
|
|
|
11
11
|
<!-- Right Side: Text Block -->
|
|
12
|
-
<div
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
<div
|
|
13
|
+
v-if="testimonials && testimonials.length > 0"
|
|
14
|
+
class="text-block"
|
|
15
|
+
role="region"
|
|
16
|
+
aria-roledescription="carousel"
|
|
17
|
+
:aria-label="title"
|
|
18
|
+
>
|
|
19
|
+
<!-- Testimonial Quotes Overlapping Grid -->
|
|
20
|
+
<div class="quotes-grid" aria-live="polite">
|
|
21
|
+
<div
|
|
22
|
+
v-for="(item, index) in testimonials"
|
|
23
|
+
:key="index"
|
|
24
|
+
class="quote-slide"
|
|
25
|
+
:class="{
|
|
26
|
+
active: index === currentIndex,
|
|
27
|
+
'quote-text': index === currentIndex,
|
|
28
|
+
}"
|
|
29
|
+
:aria-hidden="index !== currentIndex"
|
|
30
|
+
>
|
|
31
|
+
“{{ item.quote }}”
|
|
32
|
+
</div>
|
|
16
33
|
</div>
|
|
17
34
|
|
|
18
35
|
<!-- Divider / Separator -->
|
|
@@ -20,14 +37,28 @@
|
|
|
20
37
|
|
|
21
38
|
<!-- Frame 469: Author info & sliders -->
|
|
22
39
|
<div class="frame-469">
|
|
23
|
-
<!-- Frame 468: Author Name and Role -->
|
|
40
|
+
<!-- Frame 468: Author Name and Role Overlapping Grid -->
|
|
24
41
|
<div class="frame-468">
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
{
|
|
30
|
-
|
|
42
|
+
<div
|
|
43
|
+
v-for="(item, index) in testimonials"
|
|
44
|
+
:key="index"
|
|
45
|
+
class="author-slide"
|
|
46
|
+
:class="{ active: index === currentIndex }"
|
|
47
|
+
:aria-hidden="index !== currentIndex"
|
|
48
|
+
>
|
|
49
|
+
<span
|
|
50
|
+
class="author-name-text"
|
|
51
|
+
:class="{ 'author-name': index === currentIndex }"
|
|
52
|
+
>
|
|
53
|
+
{{ item.author }}
|
|
54
|
+
</span>
|
|
55
|
+
<span
|
|
56
|
+
class="author-role-text"
|
|
57
|
+
:class="{ 'author-role': index === currentIndex }"
|
|
58
|
+
>
|
|
59
|
+
{{ item.role }}
|
|
60
|
+
</span>
|
|
61
|
+
</div>
|
|
31
62
|
</div>
|
|
32
63
|
|
|
33
64
|
<!-- sliders: Navigation Buttons -->
|
|
@@ -57,7 +88,7 @@
|
|
|
57
88
|
</template>
|
|
58
89
|
|
|
59
90
|
<script setup>
|
|
60
|
-
import { ref, computed } from "vue";
|
|
91
|
+
import { ref, computed, watch } from "vue";
|
|
61
92
|
|
|
62
93
|
const props = defineProps({
|
|
63
94
|
title: {
|
|
@@ -91,6 +122,16 @@ const props = defineProps({
|
|
|
91
122
|
|
|
92
123
|
const currentIndex = ref(0);
|
|
93
124
|
|
|
125
|
+
// Clamp currentIndex if testimonials prop array changes dynamically
|
|
126
|
+
watch(
|
|
127
|
+
() => props.testimonials?.length,
|
|
128
|
+
(len) => {
|
|
129
|
+
if (len && currentIndex.value >= len) {
|
|
130
|
+
currentIndex.value = 0;
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
);
|
|
134
|
+
|
|
94
135
|
const currentTestimonial = computed(() => {
|
|
95
136
|
if (!props.testimonials || props.testimonials.length === 0) return null;
|
|
96
137
|
return props.testimonials[currentIndex.value];
|
|
@@ -118,6 +159,7 @@ section.testimonials-section {
|
|
|
118
159
|
margin-block-start: 6rem;
|
|
119
160
|
margin-block-end: 6rem;
|
|
120
161
|
box-sizing: border-box;
|
|
162
|
+
background-color: var(--surface-container-lowest, #ffffff);
|
|
121
163
|
}
|
|
122
164
|
|
|
123
165
|
@media (min-width: 1024px) {
|
|
@@ -168,6 +210,38 @@ section.testimonials-section {
|
|
|
168
210
|
gap: 24px;
|
|
169
211
|
}
|
|
170
212
|
|
|
213
|
+
.quotes-grid {
|
|
214
|
+
display: grid;
|
|
215
|
+
grid-template-columns: 1fr;
|
|
216
|
+
width: 100%;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.quote-slide {
|
|
220
|
+
grid-column: 1;
|
|
221
|
+
grid-row: 1;
|
|
222
|
+
align-self: start;
|
|
223
|
+
font-family: "Inter", sans-serif;
|
|
224
|
+
font-style: normal;
|
|
225
|
+
font-weight: 400;
|
|
226
|
+
font-size: 16px;
|
|
227
|
+
line-height: 24px;
|
|
228
|
+
letter-spacing: 0.5px;
|
|
229
|
+
color: #00060a;
|
|
230
|
+
min-height: 72px;
|
|
231
|
+
opacity: 0;
|
|
232
|
+
visibility: hidden;
|
|
233
|
+
transition:
|
|
234
|
+
opacity 0.25s ease,
|
|
235
|
+
visibility 0.25s ease;
|
|
236
|
+
pointer-events: none;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.quote-slide.active {
|
|
240
|
+
opacity: 1;
|
|
241
|
+
visibility: visible;
|
|
242
|
+
pointer-events: auto;
|
|
243
|
+
}
|
|
244
|
+
|
|
171
245
|
.quote-text {
|
|
172
246
|
font-family: "Inter", sans-serif;
|
|
173
247
|
font-style: normal;
|
|
@@ -195,14 +269,35 @@ section.testimonials-section {
|
|
|
195
269
|
}
|
|
196
270
|
|
|
197
271
|
.frame-468 {
|
|
272
|
+
display: grid;
|
|
273
|
+
grid-template-columns: 1fr;
|
|
274
|
+
align-items: flex-start;
|
|
275
|
+
flex-grow: 1;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.author-slide {
|
|
279
|
+
grid-column: 1;
|
|
280
|
+
grid-row: 1;
|
|
198
281
|
display: flex;
|
|
199
282
|
flex-direction: column;
|
|
200
283
|
align-items: flex-start;
|
|
201
284
|
gap: 8px;
|
|
202
|
-
|
|
285
|
+
opacity: 0;
|
|
286
|
+
visibility: hidden;
|
|
287
|
+
transition:
|
|
288
|
+
opacity 0.25s ease,
|
|
289
|
+
visibility 0.25s ease;
|
|
290
|
+
pointer-events: none;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.author-slide.active {
|
|
294
|
+
opacity: 1;
|
|
295
|
+
visibility: visible;
|
|
296
|
+
pointer-events: auto;
|
|
203
297
|
}
|
|
204
298
|
|
|
205
|
-
.author-name
|
|
299
|
+
.author-name,
|
|
300
|
+
.author-name-text {
|
|
206
301
|
font-family: "Inter", sans-serif;
|
|
207
302
|
font-style: normal;
|
|
208
303
|
font-weight: 700;
|
|
@@ -212,7 +307,8 @@ section.testimonials-section {
|
|
|
212
307
|
color: #00060a;
|
|
213
308
|
}
|
|
214
309
|
|
|
215
|
-
.author-role
|
|
310
|
+
.author-role,
|
|
311
|
+
.author-role-text {
|
|
216
312
|
font-family: "Inter", sans-serif;
|
|
217
313
|
font-style: normal;
|
|
218
314
|
font-weight: 400;
|
|
@@ -222,6 +318,13 @@ section.testimonials-section {
|
|
|
222
318
|
color: #00060a;
|
|
223
319
|
}
|
|
224
320
|
|
|
321
|
+
@media (prefers-reduced-motion: reduce) {
|
|
322
|
+
.quote-slide,
|
|
323
|
+
.author-slide {
|
|
324
|
+
transition: none;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
225
328
|
.sliders {
|
|
226
329
|
display: flex;
|
|
227
330
|
flex-direction: row;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { h } from "vue";
|
|
2
|
+
import TextSection from "./TextSection.vue";
|
|
3
|
+
|
|
4
|
+
describe("<TextSection />", () => {
|
|
5
|
+
it("renders title, tagline and button", () => {
|
|
6
|
+
cy.mount(TextSection, {
|
|
7
|
+
props: {
|
|
8
|
+
title: "Test Section",
|
|
9
|
+
tagline: "Test Tagline",
|
|
10
|
+
buttonText: "Click Me",
|
|
11
|
+
buttonLink: "https://example.com",
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
cy.contains("Test Section").should("exist");
|
|
16
|
+
cy.contains("Test Tagline").should("exist");
|
|
17
|
+
cy.contains("a.button", "Click Me")
|
|
18
|
+
.should("have.attr", "href", "https://example.com")
|
|
19
|
+
.and("have.attr", "target", "_blank");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("renders slot content and image", () => {
|
|
23
|
+
cy.mount(TextSection, {
|
|
24
|
+
props: {
|
|
25
|
+
image: "/test.jpg",
|
|
26
|
+
imageAlt: "Test Image",
|
|
27
|
+
},
|
|
28
|
+
slots: {
|
|
29
|
+
default: "<p>This is paragraph text.</p>",
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
cy.contains("This is paragraph text.").should("exist");
|
|
34
|
+
cy.get("img.section-image")
|
|
35
|
+
.should("have.attr", "src", "/test.jpg")
|
|
36
|
+
.and("have.attr", "alt", "Test Image");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("applies reverse class when reverse prop is true", () => {
|
|
40
|
+
cy.mount(TextSection, {
|
|
41
|
+
props: {
|
|
42
|
+
image: "/test.jpg",
|
|
43
|
+
reverse: true,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
cy.get(".text-section").should("have.class", "is-reverse");
|
|
48
|
+
cy.get(".text-section-grid").should("have.class", "grid-reverse");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("applies single column styling when no image is provided", () => {
|
|
52
|
+
cy.mount(TextSection, {
|
|
53
|
+
slots: {
|
|
54
|
+
default: "<p>Single column text</p>",
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
cy.get(".text-section").should("have.class", "no-image");
|
|
59
|
+
cy.get(".text-section-grid").should("have.class", "grid-single");
|
|
60
|
+
cy.get(".media-column").should("not.exist");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("applies extra padding-block-end to a single section", () => {
|
|
64
|
+
cy.mount(TextSection, {
|
|
65
|
+
props: {
|
|
66
|
+
title: "Single Section",
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
cy.get(".text-section").should("have.css", "padding-bottom", "80px");
|
|
71
|
+
cy.get(".text-section").should("have.css", "padding-top", "56px");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("applies extra padding-block-end only to the last section when multiple are consecutive", () => {
|
|
75
|
+
const TestWrapper = {
|
|
76
|
+
render() {
|
|
77
|
+
return h("div", [
|
|
78
|
+
h(TextSection, { title: "Section 1" }),
|
|
79
|
+
h(TextSection, { title: "Section 2" }),
|
|
80
|
+
h(TextSection, { title: "Section 3" }),
|
|
81
|
+
]);
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
cy.mount(TestWrapper);
|
|
86
|
+
|
|
87
|
+
// First section: standard padding-bottom 56px, normal padding-top 56px
|
|
88
|
+
cy.get(".text-section").eq(0).should("have.css", "padding-bottom", "56px");
|
|
89
|
+
cy.get(".text-section").eq(0).should("have.css", "padding-top", "56px");
|
|
90
|
+
|
|
91
|
+
// Second section: standard padding-bottom 56px, tightened padding-top 16px
|
|
92
|
+
cy.get(".text-section").eq(1).should("have.css", "padding-bottom", "56px");
|
|
93
|
+
cy.get(".text-section").eq(1).should("have.css", "padding-top", "16px");
|
|
94
|
+
|
|
95
|
+
// Third (last) section: extra padding-bottom 80px, tightened padding-top 16px
|
|
96
|
+
cy.get(".text-section").eq(2).should("have.css", "padding-bottom", "80px");
|
|
97
|
+
cy.get(".text-section").eq(2).should("have.css", "padding-top", "16px");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("handles compact sections with appropriate padding-block-end", () => {
|
|
101
|
+
const TestWrapper = {
|
|
102
|
+
render() {
|
|
103
|
+
return h("div", [
|
|
104
|
+
h(TextSection, { title: "Compact 1", compact: true }),
|
|
105
|
+
h(TextSection, { title: "Compact 2", compact: true }),
|
|
106
|
+
]);
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
cy.mount(TestWrapper);
|
|
111
|
+
|
|
112
|
+
cy.get(".text-section").eq(0).should("have.css", "padding-bottom", "28px");
|
|
113
|
+
cy.get(".text-section").eq(1).should("have.css", "padding-bottom", "40px");
|
|
114
|
+
});
|
|
115
|
+
});
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<section
|
|
3
|
+
class="text-section full-width"
|
|
4
|
+
:class="{
|
|
5
|
+
'is-reverse': reverse,
|
|
6
|
+
'no-image': !image && !$slots.media,
|
|
7
|
+
'is-compact': compact,
|
|
8
|
+
}"
|
|
9
|
+
>
|
|
10
|
+
<div class="holder">
|
|
11
|
+
<div
|
|
12
|
+
class="text-section-grid"
|
|
13
|
+
:class="{
|
|
14
|
+
'grid-reverse': reverse,
|
|
15
|
+
'grid-single': !image && !$slots.media,
|
|
16
|
+
}"
|
|
17
|
+
>
|
|
18
|
+
<!-- Text Column -->
|
|
19
|
+
<div class="text-column">
|
|
20
|
+
<span v-if="tagline" class="tagline">{{ tagline }}</span>
|
|
21
|
+
<h2 v-if="title" class="section-title">{{ title }}</h2>
|
|
22
|
+
<div class="section-content">
|
|
23
|
+
<slot></slot>
|
|
24
|
+
</div>
|
|
25
|
+
<!-- Prop-based button -->
|
|
26
|
+
<div v-if="buttonText && buttonLink" class="section-actions">
|
|
27
|
+
<a
|
|
28
|
+
:href="resolvedButtonLink"
|
|
29
|
+
:target="resolvedButtonTarget"
|
|
30
|
+
class="button"
|
|
31
|
+
@click="
|
|
32
|
+
trackEvent([
|
|
33
|
+
'TextSection',
|
|
34
|
+
'Click',
|
|
35
|
+
title || buttonText,
|
|
36
|
+
buttonText,
|
|
37
|
+
])
|
|
38
|
+
"
|
|
39
|
+
>
|
|
40
|
+
<span>{{ buttonText }}</span>
|
|
41
|
+
<i v-if="buttonIcon" :class="buttonIcon" class="btn-icon"></i>
|
|
42
|
+
</a>
|
|
43
|
+
</div>
|
|
44
|
+
<slot name="actions"></slot>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<!-- Media Column -->
|
|
48
|
+
<div v-if="image || $slots.media" class="media-column">
|
|
49
|
+
<slot name="media">
|
|
50
|
+
<img
|
|
51
|
+
v-if="image"
|
|
52
|
+
:src="resolvedImage"
|
|
53
|
+
:alt="imageAlt || title || ''"
|
|
54
|
+
class="section-image"
|
|
55
|
+
loading="lazy"
|
|
56
|
+
/>
|
|
57
|
+
</slot>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</section>
|
|
62
|
+
</template>
|
|
63
|
+
|
|
64
|
+
<script setup>
|
|
65
|
+
import { withBase } from "vitepress";
|
|
66
|
+
import { computed } from "vue";
|
|
67
|
+
import { trackEvent } from "../helpers.js";
|
|
68
|
+
|
|
69
|
+
const props = defineProps({
|
|
70
|
+
title: {
|
|
71
|
+
type: String,
|
|
72
|
+
default: "",
|
|
73
|
+
},
|
|
74
|
+
tagline: {
|
|
75
|
+
type: String,
|
|
76
|
+
default: "",
|
|
77
|
+
},
|
|
78
|
+
image: {
|
|
79
|
+
type: String,
|
|
80
|
+
default: "",
|
|
81
|
+
},
|
|
82
|
+
imageAlt: {
|
|
83
|
+
type: String,
|
|
84
|
+
default: "",
|
|
85
|
+
},
|
|
86
|
+
reverse: {
|
|
87
|
+
type: Boolean,
|
|
88
|
+
default: false,
|
|
89
|
+
},
|
|
90
|
+
buttonText: {
|
|
91
|
+
type: String,
|
|
92
|
+
default: "",
|
|
93
|
+
},
|
|
94
|
+
buttonLink: {
|
|
95
|
+
type: String,
|
|
96
|
+
default: "",
|
|
97
|
+
},
|
|
98
|
+
buttonTarget: {
|
|
99
|
+
type: String,
|
|
100
|
+
default: "",
|
|
101
|
+
},
|
|
102
|
+
buttonIcon: {
|
|
103
|
+
type: String,
|
|
104
|
+
default: "",
|
|
105
|
+
},
|
|
106
|
+
compact: {
|
|
107
|
+
type: Boolean,
|
|
108
|
+
default: false,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
function resolveUrl(url) {
|
|
113
|
+
if (!url) return "";
|
|
114
|
+
if (
|
|
115
|
+
url.startsWith("http://") ||
|
|
116
|
+
url.startsWith("https://") ||
|
|
117
|
+
url.startsWith("//") ||
|
|
118
|
+
url.startsWith("#")
|
|
119
|
+
) {
|
|
120
|
+
return url;
|
|
121
|
+
}
|
|
122
|
+
return withBase(url);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const resolvedImage = computed(() => resolveUrl(props.image));
|
|
126
|
+
const resolvedButtonLink = computed(() => resolveUrl(props.buttonLink));
|
|
127
|
+
const resolvedButtonTarget = computed(() => {
|
|
128
|
+
if (props.buttonTarget) return props.buttonTarget;
|
|
129
|
+
if (
|
|
130
|
+
props.buttonLink &&
|
|
131
|
+
(props.buttonLink.startsWith("http://") ||
|
|
132
|
+
props.buttonLink.startsWith("https://") ||
|
|
133
|
+
props.buttonLink.startsWith("//"))
|
|
134
|
+
) {
|
|
135
|
+
return "_blank";
|
|
136
|
+
}
|
|
137
|
+
return "_self";
|
|
138
|
+
});
|
|
139
|
+
</script>
|
|
140
|
+
|
|
141
|
+
<style scoped>
|
|
142
|
+
.full-width {
|
|
143
|
+
width: 100svw;
|
|
144
|
+
position: relative;
|
|
145
|
+
left: 50%;
|
|
146
|
+
right: 50%;
|
|
147
|
+
margin-left: -50svw;
|
|
148
|
+
margin-right: -50svw;
|
|
149
|
+
max-inline-size: unset !important;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.text-section {
|
|
153
|
+
padding-block-start: 56px;
|
|
154
|
+
padding-block-end: 80px;
|
|
155
|
+
display: flex;
|
|
156
|
+
justify-content: center;
|
|
157
|
+
background: transparent;
|
|
158
|
+
color: var(--on-surface, #020617);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.text-section.is-compact {
|
|
162
|
+
padding-block-start: 28px;
|
|
163
|
+
padding-block-end: 40px;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* When text-sections are placed consecutively, tighten spacing and only apply extra end padding to the last one */
|
|
167
|
+
:deep(.text-section:has(+ .text-section)),
|
|
168
|
+
.text-section:has(+ .text-section) {
|
|
169
|
+
padding-block-end: 56px;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
:deep(.text-section.is-compact:has(+ .text-section)),
|
|
173
|
+
.text-section.is-compact:has(+ .text-section) {
|
|
174
|
+
padding-block-end: 28px;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
:deep(.text-section + .text-section),
|
|
178
|
+
.text-section + .text-section {
|
|
179
|
+
padding-block-start: 16px;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.text-section .holder {
|
|
183
|
+
width: 100%;
|
|
184
|
+
max-width: 1200px;
|
|
185
|
+
margin-left: auto;
|
|
186
|
+
margin-right: auto;
|
|
187
|
+
padding-inline: 24px;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.text-section-grid {
|
|
191
|
+
display: grid;
|
|
192
|
+
grid-template-columns: 1fr;
|
|
193
|
+
gap: 2rem;
|
|
194
|
+
align-items: center;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* On mobile (< 1024px), the media/image column is always at the start before the text */
|
|
198
|
+
.text-section-grid .media-column {
|
|
199
|
+
order: 1;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.text-section-grid .text-column {
|
|
203
|
+
order: 2;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
@media (min-width: 1024px) {
|
|
207
|
+
.text-section-grid {
|
|
208
|
+
grid-template-columns: 1fr 1fr;
|
|
209
|
+
gap: 4rem;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* Default on desktop: Text on left (1), Media on right (2) */
|
|
213
|
+
.text-section-grid .text-column {
|
|
214
|
+
order: 1;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.text-section-grid .media-column {
|
|
218
|
+
order: 2;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* Reverse on desktop: Media on left (1), Text on right (2) */
|
|
222
|
+
.text-section-grid.grid-reverse .text-column {
|
|
223
|
+
order: 2;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.text-section-grid.grid-reverse .media-column {
|
|
227
|
+
order: 1;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.text-section-grid.grid-single {
|
|
231
|
+
grid-template-columns: 1fr;
|
|
232
|
+
max-width: 680px;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.text-column {
|
|
237
|
+
display: flex;
|
|
238
|
+
flex-direction: column;
|
|
239
|
+
width: 100%;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.tagline {
|
|
243
|
+
display: inline-block;
|
|
244
|
+
font-size: 0.875rem;
|
|
245
|
+
font-weight: 600;
|
|
246
|
+
text-transform: uppercase;
|
|
247
|
+
letter-spacing: 0.05em;
|
|
248
|
+
color: var(--primary, #004170);
|
|
249
|
+
margin-bottom: 0.5rem;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.section-title {
|
|
253
|
+
font-family: inherit;
|
|
254
|
+
font-weight: 700;
|
|
255
|
+
font-size: 2rem;
|
|
256
|
+
line-height: 1.25;
|
|
257
|
+
color: #020617;
|
|
258
|
+
margin-top: 0;
|
|
259
|
+
margin-bottom: 1.25rem;
|
|
260
|
+
border-top: none;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* Markdown Content Styling */
|
|
264
|
+
.section-content :deep(h2) {
|
|
265
|
+
font-family: inherit;
|
|
266
|
+
font-weight: 700;
|
|
267
|
+
font-size: 2rem;
|
|
268
|
+
line-height: 1.25;
|
|
269
|
+
color: #020617;
|
|
270
|
+
margin-top: 0;
|
|
271
|
+
margin-bottom: 1.25rem;
|
|
272
|
+
border-top: none;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.section-content :deep(h3) {
|
|
276
|
+
font-family: inherit;
|
|
277
|
+
font-weight: 700;
|
|
278
|
+
font-size: 1.5rem;
|
|
279
|
+
line-height: 1.35;
|
|
280
|
+
color: #020617;
|
|
281
|
+
margin-top: 1.5rem;
|
|
282
|
+
margin-bottom: 0.75rem;
|
|
283
|
+
border-top: none;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.section-content :deep(h4),
|
|
287
|
+
.section-content :deep(h5) {
|
|
288
|
+
font-family: inherit;
|
|
289
|
+
font-weight: 600;
|
|
290
|
+
font-size: 1.125rem;
|
|
291
|
+
line-height: 1.4;
|
|
292
|
+
color: #020617;
|
|
293
|
+
margin-top: 1.25rem;
|
|
294
|
+
margin-bottom: 0.5rem;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.section-content :deep(p) {
|
|
298
|
+
font-size: 1rem;
|
|
299
|
+
line-height: 1.65;
|
|
300
|
+
color: #334155;
|
|
301
|
+
margin-top: 0;
|
|
302
|
+
margin-bottom: 1rem;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.section-content :deep(ul) {
|
|
306
|
+
margin-top: 0.75rem;
|
|
307
|
+
margin-bottom: 1.25rem;
|
|
308
|
+
padding-left: 1.25rem;
|
|
309
|
+
list-style-type: disc;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.section-content :deep(li) {
|
|
313
|
+
font-size: 0.95rem;
|
|
314
|
+
line-height: 1.6;
|
|
315
|
+
color: #334155;
|
|
316
|
+
margin-bottom: 0.5rem;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.section-content :deep(li strong) {
|
|
320
|
+
color: #020617;
|
|
321
|
+
font-weight: 700;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.section-content :deep(a:not(.button)) {
|
|
325
|
+
color: #0284c7;
|
|
326
|
+
text-decoration: none;
|
|
327
|
+
font-weight: 500;
|
|
328
|
+
transition: color 0.15s ease-in-out;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.section-content :deep(a:not(.button):hover) {
|
|
332
|
+
text-decoration: underline;
|
|
333
|
+
color: #0369a1;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/* Pill Button Styling - for both prop and markdown links with .button */
|
|
337
|
+
.section-actions {
|
|
338
|
+
margin-top: 0.75rem;
|
|
339
|
+
margin-bottom: 0.5rem;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.button,
|
|
343
|
+
.section-content :deep(a.button),
|
|
344
|
+
.section-content :deep(.button) {
|
|
345
|
+
display: inline-flex;
|
|
346
|
+
align-items: center;
|
|
347
|
+
justify-content: center;
|
|
348
|
+
gap: 0.5rem;
|
|
349
|
+
padding: 0.5rem 1.5rem;
|
|
350
|
+
border-radius: 9999px;
|
|
351
|
+
border: 1px solid #020617;
|
|
352
|
+
background-color: #ffffff;
|
|
353
|
+
color: #020617 !important;
|
|
354
|
+
font-weight: 600;
|
|
355
|
+
font-size: 0.9375rem;
|
|
356
|
+
line-height: 1.5;
|
|
357
|
+
text-decoration: none !important;
|
|
358
|
+
transition: all 0.2s ease-in-out;
|
|
359
|
+
cursor: pointer;
|
|
360
|
+
width: fit-content;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.button:hover,
|
|
364
|
+
.section-content :deep(a.button:hover),
|
|
365
|
+
.section-content :deep(.button:hover) {
|
|
366
|
+
background-color: #f1f5f9;
|
|
367
|
+
border-color: #020617;
|
|
368
|
+
color: #020617 !important;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.btn-icon {
|
|
372
|
+
font-size: 1.1em;
|
|
373
|
+
display: inline-flex;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/* Media Column & Image Styling */
|
|
377
|
+
.media-column {
|
|
378
|
+
display: flex;
|
|
379
|
+
justify-content: center;
|
|
380
|
+
align-items: center;
|
|
381
|
+
width: 100%;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.section-image {
|
|
385
|
+
width: 100%;
|
|
386
|
+
max-width: 100%;
|
|
387
|
+
height: auto;
|
|
388
|
+
border-radius: 16px;
|
|
389
|
+
border: 1px solid rgba(15, 23, 42, 0.08);
|
|
390
|
+
box-shadow:
|
|
391
|
+
0 4px 16px -2px rgba(15, 23, 42, 0.06),
|
|
392
|
+
0 2px 6px -2px rgba(15, 23, 42, 0.04);
|
|
393
|
+
background-color: #ffffff;
|
|
394
|
+
display: block;
|
|
395
|
+
}
|
|
396
|
+
</style>
|