ferst-core 0.7.0 → 0.7.2
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/components/recipes/ContactCard.astro +172 -170
- package/components/recipes/Hero.astro +71 -7
- package/components/recipes/Tiles.astro +285 -271
- package/content/blocks.ts +3 -1
- package/examples/recipes.ts +37 -36
- package/lib/inline-markdown.ts +6 -0
- package/lib/video.ts +19 -0
- package/package.json +1 -1
|
@@ -1,170 +1,172 @@
|
|
|
1
|
-
---
|
|
2
|
-
/**
|
|
3
|
-
* ContactCard — a Level-2 recipe: a rich, branded "get in touch" band. A display-
|
|
4
|
-
* type title + subtitle on the left, the contact channels as tappable tiles on the
|
|
5
|
-
* right, and a closing note across the bottom. For a contact or find-us page — the
|
|
6
|
-
* polished alternative to a plain notice box.
|
|
7
|
-
*
|
|
8
|
-
* Fully THEME-ADAPTIVE (composes in light AND dark): the panel is `--bg-section`
|
|
9
|
-
* with a soft `--gold` corner wash for brand presence, text is `--fg`/`--accent`/
|
|
10
|
-
* `--muted`, tiles are `--bg-surface` recesses, icons + hovers use `--gold`. Every
|
|
11
|
-
* one of those tokens flips per theme, so the band re-composes correctly in each
|
|
12
|
-
* mode (never the old always-dark band) and a CMS colour change recolours it.
|
|
13
|
-
* Contrast-safe: brand gold is used only decoratively (wash, rule, icons, hovers),
|
|
14
|
-
* never as body/label text — those use the ink/text tokens that meet AA on the
|
|
15
|
-
* panel in both themes. Whole tile links when `href` is set.
|
|
16
|
-
*/
|
|
17
|
-
import Icon from '../Icon.astro';
|
|
18
|
-
import { isIconKey } from '../../lib/icons';
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
<
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
border:
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
font-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
.b-
|
|
161
|
-
.b-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* ContactCard — a Level-2 recipe: a rich, branded "get in touch" band. A display-
|
|
4
|
+
* type title + subtitle on the left, the contact channels as tappable tiles on the
|
|
5
|
+
* right, and a closing note across the bottom. For a contact or find-us page — the
|
|
6
|
+
* polished alternative to a plain notice box.
|
|
7
|
+
*
|
|
8
|
+
* Fully THEME-ADAPTIVE (composes in light AND dark): the panel is `--bg-section`
|
|
9
|
+
* with a soft `--gold` corner wash for brand presence, text is `--fg`/`--accent`/
|
|
10
|
+
* `--muted`, tiles are `--bg-surface` recesses, icons + hovers use `--gold`. Every
|
|
11
|
+
* one of those tokens flips per theme, so the band re-composes correctly in each
|
|
12
|
+
* mode (never the old always-dark band) and a CMS colour change recolours it.
|
|
13
|
+
* Contrast-safe: brand gold is used only decoratively (wash, rule, icons, hovers),
|
|
14
|
+
* never as body/label text — those use the ink/text tokens that meet AA on the
|
|
15
|
+
* panel in both themes. Whole tile links when `href` is set.
|
|
16
|
+
*/
|
|
17
|
+
import Icon from '../Icon.astro';
|
|
18
|
+
import { isIconKey } from '../../lib/icons';
|
|
19
|
+
import { inlineMarkdown, textWithBreaks } from '../../lib/inline-markdown';
|
|
20
|
+
|
|
21
|
+
interface Item {
|
|
22
|
+
icon?: string;
|
|
23
|
+
value: string;
|
|
24
|
+
href?: string;
|
|
25
|
+
}
|
|
26
|
+
interface Props {
|
|
27
|
+
eyebrow?: string;
|
|
28
|
+
title: string;
|
|
29
|
+
subtitle?: string;
|
|
30
|
+
note?: string;
|
|
31
|
+
items?: Item[];
|
|
32
|
+
}
|
|
33
|
+
const { eyebrow, title, subtitle, note, items = [] } = Astro.props;
|
|
34
|
+
const rel = (href?: string) => (href && /^https?:\/\//.test(href) ? 'noopener' : undefined);
|
|
35
|
+
const target = (href?: string) => (href && /^https?:\/\//.test(href) ? '_blank' : undefined);
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
<section class="b-contact">
|
|
39
|
+
<div class="b-contact__panel">
|
|
40
|
+
<div class="b-contact__head">
|
|
41
|
+
{eyebrow && <p class="b-contact__eyebrow">{eyebrow}</p>}
|
|
42
|
+
<h2 class="b-contact__title">{title}</h2>
|
|
43
|
+
{subtitle && <p class="b-contact__subtitle" set:html={inlineMarkdown(subtitle)} />}
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
{items.length > 0 && (
|
|
47
|
+
<ul class="b-contact__items">
|
|
48
|
+
{items.map((item) => {
|
|
49
|
+
const inner = (
|
|
50
|
+
<>
|
|
51
|
+
{item.icon && isIconKey(item.icon) && (
|
|
52
|
+
<span class="b-contact__icon" aria-hidden="true"><Icon name={item.icon} size={18} /></span>
|
|
53
|
+
)}
|
|
54
|
+
{/* A linked item holds plain text (no nested links); an unlinked one may carry links. */}
|
|
55
|
+
<span class="b-contact__value" set:html={item.href ? textWithBreaks(item.value) : inlineMarkdown(item.value)} />
|
|
56
|
+
</>
|
|
57
|
+
);
|
|
58
|
+
return (
|
|
59
|
+
<li>
|
|
60
|
+
{item.href ? (
|
|
61
|
+
<a class="b-contact__item" href={item.href} target={target(item.href)} rel={rel(item.href)}>{inner}</a>
|
|
62
|
+
) : (
|
|
63
|
+
<div class="b-contact__item">{inner}</div>
|
|
64
|
+
)}
|
|
65
|
+
</li>
|
|
66
|
+
);
|
|
67
|
+
})}
|
|
68
|
+
</ul>
|
|
69
|
+
)}
|
|
70
|
+
|
|
71
|
+
{note && <p class="b-contact__note" set:html={inlineMarkdown(note)} />}
|
|
72
|
+
</div>
|
|
73
|
+
</section>
|
|
74
|
+
|
|
75
|
+
<style>
|
|
76
|
+
.b-contact {
|
|
77
|
+
max-width: 72rem;
|
|
78
|
+
margin-inline: auto;
|
|
79
|
+
padding-inline: 1rem;
|
|
80
|
+
}
|
|
81
|
+
/* Adaptive panel: a section surface that flips light↔dark with the theme, lifted
|
|
82
|
+
off the page by a border + shadow, with a soft brand-gold wash for presence. */
|
|
83
|
+
.b-contact__panel {
|
|
84
|
+
position: relative;
|
|
85
|
+
isolation: isolate;
|
|
86
|
+
overflow: hidden;
|
|
87
|
+
display: grid;
|
|
88
|
+
grid-template-columns: 1fr;
|
|
89
|
+
gap: 1.5rem 2.5rem;
|
|
90
|
+
padding: clamp(1.75rem, 4vw, 3rem);
|
|
91
|
+
border-radius: var(--radius);
|
|
92
|
+
background: var(--bg-section);
|
|
93
|
+
color: var(--fg);
|
|
94
|
+
border: 1px solid var(--border);
|
|
95
|
+
box-shadow: var(--shadow-md);
|
|
96
|
+
}
|
|
97
|
+
/* Brand wash — a gold glow in the top-right, behind the content (z-index below the
|
|
98
|
+
panel's own children via isolation). Adds brand presence in both themes without
|
|
99
|
+
tinting the text ground, so contrast is never at risk. */
|
|
100
|
+
.b-contact__panel::before {
|
|
101
|
+
content: '';
|
|
102
|
+
position: absolute;
|
|
103
|
+
inset: 0;
|
|
104
|
+
z-index: -1;
|
|
105
|
+
background: radial-gradient(115% 130% at 100% 0%, color-mix(in srgb, var(--gold) 20%, transparent), transparent 62%);
|
|
106
|
+
pointer-events: none;
|
|
107
|
+
}
|
|
108
|
+
@media (min-width: 48rem) {
|
|
109
|
+
.b-contact__panel { grid-template-columns: 1.2fr 1fr; align-items: center; }
|
|
110
|
+
}
|
|
111
|
+
.b-contact__head { display: flex; flex-direction: column; gap: 0.5rem; }
|
|
112
|
+
.b-contact__eyebrow {
|
|
113
|
+
margin: 0;
|
|
114
|
+
display: inline-flex; align-items: center; gap: 0.6rem;
|
|
115
|
+
font-family: var(--font-heading);
|
|
116
|
+
font-size: 0.75rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
|
|
117
|
+
color: var(--accent);
|
|
118
|
+
}
|
|
119
|
+
/* Short gold rule carries the brand into the eyebrow without colouring the text. */
|
|
120
|
+
.b-contact__eyebrow::before {
|
|
121
|
+
content: ''; flex: 0 0 auto; width: 1.5rem; height: 2px; border-radius: 2px;
|
|
122
|
+
background: var(--gold);
|
|
123
|
+
}
|
|
124
|
+
.b-contact__title {
|
|
125
|
+
margin: 0;
|
|
126
|
+
font-family: var(--font-display, var(--font-heading));
|
|
127
|
+
font-size: clamp(1.9rem, 4vw, 2.8rem);
|
|
128
|
+
line-height: 1.05;
|
|
129
|
+
text-wrap: balance;
|
|
130
|
+
color: var(--fg);
|
|
131
|
+
}
|
|
132
|
+
.b-contact__subtitle {
|
|
133
|
+
margin: 0.15rem 0 0;
|
|
134
|
+
font-family: var(--font-heading);
|
|
135
|
+
font-weight: 600;
|
|
136
|
+
color: var(--accent);
|
|
137
|
+
}
|
|
138
|
+
.b-contact__items {
|
|
139
|
+
list-style: none;
|
|
140
|
+
margin: 0; padding: 0;
|
|
141
|
+
display: flex; flex-direction: column; gap: 0.6rem;
|
|
142
|
+
}
|
|
143
|
+
.b-contact__item {
|
|
144
|
+
display: flex; align-items: center; gap: 0.75rem;
|
|
145
|
+
padding: 0.7rem 1rem;
|
|
146
|
+
border-radius: var(--radius-sm);
|
|
147
|
+
background: var(--bg-surface);
|
|
148
|
+
border: 1px solid var(--border);
|
|
149
|
+
color: var(--fg);
|
|
150
|
+
font-family: var(--font-heading);
|
|
151
|
+
font-weight: 600;
|
|
152
|
+
font-size: 0.95rem;
|
|
153
|
+
text-decoration: none;
|
|
154
|
+
transition: border-color 0.15s ease, background 0.15s ease;
|
|
155
|
+
}
|
|
156
|
+
a.b-contact__item:hover {
|
|
157
|
+
border-color: var(--gold);
|
|
158
|
+
background: color-mix(in srgb, var(--gold) 8%, var(--bg-surface));
|
|
159
|
+
}
|
|
160
|
+
a.b-contact__item:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
|
|
161
|
+
.b-contact__icon { display: inline-flex; color: var(--gold); flex: 0 0 auto; }
|
|
162
|
+
.b-contact__value { min-width: 0; overflow-wrap: anywhere; }
|
|
163
|
+
.b-contact__note {
|
|
164
|
+
grid-column: 1 / -1;
|
|
165
|
+
margin: 0;
|
|
166
|
+
padding-top: 1.25rem;
|
|
167
|
+
border-top: 1px solid var(--border);
|
|
168
|
+
color: var(--muted);
|
|
169
|
+
font-size: 0.9rem;
|
|
170
|
+
line-height: 1.6;
|
|
171
|
+
}
|
|
172
|
+
</style>
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import Heading from '../blocks/Heading.astro';
|
|
15
15
|
import Button from '../Button.astro';
|
|
16
|
-
import { videoEmbed } from '../../lib/video';
|
|
16
|
+
import { backgroundEmbed, videoEmbed } from '../../lib/video';
|
|
17
17
|
|
|
18
18
|
interface Props {
|
|
19
19
|
eyebrow?: string;
|
|
@@ -27,17 +27,23 @@ interface Props {
|
|
|
27
27
|
background?: 'none' | 'mesh';
|
|
28
28
|
}
|
|
29
29
|
const { eyebrow, title, subtitle, cta, image, media, layout = 'centered', size = 'standard', background = 'none' } = Astro.props;
|
|
30
|
+
// A YouTube / Vimeo link plays as an embedded background (after consent); anything else
|
|
31
|
+
// in `video` is a file for a native <video>.
|
|
32
|
+
const embed = media?.video ? backgroundEmbed(media.video) : null;
|
|
30
33
|
const slides = !media?.video && (media?.images?.length ?? 0) >= 2 ? media!.images! : [];
|
|
31
34
|
const still = media?.image ?? media?.images?.[0];
|
|
32
35
|
const isMedia = layout === 'media' && media && (media.video || slides.length || still);
|
|
33
|
-
const motion = isMedia
|
|
36
|
+
const motion = isMedia ? (embed ? 'embed' : media?.video ? 'video' : slides.length ? 'slides' : undefined) : undefined;
|
|
34
37
|
const overlay = (media?.overlay ?? 45) / 100;
|
|
35
38
|
const isMesh = background === 'mesh' && !isMedia;
|
|
36
39
|
// The background can lead somewhere — e.g. the full video on YouTube (new tab; the app on phones).
|
|
37
|
-
const
|
|
40
|
+
const linkTo = media?.link ?? embed?.watchUrl; // an embedded video links to itself by default
|
|
41
|
+
const bgLink = isMedia && linkTo && /^(https?:\/\/|\/)/i.test(linkTo) ? linkTo : undefined;
|
|
38
42
|
const provider = bgLink ? videoEmbed(bgLink)?.provider : undefined;
|
|
39
43
|
const watchLabel = media?.linkLabel || (provider ? `Watch the full video on ${provider}` : 'Watch the full video');
|
|
40
|
-
|
|
44
|
+
// The still: the image (no motion), or an embed's poster until the player may load.
|
|
45
|
+
const stillSrc = embed ? media?.poster ?? still : !media?.video && !slides.length ? still : undefined;
|
|
46
|
+
const bgImage = isMedia && stillSrc ? `--hero-image: url('${stillSrc}')` : '';
|
|
41
47
|
---
|
|
42
48
|
|
|
43
49
|
<section
|
|
@@ -51,7 +57,8 @@ const bgImage = isMedia && !media?.video && !slides.length && still ? `--hero-im
|
|
|
51
57
|
>
|
|
52
58
|
{isMedia && (
|
|
53
59
|
<div class="b-hero__bg" aria-hidden="true">
|
|
54
|
-
{
|
|
60
|
+
{embed && <div class="b-hero__embed" data-embed-url={embed.embedUrl} data-provider={embed.provider}></div>}
|
|
61
|
+
{media?.video && !embed ? (
|
|
55
62
|
<video autoplay muted loop playsinline poster={media.poster} preload="metadata">
|
|
56
63
|
<source src={media.video} />
|
|
57
64
|
</video>
|
|
@@ -80,7 +87,7 @@ const bgImage = isMedia && !media?.video && !slides.length && still ? `--hero-im
|
|
|
80
87
|
</a>
|
|
81
88
|
)}
|
|
82
89
|
{motion && (
|
|
83
|
-
<button type="button" class="b-hero__pause" aria-pressed="false" aria-label="Pause background">
|
|
90
|
+
<button type="button" class="b-hero__pause" aria-pressed="false" aria-label="Pause background" hidden={motion === 'embed'}>
|
|
84
91
|
<span class="b-hero__pause-icon" aria-hidden="true"></span>
|
|
85
92
|
</button>
|
|
86
93
|
)}
|
|
@@ -92,13 +99,38 @@ const bgImage = isMedia && !media?.video && !slides.length && still ? `--hero-im
|
|
|
92
99
|
</section>
|
|
93
100
|
|
|
94
101
|
<script>
|
|
95
|
-
// Moving hero backgrounds: pause button, reduced-motion,
|
|
102
|
+
// Moving hero backgrounds: pause button, reduced-motion, the slideshow timer, and an
|
|
103
|
+
// embedded (YouTube / Vimeo) background that loads only once the visitor consents.
|
|
104
|
+
import { hasConsent, onConsentChange } from '../../lib/consent-client';
|
|
96
105
|
const SLIDE_MS = 6000;
|
|
97
106
|
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
98
107
|
for (const hero of document.querySelectorAll<HTMLElement>('.b-hero[data-motion]')) {
|
|
99
108
|
const button = hero.querySelector<HTMLButtonElement>('.b-hero__pause');
|
|
100
109
|
const video = hero.querySelector('video');
|
|
101
110
|
const slides = [...hero.querySelectorAll<HTMLElement>('.b-hero__slide')];
|
|
111
|
+
const embedSlot = hero.querySelector<HTMLElement>('.b-hero__embed');
|
|
112
|
+
let player: HTMLIFrameElement | null = null;
|
|
113
|
+
// Play / pause an embedded player by message, with no provider API script.
|
|
114
|
+
const command = (play: boolean) => {
|
|
115
|
+
if (!player?.contentWindow) return;
|
|
116
|
+
const msg = embedSlot!.dataset.provider === 'Vimeo'
|
|
117
|
+
? { method: play ? 'play' : 'pause' }
|
|
118
|
+
: { event: 'command', func: play ? 'playVideo' : 'pauseVideo', args: [] };
|
|
119
|
+
player.contentWindow.postMessage(JSON.stringify(msg), new URL(player.src).origin);
|
|
120
|
+
};
|
|
121
|
+
const loadPlayer = () => {
|
|
122
|
+
if (player || !embedSlot || reduce) return; // reduced motion keeps the still
|
|
123
|
+
player = document.createElement('iframe');
|
|
124
|
+
player.src = embedSlot.dataset.embedUrl!;
|
|
125
|
+
player.title = 'Background video';
|
|
126
|
+
player.tabIndex = -1;
|
|
127
|
+
player.setAttribute('aria-hidden', 'true');
|
|
128
|
+
player.allow = 'autoplay; encrypted-media; picture-in-picture';
|
|
129
|
+
player.addEventListener('load', () => window.setTimeout(() => embedSlot.classList.add('is-on'), 600));
|
|
130
|
+
embedSlot.appendChild(player);
|
|
131
|
+
if (button) button.hidden = false;
|
|
132
|
+
sync();
|
|
133
|
+
};
|
|
102
134
|
let paused = reduce;
|
|
103
135
|
let visible = true;
|
|
104
136
|
let index = 0;
|
|
@@ -115,6 +147,7 @@ const bgImage = isMedia && !media?.video && !slides.length && still ? `--hero-im
|
|
|
115
147
|
if (run) void video.play().catch(() => {});
|
|
116
148
|
else video.pause();
|
|
117
149
|
}
|
|
150
|
+
if (player) command(run);
|
|
118
151
|
if (slides.length > 1) {
|
|
119
152
|
window.clearInterval(timer);
|
|
120
153
|
timer = run ? window.setInterval(tick, SLIDE_MS) : undefined;
|
|
@@ -128,6 +161,10 @@ const bgImage = isMedia && !media?.video && !slides.length && still ? `--hero-im
|
|
|
128
161
|
button?.addEventListener('click', () => { paused = !paused; sync(); });
|
|
129
162
|
document.addEventListener('visibilitychange', sync);
|
|
130
163
|
new IntersectionObserver(([e]) => { visible = e.isIntersecting; sync(); }).observe(hero);
|
|
164
|
+
if (embedSlot) {
|
|
165
|
+
if (hasConsent()) loadPlayer();
|
|
166
|
+
onConsentChange((allowed) => { if (allowed) loadPlayer(); });
|
|
167
|
+
}
|
|
131
168
|
sync();
|
|
132
169
|
}
|
|
133
170
|
</script>
|
|
@@ -251,6 +288,33 @@ const bgImage = isMedia && !media?.video && !slides.length && still ? `--hero-im
|
|
|
251
288
|
height: 100%;
|
|
252
289
|
object-fit: cover;
|
|
253
290
|
}
|
|
291
|
+
/* An embedded background player, sized to COVER the hero like object-fit (16:9 kept,
|
|
292
|
+
overflow cropped). Never interactive; fades in once it has started. */
|
|
293
|
+
.b-hero__embed {
|
|
294
|
+
position: absolute;
|
|
295
|
+
inset: 0;
|
|
296
|
+
overflow: hidden;
|
|
297
|
+
pointer-events: none;
|
|
298
|
+
container-type: size;
|
|
299
|
+
opacity: 0;
|
|
300
|
+
transition: opacity 1.2s ease;
|
|
301
|
+
}
|
|
302
|
+
.b-hero__embed.is-on {
|
|
303
|
+
opacity: 1;
|
|
304
|
+
}
|
|
305
|
+
.b-hero__embed :global(iframe) {
|
|
306
|
+
position: absolute;
|
|
307
|
+
top: 50%;
|
|
308
|
+
left: 50%;
|
|
309
|
+
width: max(100cqw, 177.78cqh);
|
|
310
|
+
height: max(100cqh, 56.25cqw);
|
|
311
|
+
max-width: none; /* the global media reset caps iframes at 100%: cover must overflow */
|
|
312
|
+
transform: translate(-50%, -50%);
|
|
313
|
+
border: 0;
|
|
314
|
+
}
|
|
315
|
+
.b-hero__pause[hidden] {
|
|
316
|
+
display: none;
|
|
317
|
+
}
|
|
254
318
|
.b-hero__slide {
|
|
255
319
|
position: absolute;
|
|
256
320
|
inset: 0;
|