@hyvor/design 2.1.9 → 2.1.11

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 (52) hide show
  1. package/dist/cloud/HyvorBar/BarSupport.svelte +1 -1
  2. package/dist/components/ColorPicker/ColorPicker.svelte +68 -9
  3. package/dist/components/ColorPicker/ColorPicker.svelte.d.ts +3 -0
  4. package/dist/components/Confetti/confetti.d.ts +7 -0
  5. package/dist/components/Confetti/confetti.js +102 -0
  6. package/dist/components/NavLink/NavLinkGroup.svelte +1 -0
  7. package/dist/index.css +4 -0
  8. package/dist/marketing/AllFeaturesAccordion/AllFeaturesAccordion.svelte +251 -0
  9. package/dist/marketing/AllFeaturesAccordion/AllFeaturesAccordion.svelte.d.ts +23 -0
  10. package/dist/marketing/AllFeaturesAccordion/AllFeaturesAccordionFeature.svelte +70 -0
  11. package/dist/marketing/AllFeaturesAccordion/AllFeaturesAccordionFeature.svelte.d.ts +10 -0
  12. package/dist/marketing/FAQ/FAQ.svelte +227 -0
  13. package/dist/marketing/FAQ/FAQ.svelte.d.ts +13 -0
  14. package/dist/marketing/FeatureSplit/FeatureSplit.svelte +325 -0
  15. package/dist/marketing/FeatureSplit/FeatureSplit.svelte.d.ts +24 -0
  16. package/dist/marketing/Footer/Footer.svelte +395 -98
  17. package/dist/marketing/Footer/Footer.svelte.d.ts +18 -2
  18. package/dist/marketing/Footer/FooterLinkList.svelte +10 -3
  19. package/dist/marketing/FullTrialSignup/FullTrialSignup.svelte +164 -0
  20. package/dist/marketing/FullTrialSignup/FullTrialSignup.svelte.d.ts +18 -0
  21. package/dist/marketing/Header/Header.svelte +72 -28
  22. package/dist/marketing/Header/Header.svelte.d.ts +5 -2
  23. package/dist/marketing/Header/HeaderLanguageToggle.svelte +119 -0
  24. package/dist/marketing/Header/HeaderLanguageToggle.svelte.d.ts +14 -0
  25. package/dist/marketing/Header/HeaderNavLink.svelte +149 -0
  26. package/dist/marketing/Header/HeaderNavLink.svelte.d.ts +18 -0
  27. package/dist/marketing/Header/language.d.ts +16 -0
  28. package/dist/marketing/Header/language.js +28 -0
  29. package/dist/marketing/Hero/Hero.svelte +349 -0
  30. package/dist/marketing/Hero/Hero.svelte.d.ts +16 -0
  31. package/dist/marketing/LogoStrip/LogoStrip.svelte +199 -0
  32. package/dist/marketing/LogoStrip/LogoStrip.svelte.d.ts +19 -0
  33. package/dist/marketing/Seal/CcpaSeal.svelte +29 -0
  34. package/dist/marketing/Seal/CcpaSeal.svelte.d.ts +6 -0
  35. package/dist/marketing/Seal/GdprSeal.svelte +60 -0
  36. package/dist/marketing/Seal/GdprSeal.svelte.d.ts +6 -0
  37. package/dist/marketing/Seal/IsoSeal.svelte +44 -0
  38. package/dist/marketing/Seal/IsoSeal.svelte.d.ts +6 -0
  39. package/dist/marketing/Seal/Seal.svelte +62 -0
  40. package/dist/marketing/Seal/Seal.svelte.d.ts +8 -0
  41. package/dist/marketing/Seal/SsoSeal.svelte +31 -0
  42. package/dist/marketing/Seal/SsoSeal.svelte.d.ts +6 -0
  43. package/dist/marketing/Seal/ccpa.svg +131 -0
  44. package/dist/marketing/SpotlightSplit/SpotlightSplit.svelte +256 -0
  45. package/dist/marketing/SpotlightSplit/SpotlightSplit.svelte.d.ts +19 -0
  46. package/dist/marketing/Testimonials/Testimonials.svelte +441 -0
  47. package/dist/marketing/Testimonials/Testimonials.svelte.d.ts +24 -0
  48. package/dist/marketing/index.d.ts +20 -2
  49. package/dist/marketing/index.js +26 -2
  50. package/dist/marketing/social.d.ts +1 -1
  51. package/dist/marketing/social.js +1 -1
  52. package/package.json +1 -1
@@ -0,0 +1,256 @@
1
+ <script lang="ts">
2
+ import Button from '../../components/Button/Button.svelte';
3
+ import IconBoxArrowUpRight from '@hyvor/icons/IconBoxArrowUpRight';
4
+ import type { Snippet } from 'svelte';
5
+
6
+ type ButtonConfig = { href: string; label: string; external?: boolean };
7
+
8
+ interface Props {
9
+ title: string | Snippet;
10
+ description?: string;
11
+ stats?: string[];
12
+ button?: ButtonConfig | ButtonConfig[] | null;
13
+ content: Snippet;
14
+ reverse?: boolean;
15
+ background?: string;
16
+ highlightColor?: string;
17
+ }
18
+
19
+ let {
20
+ title,
21
+ description = '',
22
+ stats = [],
23
+ button = null,
24
+ content,
25
+ reverse = false,
26
+ background = 'var(--accent)',
27
+ highlightColor = 'rgba(255, 238, 217, 0.9)'
28
+ }: Props = $props();
29
+
30
+ const buttons = $derived(button ? (Array.isArray(button) ? button : [button]) : []);
31
+
32
+ let mouseX = $state(0);
33
+ let mouseY = $state(0);
34
+ let hovering = $state(false);
35
+
36
+ function handlePointerMove(event: PointerEvent) {
37
+ const bounds = (event.currentTarget as HTMLElement).getBoundingClientRect();
38
+ mouseX = event.clientX - bounds.left;
39
+ mouseY = event.clientY - bounds.top;
40
+ }
41
+ </script>
42
+
43
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
44
+ <section
45
+ class="hds-spotlight-split"
46
+ onpointermove={handlePointerMove}
47
+ onpointerenter={() => (hovering = true)}
48
+ onpointerleave={() => (hovering = false)}
49
+ style:--mx="{mouseX}px"
50
+ style:--my="{mouseY}px"
51
+ style:--sps-bg={background}
52
+ style:--sps-highlight={highlightColor}
53
+ >
54
+ <svg class="bg-pattern" width="100%" height="100%" aria-hidden="true">
55
+ <defs>
56
+ <pattern id="spotlight-split-grid" width="56" height="56" patternUnits="userSpaceOnUse">
57
+ <path d="M 56 0 L 0 0 0 56" fill="none" stroke="white" stroke-width="1" />
58
+ </pattern>
59
+ <radialGradient id="spotlight-split-fade" cx="50%" cy="40%" r="75%">
60
+ <stop offset="0%" stop-color="white" stop-opacity="1" />
61
+ <stop offset="100%" stop-color="white" stop-opacity="0" />
62
+ </radialGradient>
63
+ <mask id="spotlight-split-mask">
64
+ <rect width="100%" height="100%" fill="url(#spotlight-split-fade)" />
65
+ </mask>
66
+ </defs>
67
+ <rect
68
+ width="100%"
69
+ height="100%"
70
+ fill="url(#spotlight-split-grid)"
71
+ mask="url(#spotlight-split-mask)"
72
+ />
73
+ </svg>
74
+
75
+ <div class="grid-highlight" class:visible={hovering} aria-hidden="true"></div>
76
+
77
+ <div class="hds-container inner" class:reverse>
78
+ <div class="text-side">
79
+ <h2>
80
+ {#if typeof title === 'string'}
81
+ {@html title}
82
+ {:else}
83
+ {@render title()}
84
+ {/if}
85
+ </h2>
86
+
87
+ {#if description}
88
+ <p class="description">{description}</p>
89
+ {/if}
90
+
91
+ {#if stats.length}
92
+ <div class="stat-row">
93
+ {#each stats as stat}
94
+ <span class="stat">{stat}</span>
95
+ {/each}
96
+ </div>
97
+ {/if}
98
+
99
+ {#if buttons.length}
100
+ <div class="buttons">
101
+ {#each buttons as b}
102
+ <Button
103
+ as="a"
104
+ href={b.href}
105
+ target={b.external ? '_blank' : undefined}
106
+ rel={b.external ? 'noopener' : undefined}
107
+ variant="outline"
108
+ color="input"
109
+ >
110
+ {b.label}
111
+ {#if b.external}
112
+ {#snippet end()}<IconBoxArrowUpRight size={11} />{/snippet}
113
+ {/if}
114
+ </Button>
115
+ {/each}
116
+ </div>
117
+ {/if}
118
+ </div>
119
+
120
+ <div class="content-side">
121
+ {@render content()}
122
+ </div>
123
+ </div>
124
+ </section>
125
+
126
+ <style>
127
+ .hds-spotlight-split {
128
+ position: relative;
129
+ background: var(--sps-bg);
130
+ padding: 96px 0;
131
+ overflow: hidden;
132
+ }
133
+
134
+ .bg-pattern {
135
+ position: absolute;
136
+ inset: 0;
137
+ opacity: 0.08;
138
+ pointer-events: none;
139
+ }
140
+
141
+ .grid-highlight {
142
+ position: absolute;
143
+ inset: 0;
144
+ pointer-events: none;
145
+ background-image:
146
+ linear-gradient(to right, var(--sps-highlight) 1px, transparent 1px),
147
+ linear-gradient(to bottom, var(--sps-highlight) 1px, transparent 1px);
148
+ background-size: 56px 56px;
149
+ -webkit-mask-image: radial-gradient(
150
+ 180px circle at var(--mx, 50%) var(--my, 50%),
151
+ black 0%,
152
+ black 35%,
153
+ transparent 75%
154
+ );
155
+ mask-image: radial-gradient(
156
+ 180px circle at var(--mx, 50%) var(--my, 50%),
157
+ black 0%,
158
+ black 35%,
159
+ transparent 75%
160
+ );
161
+ opacity: 0;
162
+ transition: opacity 0.35s ease;
163
+ }
164
+
165
+ .grid-highlight.visible {
166
+ opacity: 0.2;
167
+ }
168
+
169
+ .inner {
170
+ position: relative;
171
+ display: flex;
172
+ align-items: center;
173
+ justify-content: space-between;
174
+ gap: 48px;
175
+ }
176
+
177
+ .inner.reverse {
178
+ flex-direction: row-reverse;
179
+ }
180
+
181
+ .text-side {
182
+ flex: 1;
183
+ min-width: 0;
184
+ }
185
+
186
+ h2 {
187
+ font-size: clamp(35px, 4vw, 50px);
188
+ font-weight: 800;
189
+ letter-spacing: -0.02em;
190
+ line-height: 1.1;
191
+ color: #fff;
192
+ margin: 0 0 18px;
193
+ font-family: var(--font-serif);
194
+ }
195
+
196
+ .description {
197
+ font-size: 16px;
198
+ line-height: 1.7;
199
+ color: rgba(255, 255, 255, 0.7);
200
+ max-width: 460px;
201
+ margin: 0 0 28px;
202
+ }
203
+
204
+ .stat-row {
205
+ display: flex;
206
+ flex-wrap: wrap;
207
+ gap: 10px 20px;
208
+ margin-bottom: 28px;
209
+ }
210
+
211
+ .stat {
212
+ font-size: 14px;
213
+ font-weight: 600;
214
+ color: rgba(255, 255, 255, 0.85);
215
+ }
216
+
217
+ .buttons {
218
+ display: flex;
219
+ flex-wrap: wrap;
220
+ gap: 12px;
221
+ }
222
+
223
+ .buttons :global(.button) {
224
+ border-color: rgba(255, 255, 255, 0.3) !important;
225
+ color: rgba(255, 255, 255, 0.85) !important;
226
+ background: transparent !important;
227
+ text-decoration: none !important;
228
+ }
229
+
230
+ .buttons :global(.button):hover {
231
+ border-color: rgba(255, 255, 255, 0.55) !important;
232
+ color: #fff !important;
233
+ }
234
+
235
+ .content-side {
236
+ flex-shrink: 0;
237
+ }
238
+
239
+ @media (max-width: 768px) {
240
+ .inner,
241
+ .inner.reverse {
242
+ flex-direction: column;
243
+ text-align: center;
244
+ gap: 36px;
245
+ }
246
+
247
+ .description {
248
+ max-width: 100%;
249
+ }
250
+
251
+ .stat-row,
252
+ .buttons {
253
+ justify-content: center;
254
+ }
255
+ }
256
+ </style>
@@ -0,0 +1,19 @@
1
+ import type { Snippet } from 'svelte';
2
+ type ButtonConfig = {
3
+ href: string;
4
+ label: string;
5
+ external?: boolean;
6
+ };
7
+ interface Props {
8
+ title: string | Snippet;
9
+ description?: string;
10
+ stats?: string[];
11
+ button?: ButtonConfig | ButtonConfig[] | null;
12
+ content: Snippet;
13
+ reverse?: boolean;
14
+ background?: string;
15
+ highlightColor?: string;
16
+ }
17
+ declare const SpotlightSplit: import("svelte").Component<Props, {}, "">;
18
+ type SpotlightSplit = ReturnType<typeof SpotlightSplit>;
19
+ export default SpotlightSplit;
@@ -0,0 +1,441 @@
1
+ <script lang="ts">
2
+ interface TextReview {
3
+ type: 'text';
4
+ name: string;
5
+ role: string;
6
+ quote: string;
7
+ }
8
+
9
+ interface VideoReview {
10
+ type: 'video';
11
+ name: string;
12
+ role: string;
13
+ videoUrl?: string;
14
+ posterUrl?: string;
15
+ summary?: string;
16
+ }
17
+
18
+ type Review = TextReview | VideoReview;
19
+
20
+ interface Props {
21
+ label?: string;
22
+ title?: string;
23
+ reviews: Review[];
24
+ handwrittenNames?: boolean;
25
+ }
26
+
27
+ let {
28
+ label = 'Testimonials',
29
+ title = 'Loved by our customers.',
30
+ reviews,
31
+ handwrittenNames = true
32
+ }: Props = $props();
33
+
34
+ function identicon(seed: string) {
35
+ let hash = 0;
36
+ for (let i = 0; i < seed.length; i++) {
37
+ hash = (hash * 31 + seed.charCodeAt(i)) | 0;
38
+ }
39
+ const hue = Math.abs(hash) % 360;
40
+ const cells: { x: number; y: number }[] = [];
41
+ for (let col = 0; col < 3; col++) {
42
+ for (let row = 0; row < 5; row++) {
43
+ const bit = (hash >> (col * 5 + row)) & 1;
44
+ if (!bit) continue;
45
+ cells.push({ x: col, y: row });
46
+ if (col < 2) cells.push({ x: 4 - col, y: row });
47
+ }
48
+ }
49
+ return {
50
+ bg: `hsl(${hue} 45% 92%)`,
51
+ fg: `hsl(${hue} 50% 40%)`,
52
+ cells
53
+ };
54
+ }
55
+
56
+ let scrollEl: HTMLDivElement | undefined = $state();
57
+ let dragging = $state(false);
58
+ let dragStartX = 0;
59
+ let dragStartScroll = 0;
60
+
61
+ let started: boolean[] = $state(reviews.map(() => false));
62
+ let playing: boolean[] = $state(reviews.map(() => false));
63
+ let videoEls: (HTMLVideoElement | undefined)[] = $state([]);
64
+
65
+ function play(i: number) {
66
+ started[i] = true;
67
+ videoEls[i]?.play().catch(() => {});
68
+ }
69
+
70
+ function autoplayVideo(node: HTMLVideoElement) {
71
+ node.play().catch(() => {});
72
+ }
73
+
74
+ function onVideoPause(e: Event, i: number) {
75
+ if ((e.currentTarget as HTMLVideoElement).seeking) return;
76
+ playing[i] = false;
77
+ }
78
+
79
+ function onPointerDown(e: PointerEvent) {
80
+ if (!scrollEl) return;
81
+ // let clicks and native <video> controls work
82
+ if ((e.target as HTMLElement).closest('button, video, a')) return;
83
+ dragging = true;
84
+ dragStartX = e.clientX;
85
+ dragStartScroll = scrollEl.scrollLeft;
86
+ scrollEl.setPointerCapture(e.pointerId);
87
+ }
88
+
89
+ function onPointerMove(e: PointerEvent) {
90
+ if (!dragging || !scrollEl) return;
91
+ scrollEl.scrollLeft = dragStartScroll - (e.clientX - dragStartX);
92
+ }
93
+
94
+ function onPointerUp() {
95
+ dragging = false;
96
+ }
97
+ </script>
98
+
99
+ <svelte:head>
100
+ {#if handwrittenNames}
101
+ <link href="https://fonts.bunny.net/css?family=caveat:600,700" rel="stylesheet" />
102
+ {/if}
103
+ </svelte:head>
104
+
105
+ <section class="hds-testimonials" class:handwritten={handwrittenNames}>
106
+ <div class="hds-container head">
107
+ <p class="label">{label}</p>
108
+ <h2>{@html title}</h2>
109
+ </div>
110
+
111
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
112
+ <div
113
+ class="scroll-row"
114
+ class:dragging
115
+ bind:this={scrollEl}
116
+ onpointerdown={onPointerDown}
117
+ onpointermove={onPointerMove}
118
+ onpointerup={onPointerUp}
119
+ onpointerleave={onPointerUp}
120
+ role="region"
121
+ aria-label={label}
122
+ >
123
+ {#each reviews as review, i}
124
+ {#if review.type === 'text'}
125
+ {@const av = identicon(review.name)}
126
+ <figure class="card text-card hds-box">
127
+ <svg class="avatar" viewBox="0 0 5 5" style="background: {av.bg}" aria-hidden="true">
128
+ {#each av.cells as cell}
129
+ <rect x={cell.x} y={cell.y} width="1" height="1" fill={av.fg} />
130
+ {/each}
131
+ </svg>
132
+ <blockquote>&ldquo;{review.quote}&rdquo;</blockquote>
133
+ <figcaption>
134
+ <span class="name">{review.name}</span>
135
+ <span class="role">{review.role}</span>
136
+ </figcaption>
137
+ </figure>
138
+ {:else}
139
+ <figure class="card video-card hds-box" class:playing={playing[i]}>
140
+ {#if started[i]}
141
+ <!-- svelte-ignore a11y_media_has_caption -->
142
+ <video
143
+ class="video"
144
+ src={review.videoUrl}
145
+ poster={review.posterUrl}
146
+ controls={playing[i]}
147
+ playsinline
148
+ bind:this={videoEls[i]}
149
+ use:autoplayVideo
150
+ onplay={() => (playing[i] = true)}
151
+ onpause={(e) => onVideoPause(e, i)}
152
+ onended={() => (playing[i] = false)}
153
+ ></video>
154
+ {:else}
155
+ <div class="poster">
156
+ <img src={review.posterUrl} alt="" />
157
+ </div>
158
+ {/if}
159
+
160
+ {#if !playing[i]}
161
+ <div class="poster-scrim"></div>
162
+
163
+ {#if !started[i] && review.summary}
164
+ <p class="video-summary">"{review.summary}"</p>
165
+ {/if}
166
+
167
+ <button
168
+ class="play-btn"
169
+ aria-label={started[i] ? 'Resume video testimonial' : 'Play video testimonial'}
170
+ onclick={() => play(i)}
171
+ >
172
+ <svg
173
+ width="20"
174
+ height="20"
175
+ viewBox="0 0 16 16"
176
+ fill="currentColor"
177
+ aria-hidden="true"
178
+ >
179
+ <path d="M5 3.5v9l8-4.5-8-4.5z" />
180
+ </svg>
181
+ </button>
182
+
183
+ <figcaption>
184
+ <span class="name">{review.name}</span>
185
+ <span class="role">{review.role}</span>
186
+ </figcaption>
187
+ {/if}
188
+ </figure>
189
+ {/if}
190
+ {/each}
191
+ </div>
192
+ </section>
193
+
194
+ <style>
195
+ .hds-testimonials {
196
+ background: var(--accent-lightest);
197
+ padding: 100px 0 96px;
198
+ overflow: hidden;
199
+ }
200
+
201
+ .head {
202
+ margin-bottom: 44px;
203
+ }
204
+
205
+ .label {
206
+ font-size: 14px;
207
+ font-weight: 600;
208
+ letter-spacing: 0.09em;
209
+ text-transform: uppercase;
210
+ color: var(--accent);
211
+ margin: 0 0 16px;
212
+ }
213
+
214
+ h2 {
215
+ font-size: clamp(30px, 4vw, 44px);
216
+ font-weight: 800;
217
+ letter-spacing: -0.02em;
218
+ line-height: 1.15;
219
+ color: var(--text);
220
+ margin: 0;
221
+ font-family: var(--font-serif);
222
+ }
223
+
224
+ .scroll-row {
225
+ --side-padding: max(15px, calc((100vw - 1000px) / 2));
226
+
227
+ display: flex;
228
+ align-items: stretch;
229
+ gap: 20px;
230
+ overflow-x: auto;
231
+ overflow-y: visible;
232
+ scroll-behavior: smooth;
233
+ cursor: grab;
234
+ padding: 32px var(--side-padding) 36px;
235
+ scrollbar-width: none;
236
+ -webkit-overflow-scrolling: touch;
237
+ touch-action: pan-y;
238
+ user-select: none;
239
+ -webkit-user-select: none;
240
+ }
241
+
242
+ .scroll-row.dragging {
243
+ cursor: grabbing;
244
+ scroll-behavior: auto;
245
+ }
246
+
247
+ .scroll-row::-webkit-scrollbar {
248
+ display: none;
249
+ }
250
+
251
+ .card {
252
+ position: relative;
253
+ flex: 0 0 auto;
254
+ width: 320px;
255
+ height: 440px;
256
+ margin: 0;
257
+ box-sizing: border-box;
258
+ transition:
259
+ width 0.35s cubic-bezier(0.16, 1, 0.3, 1),
260
+ height 0.35s cubic-bezier(0.16, 1, 0.3, 1);
261
+ }
262
+
263
+ .text-card {
264
+ padding: 28px;
265
+ display: flex;
266
+ flex-direction: column;
267
+ }
268
+
269
+ .avatar {
270
+ width: 46px;
271
+ height: 46px;
272
+ border-radius: 50%;
273
+ flex-shrink: 0;
274
+ display: block;
275
+ shape-rendering: crispEdges;
276
+ }
277
+
278
+ blockquote {
279
+ font-size: 16px;
280
+ line-height: 1.7;
281
+ color: var(--text);
282
+ margin: 20px 0 0;
283
+ flex: 1;
284
+ }
285
+
286
+ .text-card figcaption {
287
+ display: flex;
288
+ flex-direction: column;
289
+ gap: 0;
290
+ }
291
+
292
+ .name {
293
+ font-size: 16px;
294
+ font-weight: 600;
295
+ color: var(--text);
296
+ line-height: 1.2;
297
+ }
298
+
299
+ .handwritten .name {
300
+ font-family: 'Caveat', cursive;
301
+ font-size: 28px;
302
+ font-weight: 600;
303
+ }
304
+
305
+ .role {
306
+ font-size: 13px;
307
+ color: var(--text-light);
308
+ }
309
+
310
+ .video-card {
311
+ width: 280px;
312
+ overflow: hidden;
313
+ padding: 0;
314
+ }
315
+
316
+ .video-card.playing {
317
+ width: 340px;
318
+ height: 520px;
319
+ z-index: 1;
320
+ }
321
+
322
+ .poster {
323
+ position: absolute;
324
+ inset: 0;
325
+ }
326
+
327
+ .poster img {
328
+ width: 100%;
329
+ height: 100%;
330
+ object-fit: cover;
331
+ display: block;
332
+ }
333
+
334
+ .poster-scrim {
335
+ position: absolute;
336
+ inset: 0;
337
+ background: linear-gradient(160deg, rgba(20, 14, 12, 0.45), rgba(20, 14, 12, 0.65));
338
+ }
339
+
340
+ .video-summary {
341
+ position: absolute;
342
+ top: 24px;
343
+ left: 20px;
344
+ right: 20px;
345
+ margin: 0;
346
+ font-family: var(--font-serif);
347
+ font-size: 18px;
348
+ font-weight: 800;
349
+ line-height: 1.25;
350
+ letter-spacing: -0.01em;
351
+ color: #fff;
352
+ text-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
353
+ font-style: italic;
354
+ }
355
+
356
+ .play-btn {
357
+ position: absolute;
358
+ top: 50%;
359
+ left: 50%;
360
+ transform: translate(-50%, -50%);
361
+ width: 56px;
362
+ height: 56px;
363
+ border-radius: 50%;
364
+ background: rgba(255, 255, 255, 0.18);
365
+ backdrop-filter: blur(6px);
366
+ -webkit-backdrop-filter: blur(6px);
367
+ border: 1px solid rgba(255, 255, 255, 0.4);
368
+ color: #fff;
369
+ display: flex;
370
+ align-items: center;
371
+ justify-content: center;
372
+ cursor: pointer;
373
+ transition:
374
+ transform 0.2s,
375
+ background 0.2s;
376
+ }
377
+
378
+ .play-btn:hover {
379
+ transform: translate(-50%, -50%) scale(1.08);
380
+ background: rgba(255, 255, 255, 0.28);
381
+ }
382
+
383
+ .play-btn svg {
384
+ margin-left: 3px;
385
+ }
386
+
387
+ .video {
388
+ display: block;
389
+ width: 100%;
390
+ height: 100%;
391
+ object-fit: cover;
392
+ background: #000;
393
+ }
394
+
395
+ .video-card figcaption {
396
+ position: absolute;
397
+ left: 0;
398
+ right: 0;
399
+ bottom: 0;
400
+ padding: 40px 20px 20px;
401
+ background: linear-gradient(to top, rgba(0, 0, 0, 0.75), transparent);
402
+ display: flex;
403
+ flex-direction: column;
404
+ }
405
+
406
+ .video-card .name {
407
+ color: #fff;
408
+ }
409
+
410
+ .video-card .role {
411
+ color: rgba(255, 255, 255, 0.75);
412
+ }
413
+
414
+ @media (max-width: 600px) {
415
+ .scroll-row {
416
+ --side-padding: max(20px, calc((100vw - 1000px) / 2));
417
+ padding: 32px var(--side-padding) 36px;
418
+ }
419
+ }
420
+
421
+ @media (max-width: 768px) {
422
+ .head {
423
+ text-align: center;
424
+ margin-bottom: 32px;
425
+ }
426
+
427
+ .card {
428
+ width: 280px;
429
+ height: 400px;
430
+ }
431
+
432
+ .video-card {
433
+ width: 240px;
434
+ }
435
+
436
+ .video-card.playing {
437
+ width: 280px;
438
+ height: 460px;
439
+ }
440
+ }
441
+ </style>