@struq/skill 0.3.0 → 0.4.1
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/README.md +22 -36
- package/package.json +4 -2
- package/skills/struq/SKILL.md +139 -307
- package/skills/struq/blocks/CATALOG.md +426 -0
- package/skills/struq/blocks/backgrounds/background-aurora.tsx +818 -0
- package/skills/struq/blocks/backgrounds/background-facets.tsx +1283 -0
- package/skills/struq/blocks/backgrounds/background-silk.tsx +906 -0
- package/skills/struq/blocks/catalog.json +1302 -0
- package/skills/struq/blocks/footers/footer-contact.tsx +789 -0
- package/skills/struq/blocks/footers/footer-reveal.tsx +313 -0
- package/skills/struq/blocks/headers/header-bar.tsx +652 -0
- package/skills/struq/blocks/headers/menu-curtain.tsx +790 -0
- package/skills/struq/blocks/heroes/hero-framed.tsx +627 -0
- package/skills/struq/blocks/heroes/hero-index.tsx +855 -0
- package/skills/struq/blocks/heroes/hero-poster.tsx +1086 -0
- package/skills/struq/blocks/heroes/hero-split.tsx +685 -0
- package/skills/struq/blocks/heroes/hero-still.tsx +862 -0
- package/skills/struq/{recipes/components/struq → blocks/kit}/Cursor.tsx +1 -1
- package/skills/struq/{recipes/components/struq → blocks/kit}/HeroCanvas.tsx +4 -4
- package/skills/struq/{recipes/components/struq → blocks/kit}/HorizontalGallery.tsx +2 -2
- package/skills/struq/{recipes/components/struq → blocks/kit}/MagneticButton.tsx +1 -1
- package/skills/struq/{recipes/components/struq → blocks/kit}/MediaFrame.tsx +2 -2
- package/skills/struq/{recipes/components/struq → blocks/kit}/PinnedZoom.tsx +14 -4
- package/skills/struq/{recipes/components/struq → blocks/kit}/Reveal.tsx +1 -1
- package/skills/struq/{recipes/components/struq → blocks/kit}/SmoothScroll.tsx +1 -1
- package/skills/struq/{recipes/components/struq → blocks/kit}/SplitHeadline.tsx +1 -1
- package/skills/struq/blocks/kit/blocks.css +2102 -0
- package/skills/struq/blocks/kit/images.ts +17 -0
- package/skills/struq/blocks/kit/photo-read.ts +102 -0
- package/skills/struq/blocks/page-transitions/page-transition-gate.tsx +706 -0
- package/skills/struq/blocks/page-transitions/page-transition-slices.tsx +469 -0
- package/skills/struq/blocks/page-transitions/page-transition-stack.tsx +583 -0
- package/skills/struq/blocks/preloaders/preloader-gate.tsx +358 -0
- package/skills/struq/blocks/preloaders/preloader-slices-uncovered.ts +50 -0
- package/skills/struq/blocks/preloaders/preloader-slices.tsx +289 -0
- package/skills/struq/blocks/preloaders/preloader-stack.tsx +367 -0
- package/skills/struq/blocks/preloaders/preloader-wordmark.tsx +568 -0
- package/skills/struq/blocks/sections/about-studio.tsx +436 -0
- package/skills/struq/blocks/sections/approach.tsx +661 -0
- package/skills/struq/blocks/sections/contact-form.tsx +664 -0
- package/skills/struq/blocks/sections/cta-closing.tsx +763 -0
- package/skills/struq/blocks/sections/fanned-cards.tsx +547 -0
- package/skills/struq/blocks/sections/film-strip.tsx +385 -0
- package/skills/struq/blocks/sections/grid-assembly.tsx +616 -0
- package/skills/struq/blocks/sections/hover-list.tsx +467 -0
- package/skills/struq/blocks/sections/image-trail.tsx +432 -0
- package/skills/struq/blocks/sections/manifesto.tsx +691 -0
- package/skills/struq/blocks/sections/panel-accordion.tsx +380 -0
- package/skills/struq/blocks/sections/reveals.tsx +416 -0
- package/skills/struq/blocks/sections/split-pair.tsx +670 -0
- package/skills/struq/blocks/sections/sticky-chapters.tsx +256 -0
- package/skills/struq/blocks/sections/velocity-type.tsx +269 -0
- package/skills/struq/{cinematic/showpieces/toonstuk-fill-rows.tsx → blocks/showpieces/fill-rows.tsx} +1 -1
- package/skills/struq/{cinematic/showpieces/toonstuk-hero-opener.tsx → blocks/showpieces/hero-opener.tsx} +5 -5
- package/skills/struq/{cinematic/showpieces/toonstuk-image-to-fullbleed.tsx → blocks/showpieces/image-to-fullbleed.tsx} +16 -5
- package/skills/struq/blocks/showpieces/manifesto-scrub.tsx +48 -0
- package/skills/struq/{cinematic/showpieces/toonstuk-panel-wipe.tsx → blocks/showpieces/panel-wipe.tsx} +2 -2
- package/skills/struq/{cinematic/showpieces/toonstuk-pixel-resolve.tsx → blocks/showpieces/pixel-resolve.tsx} +1 -1
- package/skills/struq/{cinematic/showpieces/toonstuk-project-stack.tsx → blocks/showpieces/project-stack.tsx} +2 -2
- package/skills/struq/blocks/showpieces/scrub-connector.tsx +307 -0
- package/skills/struq/{cinematic/showpieces/toonstuk-wordmark-finale.tsx → blocks/showpieces/wordmark-finale.tsx} +1 -1
- package/skills/struq/blocks/transitions/zoom-through.tsx +613 -0
- package/skills/struq/canon/canon.json +627 -0
- package/skills/struq/cinematic/README.md +7 -18
- package/skills/struq/cinematic/examples/code/nextjs/app/components/LenisRoot.tsx +1 -1
- package/skills/struq/cinematic/examples/code/shared/styles.css +1 -1
- package/skills/struq/cinematic/examples/code/vite/src/LenisRoot.tsx +1 -1
- package/skills/struq/cinematic/references/01-core-mental-model.md +1 -1
- package/skills/struq/cinematic/references/02-vocabulary.md +1 -1
- package/skills/struq/cinematic/references/07-stack-integration.md +2 -2
- package/skills/struq/recipes/app/fonts.ts +12 -22
- package/skills/struq/recipes/app/globals.css +34 -177
- package/skills/struq/recipes/app/layout.tsx +13 -11
- package/skills/struq/references/craft.md +77 -0
- package/skills/struq/references/iconography.md +28 -230
- package/skills/struq/references/stack-targets.md +23 -74
- package/skills/struq/references/tells.md +52 -0
- package/skills/struq/references/triage.md +6 -7
- package/skills/struq/scripts/blocks.mjs +156 -0
- package/skills/struq/scripts/canon.mjs +106 -281
- package/skills/struq/scripts/check.mjs +92 -126
- package/skills/struq/scripts/images.mjs +2 -2
- package/skills/struq/scripts/log.mjs +6 -23
- package/skills/struq/scripts/tokens.mjs +222 -0
- package/skills/struq/cinematic/showpieces/README.md +0 -18
- package/skills/struq/cinematic/showpieces/toonstuk-manifesto-scrub.tsx +0 -81
- package/skills/struq/cinematic/showpieces/toonstuk-scrub-connector.tsx +0 -78
- package/skills/struq/references/anti-patterns.md +0 -330
- package/skills/struq/references/baseline-prompt.md +0 -122
- package/skills/struq/references/brand.md +0 -143
- package/skills/struq/references/brief-router.md +0 -218
- package/skills/struq/references/composition.md +0 -190
- package/skills/struq/references/depth-parallax.md +0 -235
- package/skills/struq/references/direction.md +0 -188
- package/skills/struq/references/feel-layer.md +0 -107
- package/skills/struq/references/gsap-api.md +0 -293
- package/skills/struq/references/hero-scenes.md +0 -107
- package/skills/struq/references/imagery.md +0 -125
- package/skills/struq/references/loaders.md +0 -287
- package/skills/struq/references/micro-interactions.md +0 -270
- package/skills/struq/references/mobile-composition.md +0 -70
- package/skills/struq/references/nav-header.md +0 -296
- package/skills/struq/references/niche-packets.md +0 -338
- package/skills/struq/references/page-architecture.md +0 -134
- package/skills/struq/references/page-entrances.md +0 -198
- package/skills/struq/references/performance.md +0 -235
- package/skills/struq/references/pinning-scrub.md +0 -340
- package/skills/struq/references/pitfalls.md +0 -290
- package/skills/struq/references/route-transitions.md +0 -281
- package/skills/struq/references/scaling.md +0 -151
- package/skills/struq/references/scroll-reveals.md +0 -279
- package/skills/struq/references/sections.md +0 -141
- package/skills/struq/references/seo.md +0 -219
- package/skills/struq/references/setup.md +0 -397
- package/skills/struq/references/showcase.md +0 -87
- package/skills/struq/references/struq-imagery.md +0 -107
- package/skills/struq/references/svg-motion.md +0 -390
- package/skills/struq/references/transitions.md +0 -160
- package/skills/struq/references/typography.md +0 -720
- package/skills/struq/references/vibe-presets.md +0 -287
- package/skills/struq/references/webgl-r3f.md +0 -274
- package/skills/struq/references/webgl-scenes.md +0 -593
- package/skills/struq/scripts/font-names.mjs +0 -40
- package/skills/struq/scripts/recipes.mjs +0 -135
- /package/skills/struq/{recipes/lib → blocks/kit}/gsap.ts +0 -0
- /package/skills/struq/{recipes/components/struq → blocks/kit}/scenes/contours.ts +0 -0
- /package/skills/struq/{recipes/components/struq → blocks/kit}/scenes/dots.ts +0 -0
- /package/skills/struq/{recipes/components/struq → blocks/kit}/scenes/ripple.ts +0 -0
- /package/skills/struq/{recipes/components/struq → blocks/kit}/scenes/types.ts +0 -0
|
@@ -0,0 +1,2102 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
The block kit's CSS, as a client project gets it from the skill (recipes/app/globals.css), with one change:
|
|
3
|
+
everything that the skill sets on :root and html is scoped to [data-blocks] here, so the blocks can render
|
|
4
|
+
inside struq.nl's own page without touching it. Imported once from app/globals.css.
|
|
5
|
+
|
|
6
|
+
Two names exist in Struq's own theme too: --color-accent and --font-display. Inside [data-blocks] they are
|
|
7
|
+
pointed at the block tokens, so bg-accent and font-display mean the block's palette and face there and
|
|
8
|
+
Struq's everywhere else.
|
|
9
|
+
========================================================================== */
|
|
10
|
+
|
|
11
|
+
@theme inline {
|
|
12
|
+
--color-surface-0: var(--surface-0);
|
|
13
|
+
--color-surface-1: var(--surface-1);
|
|
14
|
+
--color-surface-2: var(--surface-2);
|
|
15
|
+
--color-surface-3: var(--surface-3);
|
|
16
|
+
--color-ink: var(--ink);
|
|
17
|
+
--color-ink-muted: var(--ink-muted);
|
|
18
|
+
--color-accent-ink: var(--accent-ink);
|
|
19
|
+
--color-line: var(--line);
|
|
20
|
+
--color-on-photo: var(--on-photo);
|
|
21
|
+
--color-inverse: var(--inverse);
|
|
22
|
+
--color-on-inverse: var(--on-inverse);
|
|
23
|
+
--color-field: var(--field);
|
|
24
|
+
--color-on-field: var(--on-field);
|
|
25
|
+
--font-body: var(--face-body), ui-sans-serif, system-ui, sans-serif;
|
|
26
|
+
--font-accent: var(--face-accent, var(--face-display)), ui-serif, Georgia, serif;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@theme {
|
|
30
|
+
/* Type roles: text-display, text-h2, text-h3, text-lead, text-body, text-small. */
|
|
31
|
+
--text-display: clamp(2.875rem, 1.2rem + 6.86vw, 8rem);
|
|
32
|
+
--text-display--line-height: 0.95;
|
|
33
|
+
--text-display--letter-spacing: -0.025em;
|
|
34
|
+
--text-h2: clamp(2.25rem, 1.3rem + 3.4vw, 4.75rem);
|
|
35
|
+
--text-h2--line-height: 1.04;
|
|
36
|
+
--text-h2--letter-spacing: -0.015em;
|
|
37
|
+
--text-h3: clamp(1.375rem, 1.15rem + 0.8vw, 1.875rem);
|
|
38
|
+
--text-h3--line-height: 1.2;
|
|
39
|
+
--text-lead: clamp(1.125rem, 1.05rem + 0.3vw, 1.3125rem);
|
|
40
|
+
--text-lead--line-height: 1.5;
|
|
41
|
+
--text-body: 1.0625rem;
|
|
42
|
+
--text-body--line-height: 1.65;
|
|
43
|
+
--text-small: 0.875rem;
|
|
44
|
+
--text-small--line-height: 1.5;
|
|
45
|
+
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
|
|
46
|
+
--ease-in-out-quint: cubic-bezier(0.83, 0, 0.17, 1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* The default tokens (the skill's offline "paper" preset). The lab overwrites them per palette. */
|
|
50
|
+
[data-blocks] {
|
|
51
|
+
--surface-0: #f5efe6;
|
|
52
|
+
--surface-1: #ede4d6;
|
|
53
|
+
--surface-2: #e1d4bf;
|
|
54
|
+
--surface-3: #cdbb9f;
|
|
55
|
+
--ink: #1e1a15;
|
|
56
|
+
--ink-muted: #5d5446;
|
|
57
|
+
--accent: #8a3b22;
|
|
58
|
+
--accent-ink: #fbf6ee;
|
|
59
|
+
--line: rgb(30 26 21 / 0.12);
|
|
60
|
+
--on-photo: #f7f3ec;
|
|
61
|
+
--inverse: #1e1a15;
|
|
62
|
+
--on-inverse: #f5efe6;
|
|
63
|
+
--field: #8a3b22;
|
|
64
|
+
--on-field: #fbf6ee;
|
|
65
|
+
--display-weight: 300;
|
|
66
|
+
|
|
67
|
+
--color-accent: var(--accent);
|
|
68
|
+
--font-display: var(--face-display), ui-serif, Georgia, serif;
|
|
69
|
+
|
|
70
|
+
font-size: clamp(1rem, 0.92rem + 0.2vw, 1.125rem);
|
|
71
|
+
background: var(--surface-0);
|
|
72
|
+
color: var(--ink);
|
|
73
|
+
font-family: var(--font-body);
|
|
74
|
+
line-height: 1.65;
|
|
75
|
+
-webkit-font-smoothing: antialiased;
|
|
76
|
+
text-rendering: optimizeLegibility;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
[data-blocks] :is(h1, h2, h3, .font-display) {
|
|
80
|
+
font-family: var(--font-display);
|
|
81
|
+
font-weight: var(--display-weight);
|
|
82
|
+
text-wrap: balance;
|
|
83
|
+
}
|
|
84
|
+
[data-blocks] p {
|
|
85
|
+
text-wrap: pretty;
|
|
86
|
+
}
|
|
87
|
+
[data-blocks] ::selection {
|
|
88
|
+
background: var(--accent);
|
|
89
|
+
color: var(--accent-ink);
|
|
90
|
+
}
|
|
91
|
+
[data-blocks] :focus-visible {
|
|
92
|
+
outline: 2px solid var(--accent);
|
|
93
|
+
outline-offset: 3px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* The contrast scene: wrap a section in scene-inverse or scene-field; the tokens turn inside it. */
|
|
97
|
+
[data-blocks] .scene-inverse {
|
|
98
|
+
--surface-0: var(--inverse);
|
|
99
|
+
--surface-1: color-mix(in oklab, var(--inverse), var(--on-inverse) 6%);
|
|
100
|
+
--surface-2: color-mix(in oklab, var(--inverse), var(--on-inverse) 12%);
|
|
101
|
+
--surface-3: color-mix(in oklab, var(--inverse), var(--on-inverse) 22%);
|
|
102
|
+
--ink: var(--on-inverse);
|
|
103
|
+
--ink-muted: color-mix(in oklab, var(--on-inverse) 72%, var(--inverse));
|
|
104
|
+
--line: color-mix(in oklab, var(--on-inverse) 18%, transparent);
|
|
105
|
+
background-color: var(--surface-0);
|
|
106
|
+
color: var(--ink);
|
|
107
|
+
}
|
|
108
|
+
[data-blocks] .scene-field {
|
|
109
|
+
--surface-0: var(--field);
|
|
110
|
+
--surface-1: color-mix(in oklab, var(--field), var(--on-field) 8%);
|
|
111
|
+
--surface-2: color-mix(in oklab, var(--field), var(--on-field) 14%);
|
|
112
|
+
--surface-3: color-mix(in oklab, var(--field), var(--on-field) 24%);
|
|
113
|
+
--ink: var(--on-field);
|
|
114
|
+
--ink-muted: color-mix(in oklab, var(--on-field) 78%, var(--field));
|
|
115
|
+
--line: color-mix(in oklab, var(--on-field) 22%, transparent);
|
|
116
|
+
--accent: var(--on-field);
|
|
117
|
+
--accent-ink: var(--field);
|
|
118
|
+
background-color: var(--surface-0);
|
|
119
|
+
color: var(--ink);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* The hero opener's handover: the section after it slides up over the pinned opener. */
|
|
123
|
+
[data-blocks] [data-showpiece="hero-opener"] + *,
|
|
124
|
+
[data-blocks] .pin-spacer:has(> [data-showpiece="hero-opener"]) + * {
|
|
125
|
+
position: relative;
|
|
126
|
+
z-index: 2;
|
|
127
|
+
background-color: var(--surface-0);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* The motion gate. In a client project the skill's layout puts `js` on <html> before paint; on struq.nl the root
|
|
131
|
+
layout's pre-paint script puts `sq-js` there (components/site/loaders/use-first-visit.ts), so a visitor without
|
|
132
|
+
JavaScript gets every element in its final state and nothing waits for a script that will not run. */
|
|
133
|
+
.sq-js [data-blocks] [data-split]:not([data-ready]) {
|
|
134
|
+
visibility: hidden;
|
|
135
|
+
animation: struq-show 0s 3s forwards;
|
|
136
|
+
}
|
|
137
|
+
.sq-js [data-blocks] [data-reveal]:not([data-ready]) > * {
|
|
138
|
+
opacity: 0;
|
|
139
|
+
animation: struq-fade 0s 3s forwards;
|
|
140
|
+
}
|
|
141
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
142
|
+
.sq-js [data-blocks] [data-frame="mask"]:not([data-ready]) {
|
|
143
|
+
clip-path: inset(100% 0 0 0);
|
|
144
|
+
animation: struq-open 0s 3s forwards;
|
|
145
|
+
}
|
|
146
|
+
.sq-js [data-blocks] [data-zoom-frame] {
|
|
147
|
+
clip-path: inset(22% 30% 22% 30%);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
@media (min-width: 768px) and (prefers-reduced-motion: no-preference) {
|
|
151
|
+
.sq-js [data-blocks] [data-showpiece="image-to-fullbleed"] [data-frame] {
|
|
152
|
+
clip-path: inset(31% 40% 31% 40%);
|
|
153
|
+
}
|
|
154
|
+
/* The connector's start is computed from the layout, so the joined still waits unseen until it is set. */
|
|
155
|
+
.sq-js [data-blocks] [data-showpiece="scrub-connector"]:not([data-motion]) > [data-row] {
|
|
156
|
+
visibility: hidden;
|
|
157
|
+
animation: struq-show 0s 3s forwards;
|
|
158
|
+
}
|
|
159
|
+
.sq-js [data-blocks] [data-showpiece="pixel-resolve"] canvas {
|
|
160
|
+
opacity: 1;
|
|
161
|
+
background: var(--surface-2);
|
|
162
|
+
animation: struq-unveil 0s 4s forwards;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
@keyframes struq-unveil { to { opacity: 0; visibility: hidden; } }
|
|
166
|
+
@keyframes struq-show { to { visibility: visible; } }
|
|
167
|
+
@keyframes struq-fade { to { opacity: 1; } }
|
|
168
|
+
@keyframes struq-open { to { clip-path: inset(0 0 0 0); } }
|
|
169
|
+
|
|
170
|
+
/* SplitText masks: room for descenders inside each line's clip. */
|
|
171
|
+
[data-blocks] .struq-line-mask {
|
|
172
|
+
padding-block: 0.06em 0.16em;
|
|
173
|
+
margin-block: -0.06em -0.16em;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* --- preloader-gate and page-transition-gate --- */
|
|
177
|
+
/* The gate's ground. "deep" (the default) is a step darker than the page in every palette: the depth of an ink on
|
|
178
|
+
a light ground, a deeper night on a dark one, so the gate reads as the room the page is shown in. Browsers without
|
|
179
|
+
relative colour take the inverse. --on-gate is the palette's light colour, which reads on either. */
|
|
180
|
+
[data-blocks] [data-gate] {
|
|
181
|
+
--gate: var(--inverse);
|
|
182
|
+
--on-gate: var(--on-inverse);
|
|
183
|
+
}
|
|
184
|
+
@supports (color: oklch(from red l c h)) {
|
|
185
|
+
[data-blocks] [data-gate] {
|
|
186
|
+
--gate: oklch(from var(--surface-0) min(calc(l * 0.55), 0.2) c h);
|
|
187
|
+
--on-gate: var(--on-photo);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
[data-blocks] [data-gate][data-tone="inverse"] {
|
|
191
|
+
--gate: var(--inverse);
|
|
192
|
+
--on-gate: var(--on-inverse);
|
|
193
|
+
}
|
|
194
|
+
[data-blocks] [data-gate][data-tone="field"] {
|
|
195
|
+
--gate: var(--field);
|
|
196
|
+
--on-gate: var(--on-field);
|
|
197
|
+
}
|
|
198
|
+
/* The preloader is never painted unless its own inline script, which runs in the HTML before the gate is parsed,
|
|
199
|
+
shows it: so without JS, under reduced motion and on a session revisit it covers nothing. From its first paint
|
|
200
|
+
the leader runs on CSS alone (the count is one number, --gate-p, everything else is derived from it); the block's
|
|
201
|
+
script takes the same number over when React hydrates. If the script never arrives the gate still opens at 1.6s
|
|
202
|
+
and is gone at 2.4s, so a failed bundle can never leave a cover on the page. */
|
|
203
|
+
@property --gate-p {
|
|
204
|
+
syntax: "<number>";
|
|
205
|
+
inherits: true;
|
|
206
|
+
initial-value: 0;
|
|
207
|
+
}
|
|
208
|
+
[data-blocks] [data-preloader-gate] {
|
|
209
|
+
display: none;
|
|
210
|
+
--gate-b: min(calc(var(--gate-p) * 3), 2.99);
|
|
211
|
+
animation:
|
|
212
|
+
struq-gate-count 1.56s linear forwards,
|
|
213
|
+
struq-gate-gone 0s linear 2.4s forwards;
|
|
214
|
+
}
|
|
215
|
+
/* One beat per figure: --on is 1 during beat --i and 0 outside it, a hard cut as on a film leader. */
|
|
216
|
+
[data-blocks] [data-preloader-gate] [data-beat] {
|
|
217
|
+
--on: calc(
|
|
218
|
+
clamp(0, (var(--gate-b) - var(--i) + 0.01) * 1000, 1) * clamp(0, (var(--i) + 1 - var(--gate-b)) * 1000, 1)
|
|
219
|
+
);
|
|
220
|
+
opacity: var(--on);
|
|
221
|
+
}
|
|
222
|
+
[data-blocks] [data-preloader-gate] [data-sweep] {
|
|
223
|
+
stroke-dashoffset: clamp(0, calc(1 - var(--gate-b) + var(--i)), 1);
|
|
224
|
+
}
|
|
225
|
+
[data-blocks] [data-preloader-gate] [data-hand] {
|
|
226
|
+
transform-box: view-box;
|
|
227
|
+
transform-origin: 50% 50%;
|
|
228
|
+
rotate: calc(var(--gate-b) * 360deg);
|
|
229
|
+
}
|
|
230
|
+
[data-blocks] [data-preloader-gate] [data-bar="top"] {
|
|
231
|
+
animation: struq-gate-top 0.78s cubic-bezier(0.76, 0, 0.24, 1) 1.6s forwards;
|
|
232
|
+
}
|
|
233
|
+
[data-blocks] [data-preloader-gate] [data-bar="bottom"] {
|
|
234
|
+
animation: struq-gate-bottom 0.78s cubic-bezier(0.76, 0, 0.24, 1) 1.6s forwards;
|
|
235
|
+
}
|
|
236
|
+
[data-blocks] [data-preloader-gate] :is([data-leader], [data-name]) {
|
|
237
|
+
animation: struq-gate-leader 0.3s cubic-bezier(0.55, 0, 1, 0.45) 1.42s forwards;
|
|
238
|
+
}
|
|
239
|
+
[data-blocks] [data-preloader-gate] [data-light] {
|
|
240
|
+
scale: 0 1;
|
|
241
|
+
animation: struq-gate-light 0.4s cubic-bezier(0.16, 1, 0.3, 1) 1.5s forwards;
|
|
242
|
+
}
|
|
243
|
+
/* Once the block's script drives the gate, the CSS clock stops everywhere in it. */
|
|
244
|
+
[data-blocks] [data-preloader-gate][data-driven],
|
|
245
|
+
[data-blocks] [data-preloader-gate][data-driven] * {
|
|
246
|
+
animation: none;
|
|
247
|
+
}
|
|
248
|
+
@keyframes struq-gate-count {
|
|
249
|
+
0% {
|
|
250
|
+
--gate-p: 0;
|
|
251
|
+
animation-timing-function: cubic-bezier(0.45, 0, 0.55, 1);
|
|
252
|
+
}
|
|
253
|
+
73.7% {
|
|
254
|
+
--gate-p: 0.86;
|
|
255
|
+
}
|
|
256
|
+
83.3% {
|
|
257
|
+
--gate-p: 0.86;
|
|
258
|
+
animation-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
|
|
259
|
+
}
|
|
260
|
+
100% {
|
|
261
|
+
--gate-p: 1;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
@keyframes struq-gate-gone { to { visibility: hidden; } }
|
|
265
|
+
@keyframes struq-gate-top { to { translate: 0 -100%; } }
|
|
266
|
+
@keyframes struq-gate-bottom { to { translate: 0 100%; } }
|
|
267
|
+
@keyframes struq-gate-leader { to { opacity: 0; scale: 0.92; } }
|
|
268
|
+
@keyframes struq-gate-light { to { scale: 1 1; } }
|
|
269
|
+
|
|
270
|
+
/* --- preloader-stack --- */
|
|
271
|
+
/* The stack is never painted unless its own inline script, which runs while the cover is parsed, armed it: so without
|
|
272
|
+
JS, under reduced motion and on a session revisit it covers nothing. From its first paint it is alive on CSS alone:
|
|
273
|
+
the first print shows once it is decoded (data-first), the caption once its own faces are in (data-fonts), both
|
|
274
|
+
set by tiny scripts parsed inside the cover, and the count runs on one number, --stack-n, shown three digits wide.
|
|
275
|
+
The block's script takes that number over where it stands when it hydrates (data-live), which also stops this
|
|
276
|
+
clock and its failsafe. If the script never arrives the cover is gone at 2.3s; everything in it inherits that
|
|
277
|
+
visibility, so a failed bundle can never leave any of it on the page. */
|
|
278
|
+
@property --stack-n {
|
|
279
|
+
syntax: "<integer>";
|
|
280
|
+
inherits: true;
|
|
281
|
+
initial-value: 0;
|
|
282
|
+
}
|
|
283
|
+
@counter-style struq-stack-count {
|
|
284
|
+
system: extends decimal;
|
|
285
|
+
pad: 3 "0";
|
|
286
|
+
}
|
|
287
|
+
[data-blocks] [data-preloader="stack"] {
|
|
288
|
+
display: none;
|
|
289
|
+
}
|
|
290
|
+
[data-blocks] [data-preloader="stack"][data-armed] {
|
|
291
|
+
display: block;
|
|
292
|
+
animation:
|
|
293
|
+
struq-stack-count 1.7s cubic-bezier(0.33, 1, 0.68, 1) forwards,
|
|
294
|
+
struq-stack-gone 0s linear 2.3s forwards;
|
|
295
|
+
}
|
|
296
|
+
[data-blocks] [data-preloader="stack"][data-armed][data-first] [data-cut]:first-child {
|
|
297
|
+
visibility: inherit;
|
|
298
|
+
}
|
|
299
|
+
[data-blocks] [data-preloader="stack"][data-armed]:not([data-fonts]) [data-caption] {
|
|
300
|
+
visibility: hidden;
|
|
301
|
+
}
|
|
302
|
+
[data-blocks] [data-preloader="stack"] [data-count] {
|
|
303
|
+
counter-reset: struq-stack-n var(--stack-n);
|
|
304
|
+
}
|
|
305
|
+
[data-blocks] [data-preloader="stack"] [data-count]::after {
|
|
306
|
+
content: counter(struq-stack-n, struq-stack-count);
|
|
307
|
+
}
|
|
308
|
+
[data-blocks] [data-preloader="stack"][data-live] {
|
|
309
|
+
animation: none;
|
|
310
|
+
}
|
|
311
|
+
@media (prefers-reduced-motion: reduce) {
|
|
312
|
+
[data-blocks] [data-preloader="stack"] {
|
|
313
|
+
display: none !important;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
@keyframes struq-stack-count { from { --stack-n: 0; } to { --stack-n: 90; } }
|
|
317
|
+
@keyframes struq-stack-gone { to { visibility: hidden; } }
|
|
318
|
+
|
|
319
|
+
/* --- grid-assembly --- */
|
|
320
|
+
/* The scatter is the first paint on wide screens with motion, and only once the page runs scripts: each tile is
|
|
321
|
+
moved from its cell to its place in the pile by its own custom properties, and the block's script then drives
|
|
322
|
+
one number per tile (--p into the cell; the push out of the way is written inline, in whole pixels). If the
|
|
323
|
+
script never arrives, the tiles drop into the grid after 3s. Phones open each tile from its foot; without
|
|
324
|
+
scripts nothing is hidden. */
|
|
325
|
+
@media (min-width: 768px) and (prefers-reduced-motion: no-preference) {
|
|
326
|
+
:is(.js, .sq-js) [data-blocks] [data-grid-assembly] [data-tile] {
|
|
327
|
+
transform: translate3d(calc((1 - var(--p, 0)) * var(--sx, 0px)), calc((1 - var(--p, 0)) * var(--sy, 0px)), 0)
|
|
328
|
+
rotate(calc((1 - var(--p, 0)) * var(--sr, 0deg)))
|
|
329
|
+
scale(calc(1 - (1 - var(--p, 0)) * var(--sd, 0)));
|
|
330
|
+
}
|
|
331
|
+
:is(.js, .sq-js) [data-blocks] [data-grid-assembly] [data-ga-line] {
|
|
332
|
+
transform: translate3d(0, calc((1 - var(--p, 0)) * 112%), 0);
|
|
333
|
+
}
|
|
334
|
+
:is(.js, .sq-js) [data-blocks] [data-grid-assembly]:not([data-motion]) [data-tile] {
|
|
335
|
+
animation: struq-ga-rest 0s 3s forwards;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
@media (max-width: 767px) and (prefers-reduced-motion: no-preference) {
|
|
339
|
+
:is(.js, .sq-js) [data-blocks] [data-grid-assembly]:not([data-shown]) [data-tile] {
|
|
340
|
+
clip-path: inset(100% 0 0 0);
|
|
341
|
+
animation: struq-open 0s 3s forwards;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
@keyframes struq-ga-rest { to { --p: 1; } }
|
|
345
|
+
|
|
346
|
+
/* --- panel-accordion --- */
|
|
347
|
+
/* The panels arrive from their bottom edge, so with motion and scripts they wait below it from the first paint; the
|
|
348
|
+
block's script takes over (data-motion) and draws every window itself. If the script never arrives they open after
|
|
349
|
+
3s. Without scripts or under reduced motion nothing is hidden. */
|
|
350
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
351
|
+
:is(.js, .sq-js) [data-blocks] [data-panel-accordion]:not([data-motion]) [data-panel] {
|
|
352
|
+
clip-path: inset(100% 0 0 0);
|
|
353
|
+
animation: struq-open 0s 3s forwards;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/* --- hero-poster --- */
|
|
358
|
+
/* With scripts and motion the poster's first paint is the moment before its entrance: the lines wait below their
|
|
359
|
+
masks and the window is a hairline. The block's script takes over (data-live) and plays the entrance; if it never
|
|
360
|
+
arrives everything opens after 3s. Without scripts or under reduced motion the composed poster shows at once. */
|
|
361
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
362
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-poster]:not([data-live]) [data-hp-line] {
|
|
363
|
+
translate: 0 112%;
|
|
364
|
+
animation: struq-hp-rise 0s 3s forwards;
|
|
365
|
+
}
|
|
366
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-poster]:not([data-live]) [data-hp-slot] {
|
|
367
|
+
clip-path: inset(50% 0 50% 0);
|
|
368
|
+
animation: struq-open 0s 3s forwards;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
@keyframes struq-hp-rise { to { translate: 0 0; } }
|
|
372
|
+
|
|
373
|
+
/* --- hero-poster variants --- */
|
|
374
|
+
/* column: the slit's place as shares of the screen (its left edge, its right margin) and the band of the foot line
|
|
375
|
+
below it; the script writes the band in pixels once it has measured the foot. The slit's box is what a stack
|
|
376
|
+
preloader lands on; the cut layer inside it is the whole screen at the screen's origin, clipped to the slit, and
|
|
377
|
+
holds the photo, its light veil and the copy of the type. With scripts and motion the slit waits closed at the top
|
|
378
|
+
edge and draws down with the entrance, or after 3s. The stack needs nothing here: its windows use the gate above. */
|
|
379
|
+
[data-blocks] [data-hero-poster][data-hp-variant="column"] {
|
|
380
|
+
--hp-cl: 0.6;
|
|
381
|
+
--hp-cr: 0.24;
|
|
382
|
+
--hp-cb: calc(5svh + 1.4rem);
|
|
383
|
+
}
|
|
384
|
+
@media (max-width: 767px) {
|
|
385
|
+
[data-blocks] [data-hero-poster][data-hp-variant="column"] {
|
|
386
|
+
--hp-cl: 0.56;
|
|
387
|
+
--hp-cr: 0.18;
|
|
388
|
+
--hp-cb: 6.5rem;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
[data-blocks] [data-hero-poster] [data-hp-cutslot] {
|
|
392
|
+
position: absolute;
|
|
393
|
+
top: 0;
|
|
394
|
+
left: calc(var(--hp-cl) * 100%);
|
|
395
|
+
right: calc(var(--hp-cr) * 100%);
|
|
396
|
+
bottom: var(--hp-cb);
|
|
397
|
+
z-index: 10;
|
|
398
|
+
}
|
|
399
|
+
[data-blocks] [data-hero-poster] [data-hp-cut] {
|
|
400
|
+
position: absolute;
|
|
401
|
+
top: 0;
|
|
402
|
+
left: calc(-100% * var(--hp-cl) / (1 - var(--hp-cl) - var(--hp-cr)));
|
|
403
|
+
width: calc(100% / (1 - var(--hp-cl) - var(--hp-cr)));
|
|
404
|
+
height: calc(100% + var(--hp-cb));
|
|
405
|
+
clip-path: inset(0 calc(var(--hp-cr) * 100%) var(--hp-cb) calc(var(--hp-cl) * 100%));
|
|
406
|
+
}
|
|
407
|
+
[data-blocks] [data-hero-poster] [data-hp-veil] {
|
|
408
|
+
background: var(--inverse);
|
|
409
|
+
background: oklch(from var(--surface-0) min(l, 0.2) c h);
|
|
410
|
+
opacity: 0.4;
|
|
411
|
+
}
|
|
412
|
+
[data-blocks] [data-hero-poster] [data-hp-veil][data-tone="light"] {
|
|
413
|
+
background: var(--on-photo);
|
|
414
|
+
}
|
|
415
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
416
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-poster]:not([data-live]) [data-hp-cut] {
|
|
417
|
+
clip-path: inset(0 calc(var(--hp-cr) * 100%) 100% calc(var(--hp-cl) * 100%));
|
|
418
|
+
animation: struq-hp-cut 0s 3s forwards;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
@keyframes struq-hp-cut { to { clip-path: inset(0 calc(var(--hp-cr) * 100%) var(--hp-cb) calc(var(--hp-cl) * 100%)); } }
|
|
422
|
+
|
|
423
|
+
/* --- hero-still --- */
|
|
424
|
+
/* The veil over the photograph is the page ground held dark (its own hue, never black); the block's script tunes its
|
|
425
|
+
strength to what the photo needs (3:1 for the display-size headline, never past 0.45; the caption sits on the
|
|
426
|
+
ground in the ink), and the safe strength here stays when the photo cannot be read or scripts never run: the cap
|
|
427
|
+
on the left, a lighter one when the headline is set on a calm right side. The photograph is laid out six percent larger than its box and shown scaled
|
|
428
|
+
back to fit, so at rest it is exactly an object-cover of the box; the push-in is set up from the first paint, paused
|
|
429
|
+
(so the layer is rastered at its final scale before anything is seen), and runs once the entrance has played. With
|
|
430
|
+
motion and scripts the headline and the caption wait below their masks from the first paint (the composed still is
|
|
431
|
+
the photo and its veil) and arrive after 3s if the script never runs; without scripts nothing waits. */
|
|
432
|
+
[data-blocks] [data-hero-still] [data-veil] {
|
|
433
|
+
background: var(--inverse);
|
|
434
|
+
background: oklch(from var(--surface-0) min(l, 0.2) c h);
|
|
435
|
+
opacity: 0.45;
|
|
436
|
+
transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1);
|
|
437
|
+
}
|
|
438
|
+
[data-blocks] [data-hero-still][data-side="right"] [data-veil] {
|
|
439
|
+
opacity: 0.36;
|
|
440
|
+
}
|
|
441
|
+
[data-blocks] [data-hero-still] [data-push] {
|
|
442
|
+
transform: scale(0.9434);
|
|
443
|
+
will-change: transform;
|
|
444
|
+
}
|
|
445
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
446
|
+
[data-blocks] [data-hero-still] [data-push] {
|
|
447
|
+
animation: struq-hs-push 20s cubic-bezier(0.45, 0, 0.55, 1) 1.2s forwards paused;
|
|
448
|
+
}
|
|
449
|
+
[data-blocks] [data-hero-still][data-pushing] [data-push] {
|
|
450
|
+
animation-play-state: running;
|
|
451
|
+
}
|
|
452
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-still]:not([data-ready]) [data-rise] {
|
|
453
|
+
transform: translateY(115%);
|
|
454
|
+
animation: struq-hs-rise 0s 3s forwards;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
@keyframes struq-hs-push { to { transform: scale(1); } }
|
|
458
|
+
@keyframes struq-hs-rise { to { transform: none; } }
|
|
459
|
+
|
|
460
|
+
/* --- hero-still variants --- */
|
|
461
|
+
/* frame: the photograph inside margins of page ground; the frame's box is what a stack preloader lands on, and the
|
|
462
|
+
photo inside it is the whole still at the still's origin (placed here from the viewport units, measured by the
|
|
463
|
+
script), so the frame shows its part of the still and opening it is a clip. Phones keep the photo's own crop in a
|
|
464
|
+
smaller frame above the headline. horizon: a veil band that a dark ink turns pale, and the lower half of the
|
|
465
|
+
headline waiting above its masks (it drops to the horizon as the upper half rises to it). */
|
|
466
|
+
[data-blocks] [data-hero-still] [data-hs-slot] {
|
|
467
|
+
position: absolute;
|
|
468
|
+
top: 4svh;
|
|
469
|
+
left: 1.5rem;
|
|
470
|
+
right: 1.5rem;
|
|
471
|
+
bottom: 42%;
|
|
472
|
+
overflow: hidden;
|
|
473
|
+
}
|
|
474
|
+
[data-blocks] [data-hero-still] [data-hs-photo] {
|
|
475
|
+
position: absolute;
|
|
476
|
+
inset: 0;
|
|
477
|
+
}
|
|
478
|
+
@media (min-width: 768px) {
|
|
479
|
+
[data-blocks] [data-hero-still] [data-hs-slot] {
|
|
480
|
+
top: 7svh;
|
|
481
|
+
left: 42vw;
|
|
482
|
+
right: 4vw;
|
|
483
|
+
bottom: 3svh;
|
|
484
|
+
}
|
|
485
|
+
[data-blocks] [data-hero-still] [data-hs-photo] {
|
|
486
|
+
inset: auto;
|
|
487
|
+
left: -42vw;
|
|
488
|
+
top: -7svh;
|
|
489
|
+
width: 100vw;
|
|
490
|
+
height: calc(100% + 10svh);
|
|
491
|
+
}
|
|
492
|
+
[data-blocks] [data-hero-still][data-live] [data-hs-slot] {
|
|
493
|
+
overflow: visible;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
[data-blocks] [data-hero-still] [data-veil][data-tone="light"] {
|
|
497
|
+
background: var(--on-photo);
|
|
498
|
+
}
|
|
499
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
500
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-still]:not([data-ready]) [data-below] [data-rise] {
|
|
501
|
+
transform: translateY(-115%);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/* --- hero-index --- */
|
|
506
|
+
/* Each photograph layer carries its own light veil: the page ground held dark (its own hue, never black), tuned by
|
|
507
|
+
the block's script per photograph (none under the dark ink, at most a quarter under the light ink); the safe
|
|
508
|
+
strength here stays when a photograph cannot be read. One ink for the whole list, chosen per photograph and
|
|
509
|
+
switched once the incoming photograph has passed the list; each place line takes its own ink, or stands at the
|
|
510
|
+
foot of the list in the list's ink when neither holds (no box, no scrim, no glow). With scripts on a fine pointer only the
|
|
511
|
+
current project's place shows (at rest, the first one's). With motion and scripts the names wait below their masks
|
|
512
|
+
from the first paint and arrive after 3s if the script never runs; without scripts nothing waits. */
|
|
513
|
+
/* Without scripts nothing can read the photo, so the light ink stands over a veil strong enough for any of them; with
|
|
514
|
+
scripts the veil starts clear and the script writes each photograph's own value before the names rise. */
|
|
515
|
+
[data-blocks] [data-hero-index] [data-veil] {
|
|
516
|
+
background: var(--inverse);
|
|
517
|
+
background: oklch(from var(--surface-0) min(l, 0.2) c h);
|
|
518
|
+
opacity: 0.45;
|
|
519
|
+
transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1);
|
|
520
|
+
}
|
|
521
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-index] [data-veil] {
|
|
522
|
+
opacity: 0;
|
|
523
|
+
}
|
|
524
|
+
[data-blocks] [data-hero-index] [data-list] {
|
|
525
|
+
color: var(--on-photo);
|
|
526
|
+
}
|
|
527
|
+
[data-blocks] [data-hero-index][data-tone="dark"] [data-list] {
|
|
528
|
+
color: var(--inverse);
|
|
529
|
+
color: oklch(from var(--surface-0) min(l, 0.2) c h);
|
|
530
|
+
}
|
|
531
|
+
[data-blocks] [data-hero-index] [data-meta] {
|
|
532
|
+
transition: opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1);
|
|
533
|
+
}
|
|
534
|
+
[data-blocks] [data-hero-index] [data-row][data-place-ink="light"] [data-meta] {
|
|
535
|
+
color: var(--on-photo);
|
|
536
|
+
}
|
|
537
|
+
[data-blocks] [data-hero-index] [data-row][data-place-ink="dark"] [data-meta] {
|
|
538
|
+
color: var(--inverse);
|
|
539
|
+
color: oklch(from var(--surface-0) min(l, 0.2) c h);
|
|
540
|
+
}
|
|
541
|
+
/* A place line that cannot hold its own ink stays out of the row; the script sets it at the foot of the list, in
|
|
542
|
+
the list's ink, while its row is shown. Each place line stands on its own line under its name. */
|
|
543
|
+
[data-blocks] [data-hero-index] [data-row][data-place-ink="foot"] [data-meta] {
|
|
544
|
+
visibility: hidden;
|
|
545
|
+
}
|
|
546
|
+
[data-blocks] [data-hero-index] [data-foot]:not([data-on]) {
|
|
547
|
+
visibility: hidden;
|
|
548
|
+
}
|
|
549
|
+
@media (hover: hover) and (pointer: fine) and (min-width: 1024px) {
|
|
550
|
+
[data-blocks] [data-hero-index][data-ready] [data-meta] {
|
|
551
|
+
opacity: 0;
|
|
552
|
+
}
|
|
553
|
+
[data-blocks] [data-hero-index][data-ready] [data-row][data-current] [data-meta],
|
|
554
|
+
[data-blocks] [data-hero-index][data-ready] [data-list]:not([data-active]) li:first-child [data-meta] {
|
|
555
|
+
opacity: 1;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
559
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-index]:not([data-ready]) [data-rise] {
|
|
560
|
+
transform: translateY(115%);
|
|
561
|
+
animation: struq-hi-rise 0s 3s forwards;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
@keyframes struq-hi-rise { to { transform: none; } }
|
|
565
|
+
|
|
566
|
+
/* --- hero-split --- */
|
|
567
|
+
/* With motion and scripts the type waits below its masks from the first paint (the composed still is the photograph
|
|
568
|
+
and the ground) and arrives with the entrance; if the script never runs it arrives after 3s, and the block's script
|
|
569
|
+
reads this clock to leave type it has already shown in place. Only the first word and the first photograph show
|
|
570
|
+
without scripts; the others wait below their mask and behind their clip. Each photograph carries a light veil
|
|
571
|
+
(never past 0.3), of the ground held dark under the photo ink or of the ground under the ground's ink, whichever
|
|
572
|
+
the script finds weaker for that photo; the safe one here stays when a photo cannot be read or scripts never run,
|
|
573
|
+
and a re-grade eases. Phones carry no veil: the headline is not on the photo there. */
|
|
574
|
+
[data-blocks] [data-hero-split] [data-veil] {
|
|
575
|
+
background: var(--inverse);
|
|
576
|
+
background: oklch(from var(--surface-0) min(l, 0.2) c h);
|
|
577
|
+
opacity: 0.3;
|
|
578
|
+
transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1);
|
|
579
|
+
}
|
|
580
|
+
@media (max-width: 767px) {
|
|
581
|
+
[data-blocks] [data-hero-split] [data-veil] {
|
|
582
|
+
opacity: 0 !important;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
586
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-split]:not([data-ready]) [data-rise] {
|
|
587
|
+
transform: translateY(115%);
|
|
588
|
+
animation: struq-hsp-rise 0s 3s forwards;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
@keyframes struq-hsp-rise { to { transform: none; } }
|
|
592
|
+
|
|
593
|
+
/* --- hero-framed --- */
|
|
594
|
+
/* With scripts and motion the wall's first paint is the moment before its entrance: the marks undrawn, the
|
|
595
|
+
photograph a hairline in its frame, the label below its masks. The block's script takes over (data-live); if it
|
|
596
|
+
never arrives the wall is whole after 3s. The copy that stands on the open photograph waits below its masks for the
|
|
597
|
+
scroll. Without scripts or under reduced motion the composed wall shows at once. */
|
|
598
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
599
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-framed]:not([data-live]) [data-hf-rise] {
|
|
600
|
+
translate: 0 140%;
|
|
601
|
+
animation: struq-hf-rise 0s 3s forwards;
|
|
602
|
+
}
|
|
603
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-framed]:not([data-live]) [data-hf-mark] > [data-arm="h"] {
|
|
604
|
+
transform: scaleX(0);
|
|
605
|
+
animation: struq-hf-arm 0s 3s forwards;
|
|
606
|
+
}
|
|
607
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-framed]:not([data-live]) [data-hf-mark] > [data-arm="v"] {
|
|
608
|
+
transform: scaleY(0);
|
|
609
|
+
animation: struq-hf-arm 0s 3s forwards;
|
|
610
|
+
}
|
|
611
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-framed]:not([data-live]) [data-hf-frame] > div:first-child {
|
|
612
|
+
clip-path: inset(50% 0 50% 0);
|
|
613
|
+
animation: struq-open 0s 3s forwards;
|
|
614
|
+
}
|
|
615
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-framed]:not([data-live]) [data-hf-copy-rise] {
|
|
616
|
+
translate: 0 140%;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
@keyframes struq-hf-rise { to { translate: 0 0; } }
|
|
620
|
+
@keyframes struq-hf-arm { to { transform: none; } }
|
|
621
|
+
|
|
622
|
+
/* --- heroes: light display weights --- */
|
|
623
|
+
/* Roy, 2026-11-07: "make sure we use light font weights, we sometimes use quite thick font weights in the hero
|
|
624
|
+
section". A hero's display type never goes heavier than 300, whatever the pairing asks for (display-drama asks
|
|
625
|
+
700, condensed-poster 800). A face without a 300 falls to its lightest loaded weight. A new hero adds its root
|
|
626
|
+
attribute here. */
|
|
627
|
+
[data-blocks] :is([data-hero-still], [data-hero-poster], [data-hero-index], [data-hero-split], [data-hero-framed], [data-bg-aurora], [data-bg-silk], [data-bg-facets], [data-showpiece="hero-opener"]) :is(h1, h2, h3, .font-display) {
|
|
628
|
+
font-weight: min(var(--display-weight, 300), 300);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/* --- background-aurora --- */
|
|
632
|
+
/* The field's canvas stays hidden until the block has drawn its first frame (data-lit), then fades in while the
|
|
633
|
+
light dawns; without scripts it never shows and the hero stands on the plain ground. With scripts and motion the
|
|
634
|
+
type waits below its masks and is released after 3s if the script never runs. */
|
|
635
|
+
[data-blocks] [data-bg-aurora] [data-field] {
|
|
636
|
+
opacity: 0;
|
|
637
|
+
}
|
|
638
|
+
[data-blocks] [data-bg-aurora][data-lit] [data-field] {
|
|
639
|
+
opacity: 1;
|
|
640
|
+
}
|
|
641
|
+
/* With reduced motion the held still simply appears. */
|
|
642
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
643
|
+
[data-blocks] [data-bg-aurora] [data-field] {
|
|
644
|
+
transition: opacity 1.4s cubic-bezier(0.16, 1, 0.3, 1);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
648
|
+
:is(.js, .sq-js) [data-blocks] [data-bg-aurora]:not([data-ready]) [data-rise] {
|
|
649
|
+
transform: translateY(115%);
|
|
650
|
+
animation: struq-bga-rise 0s 3s forwards;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
@keyframes struq-bga-rise { to { transform: none; } }
|
|
654
|
+
|
|
655
|
+
/* --- background-facets --- */
|
|
656
|
+
/* The surface's canvas waits invisible until its first frame (the flat ground) is drawn, then fades in
|
|
657
|
+
(data-drawn); without scripts or WebGL the hero is the plain ground with its type. The copy stands on the calm side
|
|
658
|
+
of each variant: left for shard, bottom left for relief, bottom right for turn, and at the bottom on phones. With
|
|
659
|
+
motion and scripts the lines wait below their masks from the first paint and arrive after 3s if the script never
|
|
660
|
+
runs; without scripts nothing waits. */
|
|
661
|
+
[data-blocks] [data-bg-facets] [data-bf-canvas] {
|
|
662
|
+
display: block;
|
|
663
|
+
opacity: 0;
|
|
664
|
+
transition: opacity 1.2s cubic-bezier(0.22, 1, 0.36, 1);
|
|
665
|
+
}
|
|
666
|
+
[data-blocks] [data-bg-facets][data-drawn] [data-bf-canvas] {
|
|
667
|
+
opacity: 1;
|
|
668
|
+
}
|
|
669
|
+
[data-blocks] [data-bg-facets] [data-copy] {
|
|
670
|
+
left: 24px;
|
|
671
|
+
right: 24px;
|
|
672
|
+
bottom: 9svh;
|
|
673
|
+
}
|
|
674
|
+
[data-blocks] [data-bg-facets] h1 {
|
|
675
|
+
font-size: clamp(2.75rem, 12.5vw, 4.25rem);
|
|
676
|
+
}
|
|
677
|
+
@media (min-width: 768px) {
|
|
678
|
+
[data-blocks] [data-bg-facets] [data-copy] {
|
|
679
|
+
left: 6vw;
|
|
680
|
+
right: auto;
|
|
681
|
+
width: min(46vw, 44rem);
|
|
682
|
+
}
|
|
683
|
+
[data-blocks] [data-bg-facets][data-variant="shard"] [data-copy] {
|
|
684
|
+
bottom: 15svh;
|
|
685
|
+
}
|
|
686
|
+
[data-blocks] [data-bg-facets][data-variant="turn"] [data-copy] {
|
|
687
|
+
left: 58vw;
|
|
688
|
+
width: 36vw;
|
|
689
|
+
bottom: 11svh;
|
|
690
|
+
}
|
|
691
|
+
[data-blocks] [data-bg-facets] h1 {
|
|
692
|
+
font-size: clamp(3.5rem, 6.6vw, 8rem);
|
|
693
|
+
}
|
|
694
|
+
[data-blocks] [data-bg-facets][data-variant="turn"] h1 {
|
|
695
|
+
font-size: clamp(3.25rem, 5.4vw, 6.5rem);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
@media (prefers-reduced-motion: reduce) {
|
|
699
|
+
[data-blocks] [data-bg-facets] [data-bf-canvas] {
|
|
700
|
+
transition: none;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
704
|
+
:is(.js, .sq-js) [data-blocks] [data-bg-facets]:not([data-ready]) [data-rise] {
|
|
705
|
+
transform: translateY(115%);
|
|
706
|
+
animation: struq-bf-rise 0s 3s forwards;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
@keyframes struq-bf-rise { to { transform: none; } }
|
|
710
|
+
|
|
711
|
+
/* --- background-silk --- */
|
|
712
|
+
/* The cloth's canvas is made by the script, so without scripts or WebGL the hero is the plain ground with its type.
|
|
713
|
+
The canvas waits invisible until its first frame is drawn, then fades in (data-live). With motion and scripts the
|
|
714
|
+
type waits below its masks from the first paint and arrives after 3s if the script never runs; without scripts
|
|
715
|
+
nothing waits. */
|
|
716
|
+
[data-blocks] [data-bg-silk] [data-field] > canvas {
|
|
717
|
+
position: absolute;
|
|
718
|
+
inset: 0;
|
|
719
|
+
display: block;
|
|
720
|
+
width: 100%;
|
|
721
|
+
height: 100%;
|
|
722
|
+
opacity: 0;
|
|
723
|
+
transition: opacity 1.6s cubic-bezier(0.22, 1, 0.36, 1);
|
|
724
|
+
}
|
|
725
|
+
[data-blocks] [data-bg-silk][data-live] [data-field] > canvas {
|
|
726
|
+
opacity: 1;
|
|
727
|
+
}
|
|
728
|
+
@media (prefers-reduced-motion: reduce) {
|
|
729
|
+
[data-blocks] [data-bg-silk] [data-field] > canvas {
|
|
730
|
+
transition: none;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
734
|
+
:is(.js, .sq-js) [data-blocks] [data-bg-silk]:not([data-ready]) [data-rise] {
|
|
735
|
+
transform: translateY(115%);
|
|
736
|
+
animation: struq-silk-rise 0s 3s forwards;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
@keyframes struq-silk-rise { to { transform: none; } }
|
|
740
|
+
|
|
741
|
+
/* --- reveals --- */
|
|
742
|
+
/* The card variant's top edge: a hairline and a short soft shadow, both from the ground held dark (its own hue, never
|
|
743
|
+
black); browsers without relative colour keep the hairline. The shade above the card is the dark that falls over the
|
|
744
|
+
section it covers; it starts clear and the block's scroll writes its strength. With motion and scripts every start
|
|
745
|
+
state waits from the first paint (the statement unseen until it is cut into lines, the paragraph and the link and
|
|
746
|
+
the card's content clear, the photograph's clip shut) and arrives after 3s if the script never runs; without scripts
|
|
747
|
+
nothing waits. */
|
|
748
|
+
[data-blocks] [data-reveals="card"] {
|
|
749
|
+
box-shadow: 0 -1px 0 var(--line);
|
|
750
|
+
box-shadow: 0 -1px 0 var(--line), 0 -22px 44px -22px oklch(from var(--surface-0) calc(l * 0.3) c h / 0.3);
|
|
751
|
+
}
|
|
752
|
+
[data-blocks] [data-reveals] [data-rv-shade] {
|
|
753
|
+
background: var(--inverse);
|
|
754
|
+
background: oklch(from var(--surface-0) calc(l * 0.3) c h);
|
|
755
|
+
opacity: 0;
|
|
756
|
+
}
|
|
757
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
758
|
+
:is(.js, .sq-js) [data-blocks] [data-reveals]:not([data-ready]) [data-rv-type] {
|
|
759
|
+
visibility: hidden;
|
|
760
|
+
animation: struq-show 0s 3s forwards;
|
|
761
|
+
}
|
|
762
|
+
:is(.js, .sq-js) [data-blocks] [data-reveals]:not([data-ready]) :is([data-rv-soft], [data-rv-settle]) {
|
|
763
|
+
opacity: 0;
|
|
764
|
+
animation: struq-fade 0s 3s forwards;
|
|
765
|
+
}
|
|
766
|
+
:is(.js, .sq-js) [data-blocks] [data-reveals="lines"]:not([data-ready]) [data-rv-frame] {
|
|
767
|
+
clip-path: inset(100% 0% 0% 0%);
|
|
768
|
+
animation: struq-open 0s 3s forwards;
|
|
769
|
+
}
|
|
770
|
+
:is(.js, .sq-js) [data-blocks] [data-reveals="clip"]:not([data-ready]) [data-rv-frame] {
|
|
771
|
+
clip-path: inset(49.6% 7% 49.6% 7%);
|
|
772
|
+
animation: struq-open 0s 3s forwards;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/* --- preloader-wordmark --- */
|
|
777
|
+
/* The cover is never painted unless its own inline script, which runs while the cover is parsed, armed it: so without
|
|
778
|
+
JS and on a session revisit it covers nothing. The name stays unseen until a second inline script has loaded its
|
|
779
|
+
face and fitted it to the width (data-fit), so it is never drawn in a fallback. From the first paint the fill runs
|
|
780
|
+
on one number, --wm-p, on a CSS clock; the block's script takes the number over where it stands (data-live), which
|
|
781
|
+
stops this clock and its failsafe. If the script never arrives, the cover fades from 1.95s and is hidden at 2.3s,
|
|
782
|
+
and everything in it inherits that, so a failed bundle never leaves it over the hero. Reduced motion: the name is
|
|
783
|
+
filled from the start and the cover fades sooner. */
|
|
784
|
+
@property --wm-p {
|
|
785
|
+
syntax: "<number>";
|
|
786
|
+
inherits: true;
|
|
787
|
+
initial-value: 0;
|
|
788
|
+
}
|
|
789
|
+
[data-blocks] [data-preloader="wordmark"] {
|
|
790
|
+
display: none;
|
|
791
|
+
}
|
|
792
|
+
/* Nothing in the cover transitions: the fit measures in the same frame it sets a size, and a site-wide reduced-motion
|
|
793
|
+
rule that gives every element a tiny transition would otherwise hand it the old size. */
|
|
794
|
+
[data-blocks] :is([data-preloader="wordmark"], [data-preloader="wordmark"] *) {
|
|
795
|
+
transition-property: none;
|
|
796
|
+
}
|
|
797
|
+
[data-blocks] [data-preloader="wordmark"][data-armed] {
|
|
798
|
+
display: block;
|
|
799
|
+
animation:
|
|
800
|
+
struq-wm-fill 1.5s linear forwards,
|
|
801
|
+
struq-wm-fade 0.35s cubic-bezier(0.33, 1, 0.68, 1) 1.95s forwards,
|
|
802
|
+
struq-wm-gone 0s linear 2.3s forwards;
|
|
803
|
+
}
|
|
804
|
+
[data-blocks] [data-preloader="wordmark"][data-armed]:not([data-fit]) [data-name] {
|
|
805
|
+
visibility: hidden;
|
|
806
|
+
}
|
|
807
|
+
[data-blocks] [data-preloader="wordmark"][data-live] {
|
|
808
|
+
animation: none;
|
|
809
|
+
}
|
|
810
|
+
@media (prefers-reduced-motion: reduce) {
|
|
811
|
+
[data-blocks] [data-preloader="wordmark"][data-armed] {
|
|
812
|
+
--wm-p: 1;
|
|
813
|
+
animation:
|
|
814
|
+
struq-wm-fade 0.3s ease-out 1.1s forwards,
|
|
815
|
+
struq-wm-gone 0s linear 1.4s forwards;
|
|
816
|
+
}
|
|
817
|
+
[data-blocks] [data-preloader="wordmark"][data-live] {
|
|
818
|
+
animation: none;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
/* The name and the header's wordmark it settles into share one face, weight and tracking, so the hand-over is exact:
|
|
822
|
+
the display face at a light weight (300, or the pairing's own if lighter), no kerning or ligatures, since the
|
|
823
|
+
preloader sets each letter in a cell of its own. */
|
|
824
|
+
[data-blocks] :is([data-preloader="wordmark"] [data-name], [data-wordmark-target]) {
|
|
825
|
+
font-family: var(--font-display);
|
|
826
|
+
font-weight: min(var(--display-weight, 300), 300);
|
|
827
|
+
letter-spacing: -0.03em;
|
|
828
|
+
font-kerning: none;
|
|
829
|
+
font-variant-ligatures: none;
|
|
830
|
+
}
|
|
831
|
+
[data-blocks] [data-preloader="wordmark"] [data-name] {
|
|
832
|
+
display: flex;
|
|
833
|
+
flex: none;
|
|
834
|
+
white-space: nowrap;
|
|
835
|
+
font-size: var(--wm-size, 22vw);
|
|
836
|
+
line-height: 1;
|
|
837
|
+
/* The last letter's tracking is taken back, so the box holds its whole glyph and centres it. */
|
|
838
|
+
padding-inline-end: 0.03em;
|
|
839
|
+
color: var(--wm-ink, var(--ink));
|
|
840
|
+
}
|
|
841
|
+
/* Letters sit above every column of the cover, so a glyph's overhang is never cut by a neighbour's column. */
|
|
842
|
+
[data-blocks] [data-preloader="wordmark"] [data-fitbox] {
|
|
843
|
+
z-index: 1;
|
|
844
|
+
}
|
|
845
|
+
[data-blocks] [data-preloader="wordmark"] [data-l] {
|
|
846
|
+
display: grid;
|
|
847
|
+
/* Each letter fills a little after the one before: the whole name spans 1.6 of --wm-p. */
|
|
848
|
+
--f: clamp(0, var(--wm-p) * 1.6 - var(--wm-lag, 0) * var(--i, 0), 1);
|
|
849
|
+
}
|
|
850
|
+
/* Both copies of a letter in one cell, trimmed to the cap height and the baseline where the browser can, so the name
|
|
851
|
+
centres on its cap band. Where it cannot (Firefox), the cell is the whole line box and the script's measures say
|
|
852
|
+
where the ink and the cap band sit in it instead. */
|
|
853
|
+
[data-blocks] [data-preloader="wordmark"] [data-l] > * {
|
|
854
|
+
grid-area: 1 / 1;
|
|
855
|
+
}
|
|
856
|
+
@supports (text-box: trim-both cap alphabetic) {
|
|
857
|
+
[data-blocks] [data-preloader="wordmark"] [data-l] > * {
|
|
858
|
+
text-box: trim-both cap alphabetic;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
[data-blocks] [data-preloader="wordmark"] [data-faint] {
|
|
862
|
+
color: color-mix(in oklch, var(--wm-ink, var(--ink)) 15%, transparent);
|
|
863
|
+
}
|
|
864
|
+
/* The ink rises over the letter's own measured ink, from its lowest point (--wm-b, in the cell) to its highest
|
|
865
|
+
(--wm-a), with 0.02em of air at each end: shut at --f 0, the whole glyph at --f 1 and not before, so the last letter
|
|
866
|
+
completes as the fill does. */
|
|
867
|
+
[data-blocks] [data-preloader="wordmark"] [data-ink] {
|
|
868
|
+
clip-path: inset(
|
|
869
|
+
calc(var(--wm-b) + 0.02em + (var(--wm-a) - var(--wm-b) - 0.04em) * var(--f)) -0.12em calc(100% - var(--wm-b) - 0.02em) -0.12em
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
@keyframes struq-wm-fill {
|
|
873
|
+
0% {
|
|
874
|
+
--wm-p: 0;
|
|
875
|
+
animation-timing-function: cubic-bezier(0.45, 0, 0.55, 1);
|
|
876
|
+
}
|
|
877
|
+
80% {
|
|
878
|
+
--wm-p: 0.85;
|
|
879
|
+
animation-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
|
|
880
|
+
}
|
|
881
|
+
100% {
|
|
882
|
+
--wm-p: 1;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
@keyframes struq-wm-fade { to { opacity: 0; } }
|
|
886
|
+
@keyframes struq-wm-gone { to { visibility: hidden; } }
|
|
887
|
+
|
|
888
|
+
/* --- menu-curtain --- */
|
|
889
|
+
/* The menu's ground is the gate's deep one (a step darker than the page in every palette, so the menu reads as the
|
|
890
|
+
room the page is shown in); the slices take their own, as in page-transition-slices. Browsers without relative
|
|
891
|
+
colour take the inverse. The bar's height is shared with the page, which keeps its hero type below it. */
|
|
892
|
+
[data-blocks] [data-menu-curtain] {
|
|
893
|
+
--menu-bar-h: 4rem;
|
|
894
|
+
--menu: var(--inverse);
|
|
895
|
+
--on-menu: var(--on-inverse);
|
|
896
|
+
--on-menu-muted: color-mix(in oklch, var(--on-menu) 70%, var(--menu));
|
|
897
|
+
}
|
|
898
|
+
@media (min-width: 768px) {
|
|
899
|
+
[data-blocks] [data-menu-curtain] {
|
|
900
|
+
--menu-bar-h: 4.5rem;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
@supports (color: oklch(from red l c h)) {
|
|
904
|
+
[data-blocks] [data-menu-curtain] {
|
|
905
|
+
--menu: oklch(from var(--surface-0) min(calc(l * 0.55), 0.2) c h);
|
|
906
|
+
--on-menu: var(--on-photo);
|
|
907
|
+
}
|
|
908
|
+
[data-blocks] [data-menu-curtain][data-variant="slices"] {
|
|
909
|
+
--menu: oklch(from var(--surface-0) min(l + 0.07, 0.27) c h);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
/* Large type in the first screen stays light, as in the heroes. */
|
|
913
|
+
[data-blocks] [data-menu-curtain] .font-display {
|
|
914
|
+
font-weight: min(var(--display-weight, 300), 300);
|
|
915
|
+
}
|
|
916
|
+
[data-blocks] [data-menu-curtain] [data-menu-panel] :focus-visible {
|
|
917
|
+
outline-color: var(--on-menu);
|
|
918
|
+
}
|
|
919
|
+
/* The hovered or focused link stands; the others step back. */
|
|
920
|
+
[data-blocks] [data-menu-curtain] [data-menu-link] {
|
|
921
|
+
transition:
|
|
922
|
+
opacity 0.3s var(--ease-out-expo),
|
|
923
|
+
transform 0.45s var(--ease-out-expo);
|
|
924
|
+
}
|
|
925
|
+
[data-blocks] [data-menu-curtain] [data-menu-list]:is(:hover, :focus-within) [data-menu-link]:not([data-on]) {
|
|
926
|
+
opacity: 0.42;
|
|
927
|
+
}
|
|
928
|
+
[data-blocks] [data-menu-curtain] [data-menu-list]:is(:hover, :focus-within) [data-menu-link][data-on] {
|
|
929
|
+
transform: translateX(0.06em);
|
|
930
|
+
}
|
|
931
|
+
/* The button works only with the script. Without it the bar shows its links in a row; with it but before it runs, the
|
|
932
|
+
button shows for 3 seconds and then gives way to the row if the script never arrived. */
|
|
933
|
+
[data-blocks] [data-menu-curtain] [data-menu-toggle] {
|
|
934
|
+
visibility: hidden;
|
|
935
|
+
}
|
|
936
|
+
.sq-js [data-blocks] [data-menu-curtain]:not([data-ready]) [data-menu-toggle] {
|
|
937
|
+
visibility: inherit;
|
|
938
|
+
animation: struq-mc-gone 0s 3s forwards;
|
|
939
|
+
}
|
|
940
|
+
.sq-js [data-blocks] [data-menu-curtain]:not([data-ready]) [data-menu-fallback] {
|
|
941
|
+
visibility: hidden;
|
|
942
|
+
animation: struq-show 0s 3s forwards;
|
|
943
|
+
}
|
|
944
|
+
[data-blocks] [data-menu-curtain][data-ready] [data-menu-toggle] {
|
|
945
|
+
visibility: inherit;
|
|
946
|
+
}
|
|
947
|
+
[data-blocks] [data-menu-curtain][data-ready] [data-menu-fallback] {
|
|
948
|
+
visibility: hidden;
|
|
949
|
+
}
|
|
950
|
+
@keyframes struq-mc-gone { to { visibility: hidden; } }
|
|
951
|
+
|
|
952
|
+
/* --- footer-reveal --- */
|
|
953
|
+
/* The last section sits above the footer in one wrapper; the footer goes sticky beneath it only once the script is
|
|
954
|
+
live (it sets data-live, and only with motion allowed), so without scripts and under reduced motion the footer simply
|
|
955
|
+
follows the page and nothing waits. --fr-bottom is 0 for a footer that fits the screen and the screen height minus
|
|
956
|
+
the footer's for a taller one, which then sticks by its top. The wrapper clips sideways only (clip, not hidden, so
|
|
957
|
+
the sticky footer still sticks) for the fold's tilt. The edge is a transparent strip in the sheet's foot whose
|
|
958
|
+
hairline and short shadow fall on the footer, from the ground held dark; the shade is the dark the fold lifts off
|
|
959
|
+
the footer. The footer's name never goes heavier than 300. */
|
|
960
|
+
[data-blocks] [data-footer-reveal] {
|
|
961
|
+
position: relative;
|
|
962
|
+
isolation: isolate;
|
|
963
|
+
overflow-x: clip;
|
|
964
|
+
background-color: var(--surface-0);
|
|
965
|
+
}
|
|
966
|
+
[data-blocks] [data-footer-reveal] > [data-fr-sheet] {
|
|
967
|
+
position: relative;
|
|
968
|
+
z-index: 1;
|
|
969
|
+
background-color: var(--surface-0);
|
|
970
|
+
}
|
|
971
|
+
[data-blocks] [data-footer-reveal] > [data-fr-foot] {
|
|
972
|
+
position: relative;
|
|
973
|
+
z-index: 0;
|
|
974
|
+
}
|
|
975
|
+
[data-blocks] [data-footer-reveal][data-live] > [data-fr-foot] {
|
|
976
|
+
position: sticky;
|
|
977
|
+
bottom: var(--fr-bottom, 0px);
|
|
978
|
+
}
|
|
979
|
+
[data-blocks] [data-footer-reveal] [data-fr-edge] {
|
|
980
|
+
position: absolute;
|
|
981
|
+
inset-inline: 0;
|
|
982
|
+
bottom: 0;
|
|
983
|
+
height: 3rem;
|
|
984
|
+
pointer-events: none;
|
|
985
|
+
opacity: 0;
|
|
986
|
+
box-shadow: 0 1px 0 var(--line);
|
|
987
|
+
box-shadow: 0 1px 0 var(--line), 0 26px 44px -20px oklch(from var(--surface-1) calc(l * 0.3) c h / 0.32);
|
|
988
|
+
}
|
|
989
|
+
[data-blocks] [data-footer-reveal] [data-fr-shade] {
|
|
990
|
+
position: absolute;
|
|
991
|
+
inset: 0;
|
|
992
|
+
z-index: 1;
|
|
993
|
+
pointer-events: none;
|
|
994
|
+
opacity: 0;
|
|
995
|
+
}
|
|
996
|
+
/* Only where relative colour works: a fallback colour would brighten a dark footer instead of shading it. */
|
|
997
|
+
@supports (color: oklch(from red l c h)) {
|
|
998
|
+
[data-blocks] [data-footer-reveal] [data-fr-shade] {
|
|
999
|
+
background: oklch(from var(--surface-1) calc(l * 0.3) c h);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
[data-blocks] [data-footer-reveal] [data-fr-name] {
|
|
1003
|
+
font-weight: min(var(--display-weight, 300), 300);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/* --- cta-closing --- */
|
|
1007
|
+
/* The line and the link stay light, whatever the pairing asks, as in the heroes. The line's size is fitted by the
|
|
1008
|
+
block's script to its column with the real face (--cc-size); until then, and without scripts, a clamp that wraps. */
|
|
1009
|
+
[data-blocks] [data-cta-closing] .font-display {
|
|
1010
|
+
font-weight: min(var(--display-weight, 300), 300);
|
|
1011
|
+
}
|
|
1012
|
+
[data-blocks] [data-cta-closing] [data-cc-head] {
|
|
1013
|
+
font-size: var(--cc-size, clamp(2.75rem, 1rem + 6vw, 8.5rem));
|
|
1014
|
+
line-height: 0.98;
|
|
1015
|
+
letter-spacing: -0.03em;
|
|
1016
|
+
}
|
|
1017
|
+
[data-blocks] [data-cta-closing][data-fit] [data-cc-text] {
|
|
1018
|
+
white-space: nowrap;
|
|
1019
|
+
}
|
|
1020
|
+
/* photo: the ink and the veil come in two pairs (light ink over the ground held dark, dark ink over the photo's own
|
|
1021
|
+
light), chosen and graded by the block's script from the photograph; the safe pair stays when it cannot be read.
|
|
1022
|
+
Browsers without relative colour take the inverse for the dark. */
|
|
1023
|
+
[data-blocks] [data-cta-closing="photo"] {
|
|
1024
|
+
--cc-dark: var(--inverse);
|
|
1025
|
+
--cc-light: var(--on-photo);
|
|
1026
|
+
}
|
|
1027
|
+
@supports (color: oklch(from red l c h)) {
|
|
1028
|
+
[data-blocks] [data-cta-closing="photo"] {
|
|
1029
|
+
--cc-dark: oklch(from var(--surface-0) min(l, 0.2) c h);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
[data-blocks] [data-cta-closing="photo"] [data-cc-veil] {
|
|
1033
|
+
background: var(--cc-dark);
|
|
1034
|
+
opacity: 0.4;
|
|
1035
|
+
}
|
|
1036
|
+
[data-blocks] [data-cta-closing="photo"] [data-cc-ink] {
|
|
1037
|
+
color: var(--cc-light);
|
|
1038
|
+
}
|
|
1039
|
+
[data-blocks] [data-cta-closing="photo"][data-ink="dark"] [data-cc-veil] {
|
|
1040
|
+
background: var(--cc-light);
|
|
1041
|
+
}
|
|
1042
|
+
[data-blocks] [data-cta-closing="photo"][data-ink="dark"] [data-cc-ink] {
|
|
1043
|
+
color: var(--cc-dark);
|
|
1044
|
+
}
|
|
1045
|
+
[data-blocks] [data-cta-closing="photo"] [data-cc-link]:focus-visible {
|
|
1046
|
+
outline-color: currentColor;
|
|
1047
|
+
}
|
|
1048
|
+
@media (min-width: 768px) {
|
|
1049
|
+
[data-blocks] [data-cta-closing="photo"][data-side="right"] [data-cc-copy] {
|
|
1050
|
+
margin-left: auto;
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
/* The copy stands at the bottom, or at the top when the read of the photograph finds the top calmer. */
|
|
1054
|
+
[data-blocks] [data-cta-closing="photo"][data-v="top"] [data-cc-stage] {
|
|
1055
|
+
justify-content: flex-start;
|
|
1056
|
+
}
|
|
1057
|
+
/* marquee: the band is a backdrop, not a second voice: the muted ink drawn far toward the ground (about 1.7:1; it is
|
|
1058
|
+
decoration and hidden from screen readers), so the link in the full ink reads in front of it without any halo. */
|
|
1059
|
+
[data-blocks] [data-cta-closing="marquee"] [data-cc-band] {
|
|
1060
|
+
color: var(--ink-muted);
|
|
1061
|
+
color: color-mix(in oklch, var(--ink-muted) 34%, var(--surface-0));
|
|
1062
|
+
}
|
|
1063
|
+
/* pointer: the project frame is shut until the pointer or the focus opens it. */
|
|
1064
|
+
[data-blocks] [data-cta-closing="pointer"] [data-cc-float] {
|
|
1065
|
+
clip-path: inset(0 0 100% 0);
|
|
1066
|
+
}
|
|
1067
|
+
/* Every photograph waits unseen until it is decoded, and shows after 3 s whatever happens. */
|
|
1068
|
+
[data-blocks] [data-cta-closing] [data-cc-pic] {
|
|
1069
|
+
transition: opacity 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
|
1070
|
+
}
|
|
1071
|
+
:is(.js, .sq-js) [data-blocks] [data-cta-closing] [data-cc-pic]:not([data-shown]) {
|
|
1072
|
+
opacity: 0;
|
|
1073
|
+
animation: struq-fade 0s 3s forwards;
|
|
1074
|
+
}
|
|
1075
|
+
/* The photo variant's copy waits for the first read of the photograph (it sets the side and the ink). */
|
|
1076
|
+
:is(.js, .sq-js) [data-blocks] [data-cta-closing="photo"]:not([data-graded]) [data-cc-copy] {
|
|
1077
|
+
opacity: 0;
|
|
1078
|
+
animation: struq-fade 0s 3s forwards;
|
|
1079
|
+
}
|
|
1080
|
+
/* With motion and scripts every start state waits from the first paint (the lines and the band unseen, the link clear,
|
|
1081
|
+
the photograph's clip shut) and arrives after 3 s if the script never runs; without scripts nothing waits. */
|
|
1082
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
1083
|
+
:is(.js, .sq-js) [data-blocks] [data-cta-closing]:not([data-ready]) [data-cc-rise] {
|
|
1084
|
+
visibility: hidden;
|
|
1085
|
+
animation: struq-show 0s 3s forwards;
|
|
1086
|
+
}
|
|
1087
|
+
:is(.js, .sq-js) [data-blocks] [data-cta-closing]:not([data-ready]) [data-cc-soft] {
|
|
1088
|
+
opacity: 0;
|
|
1089
|
+
animation: struq-fade 0s 3s forwards;
|
|
1090
|
+
}
|
|
1091
|
+
:is(.js, .sq-js) [data-blocks] [data-cta-closing="photo"]:not([data-ready]) [data-cc-frame] {
|
|
1092
|
+
clip-path: inset(50% 50% 50% 50%);
|
|
1093
|
+
animation: struq-open 0s 3s forwards;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
/* --- footer-contact --- */
|
|
1098
|
+
/* Display type stays light (300, or the pairing's own if lighter); the column headings are in the body face. The mail
|
|
1099
|
+
address rolls per character: each one sits in its own mask with a copy below it, and hover or keyboard focus lifts
|
|
1100
|
+
the pair by one line, each after its own delay (--d, written by the script from the pointer's side). Plain links
|
|
1101
|
+
draw an underline from the side in --fc-o. With scripts the time and the open marker are unseen until the time is
|
|
1102
|
+
known; without them they are left out and the clock's slot shows the opening hours (a noscript line), never a
|
|
1103
|
+
made-up time. The fitted address and clock are unseen until measured with their face, and show after 3s if the
|
|
1104
|
+
script never runs. With motion and scripts every entrance start state waits from the first paint, with the same 3s
|
|
1105
|
+
way out. */
|
|
1106
|
+
/* The footer clips what hangs below it: the waiting copies of the rolling characters would otherwise lengthen the
|
|
1107
|
+
page by a line. */
|
|
1108
|
+
[data-blocks] [data-footer-contact] {
|
|
1109
|
+
overflow: clip;
|
|
1110
|
+
}
|
|
1111
|
+
[data-blocks] [data-footer-contact] :is(h2, .font-display) {
|
|
1112
|
+
font-weight: min(var(--display-weight, 300), 300);
|
|
1113
|
+
}
|
|
1114
|
+
[data-blocks] [data-footer-contact] h3 {
|
|
1115
|
+
font-family: inherit;
|
|
1116
|
+
font-weight: inherit;
|
|
1117
|
+
}
|
|
1118
|
+
[data-blocks] [data-footer-contact] a {
|
|
1119
|
+
color: inherit;
|
|
1120
|
+
text-decoration: none;
|
|
1121
|
+
}
|
|
1122
|
+
[data-blocks] [data-footer-contact] [data-fc-rule] {
|
|
1123
|
+
transform-origin: left center;
|
|
1124
|
+
}
|
|
1125
|
+
[data-blocks] [data-footer-contact="address"] [data-fc-address] {
|
|
1126
|
+
display: block;
|
|
1127
|
+
width: max-content;
|
|
1128
|
+
max-width: none;
|
|
1129
|
+
font-size: min(7vw, 30svh);
|
|
1130
|
+
line-height: 1.2;
|
|
1131
|
+
letter-spacing: -0.03em;
|
|
1132
|
+
white-space: nowrap;
|
|
1133
|
+
}
|
|
1134
|
+
@media (max-width: 639px) {
|
|
1135
|
+
[data-blocks] [data-footer-contact="address"] [data-fc-address]:not([data-lines]) {
|
|
1136
|
+
font-size: 10.5vw;
|
|
1137
|
+
}
|
|
1138
|
+
[data-blocks] [data-footer-contact="address"] [data-fc-address]:not([data-lines]) [data-fc-part] {
|
|
1139
|
+
display: block;
|
|
1140
|
+
width: max-content;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
[data-blocks] [data-footer-contact="address"] [data-fc-address][data-lines="2"] [data-fc-part] {
|
|
1144
|
+
display: block;
|
|
1145
|
+
width: max-content;
|
|
1146
|
+
}
|
|
1147
|
+
[data-blocks] [data-footer-contact] :is([data-fc-address], [data-fc-clock]) {
|
|
1148
|
+
font-kerning: none;
|
|
1149
|
+
font-variant-ligatures: none;
|
|
1150
|
+
}
|
|
1151
|
+
[data-blocks] [data-footer-contact] [data-fc-part] {
|
|
1152
|
+
display: inline-block;
|
|
1153
|
+
white-space: nowrap;
|
|
1154
|
+
clip-path: inset(0 -0.3em);
|
|
1155
|
+
}
|
|
1156
|
+
[data-blocks] [data-footer-contact] [data-fc-ch] {
|
|
1157
|
+
display: inline-block;
|
|
1158
|
+
position: relative;
|
|
1159
|
+
white-space: pre;
|
|
1160
|
+
clip-path: inset(0 -0.2em);
|
|
1161
|
+
}
|
|
1162
|
+
[data-blocks] [data-footer-contact] [data-fc-enter] {
|
|
1163
|
+
display: block;
|
|
1164
|
+
}
|
|
1165
|
+
[data-blocks] [data-footer-contact] [data-fc-roll] {
|
|
1166
|
+
display: block;
|
|
1167
|
+
position: relative;
|
|
1168
|
+
transition: transform 0.75s var(--ease-out-expo) var(--d, 0s);
|
|
1169
|
+
}
|
|
1170
|
+
[data-blocks] [data-footer-contact] [data-fc-roll] > span {
|
|
1171
|
+
display: block;
|
|
1172
|
+
}
|
|
1173
|
+
[data-blocks] [data-footer-contact] [data-fc-roll] > [data-fc-copy] {
|
|
1174
|
+
position: absolute;
|
|
1175
|
+
left: 0;
|
|
1176
|
+
top: 100%;
|
|
1177
|
+
}
|
|
1178
|
+
[data-blocks] [data-footer-contact] [data-fc-roll-host]:is(:hover, :focus-visible) [data-fc-roll] {
|
|
1179
|
+
transform: translateY(-100%);
|
|
1180
|
+
}
|
|
1181
|
+
[data-blocks] [data-footer-contact] [data-fc-draw]::after {
|
|
1182
|
+
content: "";
|
|
1183
|
+
position: absolute;
|
|
1184
|
+
left: 0;
|
|
1185
|
+
right: 0;
|
|
1186
|
+
bottom: -0.08em;
|
|
1187
|
+
height: 1px;
|
|
1188
|
+
background: currentColor;
|
|
1189
|
+
transform: scaleX(0);
|
|
1190
|
+
transform-origin: var(--fc-o, left) center;
|
|
1191
|
+
transition: transform 0.55s var(--ease-out-expo);
|
|
1192
|
+
}
|
|
1193
|
+
[data-blocks] [data-footer-contact] [data-fc-draw]:is(:hover, :focus-visible)::after {
|
|
1194
|
+
transform: scaleX(1);
|
|
1195
|
+
}
|
|
1196
|
+
[data-blocks] [data-footer-contact] [data-fc-dot] {
|
|
1197
|
+
display: inline-block;
|
|
1198
|
+
flex: none;
|
|
1199
|
+
width: 0.5em;
|
|
1200
|
+
height: 0.5em;
|
|
1201
|
+
border: 1px solid currentColor;
|
|
1202
|
+
border-radius: 50%;
|
|
1203
|
+
}
|
|
1204
|
+
[data-blocks] [data-footer-contact] [data-fc-dot][data-open] {
|
|
1205
|
+
background: currentColor;
|
|
1206
|
+
}
|
|
1207
|
+
[data-blocks] [data-footer-contact] :is([data-fc-fade], [data-fc-dot], [data-fc-clock]) {
|
|
1208
|
+
transition: opacity 0.45s cubic-bezier(0.33, 1, 0.68, 1);
|
|
1209
|
+
}
|
|
1210
|
+
.sq-js [data-blocks] [data-footer-contact] [data-pending] {
|
|
1211
|
+
opacity: 0;
|
|
1212
|
+
}
|
|
1213
|
+
html:not(.sq-js) [data-blocks] [data-footer-contact] [data-pending] {
|
|
1214
|
+
display: none;
|
|
1215
|
+
}
|
|
1216
|
+
/* The clock: hours and minutes each in a mask that clips sideways (they slide out from the colon), every digit in a
|
|
1217
|
+
mask that clips up and down (a changed digit rolls over), the colon breathing once a second. */
|
|
1218
|
+
[data-blocks] [data-footer-contact="clock"] [data-fc-clock] {
|
|
1219
|
+
display: inline-flex;
|
|
1220
|
+
align-items: baseline;
|
|
1221
|
+
justify-content: center;
|
|
1222
|
+
font-size: min(24vw, 40svh);
|
|
1223
|
+
line-height: 1;
|
|
1224
|
+
letter-spacing: -0.04em;
|
|
1225
|
+
font-variant-numeric: tabular-nums lining-nums;
|
|
1226
|
+
white-space: nowrap;
|
|
1227
|
+
}
|
|
1228
|
+
[data-blocks] [data-footer-contact] :is([data-fc-hmask], [data-fc-mmask]) {
|
|
1229
|
+
display: inline-block;
|
|
1230
|
+
clip-path: inset(-0.3em 0);
|
|
1231
|
+
}
|
|
1232
|
+
[data-blocks] [data-footer-contact] [data-fc-colon-wrap] {
|
|
1233
|
+
display: inline-block;
|
|
1234
|
+
padding-inline: 0.04em;
|
|
1235
|
+
}
|
|
1236
|
+
[data-blocks] [data-footer-contact] [data-fc-colon] {
|
|
1237
|
+
display: inline-block;
|
|
1238
|
+
transform: translateY(-0.06em);
|
|
1239
|
+
animation: struq-fc-breathe 1s ease-in-out infinite;
|
|
1240
|
+
}
|
|
1241
|
+
/* Every digit stands in a slot as wide as the face's widest (--fc-dw, measured by the script), centred, so a narrow
|
|
1242
|
+
1 never shifts the colon on the minute; many display faces keep proportional figures whatever tabular-nums asks. */
|
|
1243
|
+
[data-blocks] [data-footer-contact] [data-fc-digit] {
|
|
1244
|
+
display: inline-block;
|
|
1245
|
+
position: relative;
|
|
1246
|
+
width: var(--fc-dw, auto);
|
|
1247
|
+
text-align: center;
|
|
1248
|
+
clip-path: inset(0 -0.12em);
|
|
1249
|
+
}
|
|
1250
|
+
[data-blocks] [data-footer-contact] [data-fc-out] {
|
|
1251
|
+
position: absolute;
|
|
1252
|
+
inset: 0;
|
|
1253
|
+
animation: struq-fc-out 0.8s var(--ease-out-expo) forwards;
|
|
1254
|
+
}
|
|
1255
|
+
[data-blocks] [data-footer-contact] [data-fc-in] {
|
|
1256
|
+
animation: struq-fc-in 0.8s var(--ease-out-expo) both;
|
|
1257
|
+
}
|
|
1258
|
+
@keyframes struq-fc-breathe {
|
|
1259
|
+
0%,
|
|
1260
|
+
100% {
|
|
1261
|
+
opacity: 1;
|
|
1262
|
+
}
|
|
1263
|
+
50% {
|
|
1264
|
+
opacity: 0.22;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
@keyframes struq-fc-out { to { transform: translateY(-100%); } }
|
|
1268
|
+
@keyframes struq-fc-in { from { transform: translateY(100%); } }
|
|
1269
|
+
@keyframes struq-fc-rule { to { transform: none; } }
|
|
1270
|
+
.sq-js [data-blocks] [data-footer-contact]:not([data-fit]) :is([data-fc-address], [data-fc-clock]) {
|
|
1271
|
+
visibility: hidden;
|
|
1272
|
+
animation: struq-show 0s 3s forwards;
|
|
1273
|
+
}
|
|
1274
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
1275
|
+
.sq-js [data-blocks] [data-footer-contact]:not([data-ready]) :is([data-fc-enter], [data-fc-split], [data-fc-h], [data-fc-m], [data-fc-colon-wrap]) {
|
|
1276
|
+
visibility: hidden;
|
|
1277
|
+
animation: struq-show 0s 3s forwards;
|
|
1278
|
+
}
|
|
1279
|
+
.sq-js [data-blocks] [data-footer-contact]:not([data-ready]) [data-fc-soft] {
|
|
1280
|
+
opacity: 0;
|
|
1281
|
+
animation: struq-fade 0s 3s forwards;
|
|
1282
|
+
}
|
|
1283
|
+
.sq-js [data-blocks] [data-footer-contact]:not([data-ready]) [data-fc-rule] {
|
|
1284
|
+
transform: scaleX(0);
|
|
1285
|
+
animation: struq-fc-rule 0s 3s forwards;
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
/* Reduced motion: no roll and no draw, the hover shows as a cut (the address steps back to the muted ink, a link's
|
|
1289
|
+
underline is simply there); the colon holds and a changed digit cuts. */
|
|
1290
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1291
|
+
[data-blocks] [data-footer-contact] [data-fc-roll],
|
|
1292
|
+
[data-blocks] [data-footer-contact] [data-fc-draw]::after {
|
|
1293
|
+
transition: none;
|
|
1294
|
+
}
|
|
1295
|
+
[data-blocks] [data-footer-contact] [data-fc-roll-host]:is(:hover, :focus-visible) [data-fc-roll] {
|
|
1296
|
+
transform: none;
|
|
1297
|
+
}
|
|
1298
|
+
[data-blocks] [data-footer-contact] [data-fc-roll-host]:is(:hover, :focus-visible) {
|
|
1299
|
+
color: var(--ink-muted);
|
|
1300
|
+
}
|
|
1301
|
+
[data-blocks] [data-footer-contact] [data-fc-colon],
|
|
1302
|
+
[data-blocks] [data-footer-contact] [data-fc-in] {
|
|
1303
|
+
animation: none;
|
|
1304
|
+
}
|
|
1305
|
+
[data-blocks] [data-footer-contact] [data-fc-out] {
|
|
1306
|
+
display: none;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
/* --- manifesto --- */
|
|
1311
|
+
/* The statement is display type at a light weight, whatever the pairing asks. The quote's veil is the page ground held
|
|
1312
|
+
dark (its own hue, never black), and the frame under the photograph is that same dark, so the light quote reads
|
|
1313
|
+
before the photo is decoded; the block's script tunes the veil's strength to the photo (never past 0.45) and this
|
|
1314
|
+
safe strength stays when the photo cannot be read. The caret is unseen until the script writes it. With motion and
|
|
1315
|
+
scripts every start state waits from the first paint (the statement unseen until it is cut into lines and every
|
|
1316
|
+
clip or mask is set, the signature clear, the photograph clear until decoded) and arrives after 3s if the script
|
|
1317
|
+
never runs; without scripts nothing waits. */
|
|
1318
|
+
[data-blocks] [data-manifesto] .font-display {
|
|
1319
|
+
font-weight: min(var(--display-weight, 300), 300);
|
|
1320
|
+
}
|
|
1321
|
+
[data-blocks] [data-manifesto] :is([data-mf-frame], [data-mf-veil]) {
|
|
1322
|
+
background: var(--inverse);
|
|
1323
|
+
background: oklch(from var(--surface-0) min(l, 0.2) c h);
|
|
1324
|
+
}
|
|
1325
|
+
[data-blocks] [data-manifesto] [data-mf-veil] {
|
|
1326
|
+
opacity: 0.4;
|
|
1327
|
+
transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1);
|
|
1328
|
+
}
|
|
1329
|
+
[data-blocks] [data-manifesto] [data-mf-caret] {
|
|
1330
|
+
opacity: 0;
|
|
1331
|
+
will-change: transform;
|
|
1332
|
+
}
|
|
1333
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
1334
|
+
:is(.js, .sq-js) [data-blocks] [data-manifesto]:not([data-ready]) [data-mf-text] {
|
|
1335
|
+
visibility: hidden;
|
|
1336
|
+
animation: struq-show 0s 3s forwards;
|
|
1337
|
+
}
|
|
1338
|
+
:is(.js, .sq-js) [data-blocks] [data-manifesto]:not([data-ready]) [data-mf-soft] {
|
|
1339
|
+
opacity: 0;
|
|
1340
|
+
animation: struq-fade 0s 3s forwards;
|
|
1341
|
+
}
|
|
1342
|
+
:is(.js, .sq-js) [data-blocks] [data-manifesto="quote"]:not([data-ready]) [data-mf-layer] {
|
|
1343
|
+
opacity: 0;
|
|
1344
|
+
animation: struq-fade 0s 3s forwards;
|
|
1345
|
+
}
|
|
1346
|
+
[data-blocks] [data-manifesto="quote"] [data-mf-layer] {
|
|
1347
|
+
will-change: transform;
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
/* --- header-bar --- */
|
|
1352
|
+
/* The bar's height is shared with the page through the nearest [data-blocks]; the block sets --hb-top there once it has
|
|
1353
|
+
measured (the bar's height, or for settle the bottom of the large wordmark), so a hero keeps its type below it. The
|
|
1354
|
+
copies (one per ink) show only once the script has cut them; until then, and without scripts, the real bar stands in
|
|
1355
|
+
the start tone's ink as the plain bar (settle's set large, below). */
|
|
1356
|
+
[data-blocks]:has([data-header-bar]),
|
|
1357
|
+
[data-blocks] [data-header-bar] {
|
|
1358
|
+
--hb-h: 4rem;
|
|
1359
|
+
}
|
|
1360
|
+
[data-blocks]:has([data-header-bar]) {
|
|
1361
|
+
--hb-top: var(--hb-h);
|
|
1362
|
+
}
|
|
1363
|
+
/* Without scripts the plain bar stays on the hero it was inked for, instead of riding over light sections in light ink. */
|
|
1364
|
+
html:not(.sq-js) [data-blocks] [data-header-bar] [data-hb-move] {
|
|
1365
|
+
position: absolute;
|
|
1366
|
+
}
|
|
1367
|
+
@media (min-width: 768px) {
|
|
1368
|
+
[data-blocks]:has([data-header-bar]),
|
|
1369
|
+
[data-blocks] [data-header-bar] {
|
|
1370
|
+
--hb-h: 4.5rem;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
[data-blocks] [data-header-bar] .font-display {
|
|
1374
|
+
font-weight: min(var(--display-weight, 300), 300);
|
|
1375
|
+
}
|
|
1376
|
+
[data-blocks] [data-header-bar] :is(a, button) {
|
|
1377
|
+
color: inherit;
|
|
1378
|
+
text-decoration: none;
|
|
1379
|
+
}
|
|
1380
|
+
[data-blocks] [data-header-bar] [data-hb-real] :is(a, button) {
|
|
1381
|
+
pointer-events: auto;
|
|
1382
|
+
}
|
|
1383
|
+
[data-blocks] [data-header-bar][data-links-off] [data-hb-real] [data-hb-fade] :is(a, button) {
|
|
1384
|
+
pointer-events: none;
|
|
1385
|
+
}
|
|
1386
|
+
[data-blocks] [data-header-bar]:not([data-ready]) [data-hb-copy] {
|
|
1387
|
+
visibility: hidden;
|
|
1388
|
+
}
|
|
1389
|
+
[data-blocks] [data-header-bar][data-ready] [data-hb-real] {
|
|
1390
|
+
color: transparent;
|
|
1391
|
+
}
|
|
1392
|
+
/* settle from the first paint: the wordmark already stands large across the hero, its scale the gutter span over its
|
|
1393
|
+
estimated advance (--hb-adv, set by the block, read against the face on screen as half em and half ch), capped at
|
|
1394
|
+
55svh over its line, at the top the script will use;
|
|
1395
|
+
the links wait for the landing. The script takes over with the measured face and eases out the difference. If the
|
|
1396
|
+
script never runs the plain bar returns after 3s. The wordmark rises out of its mask from 0s, script or not. */
|
|
1397
|
+
[data-blocks] [data-header-bar] {
|
|
1398
|
+
--hb-gx: 1.5rem;
|
|
1399
|
+
}
|
|
1400
|
+
@media (min-width: 768px) {
|
|
1401
|
+
[data-blocks] [data-header-bar] {
|
|
1402
|
+
--hb-gx: 3vw;
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
1406
|
+
.sq-js [data-blocks] [data-header-bar="settle"]:not([data-ready]) [data-hb-word] {
|
|
1407
|
+
translate: 0 calc(var(--hb-h) * 0.5 - (var(--hb-h) - 1em) / 2);
|
|
1408
|
+
scale: max(1, min(tan(atan2(100vw - 2 * var(--hb-gx), var(--hb-adv, 3) * (0.5em + 0.76ch))), tan(atan2(55svh, 1em))));
|
|
1409
|
+
animation: hb-plain 0s 3s forwards;
|
|
1410
|
+
}
|
|
1411
|
+
.sq-js [data-blocks] [data-header-bar="settle"]:not([data-ready]) [data-hb-fade] {
|
|
1412
|
+
visibility: hidden;
|
|
1413
|
+
animation: struq-show 0s 3s forwards;
|
|
1414
|
+
}
|
|
1415
|
+
.sq-js [data-blocks] [data-header-bar="settle"] [data-hb-word] [data-hb-wroll] {
|
|
1416
|
+
animation: hb-rise 1.2s var(--ease-out-expo) both;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
@keyframes hb-plain {
|
|
1420
|
+
to {
|
|
1421
|
+
translate: none;
|
|
1422
|
+
scale: none;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
@keyframes hb-rise {
|
|
1426
|
+
from {
|
|
1427
|
+
translate: 0 calc(100% + 0.22em);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
/* Keyboard focus: drawn on the copies in their own ink (the real links' text is transparent once they show). */
|
|
1431
|
+
[data-blocks] [data-header-bar] [data-hb-real] :focus-visible {
|
|
1432
|
+
outline: 1.5px solid currentColor;
|
|
1433
|
+
outline-offset: 6px;
|
|
1434
|
+
}
|
|
1435
|
+
[data-blocks] [data-header-bar][data-ready] [data-hb-real] :focus-visible {
|
|
1436
|
+
outline: none;
|
|
1437
|
+
}
|
|
1438
|
+
[data-blocks] [data-header-bar] [data-hb-copy] [data-hb-link][data-focus] {
|
|
1439
|
+
outline: 1.5px solid currentColor;
|
|
1440
|
+
outline-offset: 6px;
|
|
1441
|
+
border-radius: 1px;
|
|
1442
|
+
}
|
|
1443
|
+
/* Once the page has scrolled the bar takes the ground under it (over a photo the palette's dark over a blur) and quiet
|
|
1444
|
+
its hairline, all scrubbed by --hb-k over the bar's own height of scroll. */
|
|
1445
|
+
[data-blocks] [data-header-bar] :is([data-hb-ground], [data-hb-blur]) {
|
|
1446
|
+
opacity: var(--hb-k, 0);
|
|
1447
|
+
}
|
|
1448
|
+
[data-blocks] [data-header-bar] [data-hb-line] {
|
|
1449
|
+
opacity: calc(var(--hb-k, 0) * 0.2);
|
|
1450
|
+
}
|
|
1451
|
+
[data-blocks] [data-header-bar] [data-hb-track] {
|
|
1452
|
+
opacity: 0.2;
|
|
1453
|
+
}
|
|
1454
|
+
/* The wordmark rolls whole (its letters keep their kerning, even set large): the name slides up out of its mask and its
|
|
1455
|
+
copy rises in from below. The mask has room for descenders; the copy waits a little lower than the line. */
|
|
1456
|
+
[data-blocks] [data-header-bar] [data-hb-wmask] {
|
|
1457
|
+
overflow: hidden;
|
|
1458
|
+
padding-block: 0.08em 0.14em;
|
|
1459
|
+
margin-block: -0.08em -0.14em;
|
|
1460
|
+
}
|
|
1461
|
+
[data-blocks] [data-header-bar] [data-hb-wroll] > span:last-child {
|
|
1462
|
+
top: calc(100% + 0.22em);
|
|
1463
|
+
}
|
|
1464
|
+
[data-blocks] [data-header-bar] [data-hb-wroll] {
|
|
1465
|
+
transition: transform 0.6s var(--ease-out-expo);
|
|
1466
|
+
}
|
|
1467
|
+
[data-blocks] [data-header-bar] [data-hb-word][data-hot] [data-hb-wroll] {
|
|
1468
|
+
transform: translateY(calc(-100% - 0.22em));
|
|
1469
|
+
}
|
|
1470
|
+
/* quiet: each letter in its own mask, rolling after the one before it. */
|
|
1471
|
+
[data-blocks] [data-header-bar] [data-hb-ch] {
|
|
1472
|
+
position: relative;
|
|
1473
|
+
display: inline-block;
|
|
1474
|
+
overflow: hidden;
|
|
1475
|
+
white-space: pre;
|
|
1476
|
+
padding-block: 0.15em;
|
|
1477
|
+
margin-block: -0.15em;
|
|
1478
|
+
}
|
|
1479
|
+
[data-blocks] [data-header-bar] [data-hb-ch] > span {
|
|
1480
|
+
display: block;
|
|
1481
|
+
transition: transform 0.55s var(--ease-out-expo) calc(var(--i, 0) * 24ms);
|
|
1482
|
+
}
|
|
1483
|
+
[data-blocks] [data-header-bar] [data-hb-ch] > span:last-child {
|
|
1484
|
+
position: absolute;
|
|
1485
|
+
left: 0;
|
|
1486
|
+
top: calc(100% + 0.15em);
|
|
1487
|
+
}
|
|
1488
|
+
[data-blocks] [data-header-bar] [data-hb-link][data-hot] [data-hb-ch] > span {
|
|
1489
|
+
transform: translateY(calc(-100% - 0.3em));
|
|
1490
|
+
}
|
|
1491
|
+
/* The action: a text link with a faint rule under it at rest; quiet's rule comes up to the full ink on hover. */
|
|
1492
|
+
[data-blocks] [data-header-bar] [data-hb-action]::before {
|
|
1493
|
+
content: "";
|
|
1494
|
+
position: absolute;
|
|
1495
|
+
left: 0;
|
|
1496
|
+
right: 0;
|
|
1497
|
+
bottom: 0.2rem;
|
|
1498
|
+
height: 1px;
|
|
1499
|
+
background: currentColor;
|
|
1500
|
+
opacity: 0.35;
|
|
1501
|
+
transition: opacity 0.3s var(--ease-out-expo);
|
|
1502
|
+
}
|
|
1503
|
+
[data-blocks] [data-header-bar="quiet"] [data-hb-action][data-hot]::before {
|
|
1504
|
+
opacity: 1;
|
|
1505
|
+
}
|
|
1506
|
+
/* settle (every link but the wordmark) and split's action: a line draws from the side the pointer came in and
|
|
1507
|
+
retracts toward the side it leaves (--hb-o); settle's other links step back. */
|
|
1508
|
+
[data-blocks] [data-header-bar="settle"] [data-hb-link]:not([data-hb-link="w"])::after,
|
|
1509
|
+
[data-blocks] [data-header-bar="split"] [data-hb-action]::after {
|
|
1510
|
+
content: "";
|
|
1511
|
+
position: absolute;
|
|
1512
|
+
left: 0;
|
|
1513
|
+
right: 0;
|
|
1514
|
+
bottom: 0.2rem;
|
|
1515
|
+
height: 1px;
|
|
1516
|
+
background: currentColor;
|
|
1517
|
+
transform: scaleX(0);
|
|
1518
|
+
transform-origin: var(--hb-o, left) center;
|
|
1519
|
+
transition: transform 0.5s var(--ease-out-expo);
|
|
1520
|
+
}
|
|
1521
|
+
[data-blocks] [data-header-bar="settle"] [data-hb-link][data-hot]::after,
|
|
1522
|
+
[data-blocks] [data-header-bar="split"] [data-hb-action][data-hot]::after {
|
|
1523
|
+
transform: scaleX(1);
|
|
1524
|
+
}
|
|
1525
|
+
[data-blocks] [data-header-bar="settle"] [data-hb-fade] [data-hb-link] {
|
|
1526
|
+
transition: opacity 0.3s var(--ease-out-expo);
|
|
1527
|
+
}
|
|
1528
|
+
[data-blocks] [data-header-bar="settle"][data-hot-any] [data-hb-fade] [data-hb-link]:not([data-hot]) {
|
|
1529
|
+
opacity: 0.45;
|
|
1530
|
+
}
|
|
1531
|
+
/* split: one line glides under the hovered link and takes its width; it appears in place the first time. */
|
|
1532
|
+
[data-blocks] [data-header-bar="split"] [data-hb-mark] {
|
|
1533
|
+
bottom: 0.2rem;
|
|
1534
|
+
width: var(--hb-mw, 0px);
|
|
1535
|
+
transform: translate3d(var(--hb-mx, 0px), 0, 0);
|
|
1536
|
+
opacity: 0;
|
|
1537
|
+
transition:
|
|
1538
|
+
transform 0.45s var(--ease-out-expo),
|
|
1539
|
+
width 0.45s var(--ease-out-expo),
|
|
1540
|
+
opacity 0.25s linear;
|
|
1541
|
+
}
|
|
1542
|
+
[data-blocks] [data-header-bar="split"][data-mark] [data-hb-mark] {
|
|
1543
|
+
opacity: 1;
|
|
1544
|
+
}
|
|
1545
|
+
[data-blocks] [data-header-bar="split"][data-mark-snap] [data-hb-mark] {
|
|
1546
|
+
transition: opacity 0.25s linear;
|
|
1547
|
+
}
|
|
1548
|
+
/* The local time: unseen until it is known; without scripts it is left out, never a made-up time. */
|
|
1549
|
+
.sq-js [data-blocks] [data-header-bar] [data-hb-time][data-pending] {
|
|
1550
|
+
visibility: hidden;
|
|
1551
|
+
}
|
|
1552
|
+
html:not(.sq-js) [data-blocks] [data-header-bar] [data-hb-time] {
|
|
1553
|
+
display: none;
|
|
1554
|
+
}
|
|
1555
|
+
/* Reduced motion: nothing rolls or glides; a hover underlines (quiet and the wordmark), draws its line as a cut
|
|
1556
|
+
(settle) or puts the line under the link at once (split); the scrolled ground cuts in with a short fade. */
|
|
1557
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1558
|
+
[data-blocks] [data-header-bar] :is([data-hb-ch] > span, [data-hb-wroll], [data-hb-mark]),
|
|
1559
|
+
[data-blocks] [data-header-bar] [data-hb-link]::after {
|
|
1560
|
+
transition: none;
|
|
1561
|
+
}
|
|
1562
|
+
[data-blocks] [data-header-bar] [data-hb-link][data-hot] :is([data-hb-ch] > span, [data-hb-wroll]) {
|
|
1563
|
+
transform: none;
|
|
1564
|
+
}
|
|
1565
|
+
[data-blocks] [data-header-bar="quiet"] [data-hb-link][data-hot] [data-hb-label] {
|
|
1566
|
+
box-shadow: 0 1px 0 currentColor;
|
|
1567
|
+
}
|
|
1568
|
+
[data-blocks] [data-header-bar] [data-hb-word][data-hot] [data-hb-wroll] > span:first-child {
|
|
1569
|
+
text-decoration: underline;
|
|
1570
|
+
text-decoration-thickness: 1px;
|
|
1571
|
+
text-underline-offset: 0.2em;
|
|
1572
|
+
}
|
|
1573
|
+
[data-blocks] [data-header-bar] :is([data-hb-ground], [data-hb-blur], [data-hb-line]) {
|
|
1574
|
+
transition: opacity 0.2s linear;
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
/* --- contact-form --- */
|
|
1579
|
+
/* Display type stays light, whatever the pairing asks, as in the heroes. */
|
|
1580
|
+
[data-blocks] [data-contact-form] .font-display,
|
|
1581
|
+
[data-blocks] [data-contact-form] [data-cf-float] :is([data-cf-grow], label) {
|
|
1582
|
+
font-weight: min(var(--display-weight, 300), 300);
|
|
1583
|
+
}
|
|
1584
|
+
/* The fields carry no box and no browser chrome: the rule under them is the field. */
|
|
1585
|
+
[data-blocks] [data-contact-form] :is(input[type="text"], input[type="email"], textarea) {
|
|
1586
|
+
appearance: none;
|
|
1587
|
+
min-width: 0;
|
|
1588
|
+
margin: 0;
|
|
1589
|
+
padding: 0;
|
|
1590
|
+
border: 0;
|
|
1591
|
+
border-radius: 0;
|
|
1592
|
+
background: transparent;
|
|
1593
|
+
box-shadow: none;
|
|
1594
|
+
color: inherit;
|
|
1595
|
+
font: inherit;
|
|
1596
|
+
letter-spacing: inherit;
|
|
1597
|
+
outline: none;
|
|
1598
|
+
caret-color: var(--accent);
|
|
1599
|
+
resize: none;
|
|
1600
|
+
overflow: hidden;
|
|
1601
|
+
overflow-wrap: anywhere;
|
|
1602
|
+
/* struq.nl's own base layer paints text inputs with these two (as !important, which a layer wins); pointed at the
|
|
1603
|
+
block's ground and ink here, so the field stays a rule. */
|
|
1604
|
+
--color-panel: transparent;
|
|
1605
|
+
--color-primary-text: var(--ink);
|
|
1606
|
+
}
|
|
1607
|
+
[data-blocks] [data-contact-form] :is(input, textarea)::placeholder {
|
|
1608
|
+
color: color-mix(in oklch, var(--ink-muted) 72%, var(--surface-0));
|
|
1609
|
+
opacity: 1;
|
|
1610
|
+
}
|
|
1611
|
+
/* Autofill keeps its own ground away from a field that is only a rule. */
|
|
1612
|
+
[data-blocks] [data-contact-form] :is(input, textarea):-webkit-autofill {
|
|
1613
|
+
-webkit-text-fill-color: var(--ink);
|
|
1614
|
+
transition: background-color 0s 86400s, color 0s 86400s;
|
|
1615
|
+
}
|
|
1616
|
+
/* A field that grows with its text: the hidden copy of the value (data-mirror) sizes the cell the field fills, in the
|
|
1617
|
+
same face, so nothing is measured by script. */
|
|
1618
|
+
[data-blocks] [data-contact-form] [data-cf-grow] {
|
|
1619
|
+
position: relative;
|
|
1620
|
+
display: inline-grid;
|
|
1621
|
+
max-width: 100%;
|
|
1622
|
+
padding-bottom: 0.14em;
|
|
1623
|
+
vertical-align: baseline;
|
|
1624
|
+
}
|
|
1625
|
+
[data-blocks] [data-contact-form] [data-cf-grow][data-block] {
|
|
1626
|
+
display: grid;
|
|
1627
|
+
width: 100%;
|
|
1628
|
+
}
|
|
1629
|
+
[data-blocks] [data-contact-form] [data-cf-grow] > :is(input, textarea) {
|
|
1630
|
+
grid-area: 1 / 1;
|
|
1631
|
+
width: 100%;
|
|
1632
|
+
}
|
|
1633
|
+
[data-blocks] [data-contact-form] [data-cf-grow]::after {
|
|
1634
|
+
content: attr(data-mirror);
|
|
1635
|
+
grid-area: 1 / 1;
|
|
1636
|
+
padding-right: 0.08em;
|
|
1637
|
+
visibility: hidden;
|
|
1638
|
+
white-space: pre;
|
|
1639
|
+
overflow: hidden;
|
|
1640
|
+
pointer-events: none;
|
|
1641
|
+
}
|
|
1642
|
+
/* A text area keeps a trailing space in its copy, so a closing new line still takes its height. */
|
|
1643
|
+
[data-blocks] [data-contact-form] [data-cf-grow][data-multiline]::after {
|
|
1644
|
+
content: attr(data-mirror) " ";
|
|
1645
|
+
}
|
|
1646
|
+
[data-blocks] [data-contact-form] [data-cf-grow][data-multiline]::after {
|
|
1647
|
+
white-space: pre-wrap;
|
|
1648
|
+
overflow-wrap: anywhere;
|
|
1649
|
+
}
|
|
1650
|
+
/* The rule: a quiet line at rest; on focus the accent draws over it from the left, and draws back the way it came on
|
|
1651
|
+
leaving. An invalid field's rule turns to the full ink. */
|
|
1652
|
+
[data-blocks] [data-contact-form] [data-cf-rule] {
|
|
1653
|
+
position: absolute;
|
|
1654
|
+
inset: auto 0 0 0;
|
|
1655
|
+
height: 1px;
|
|
1656
|
+
background: color-mix(in oklch, var(--ink-muted) 55%, transparent);
|
|
1657
|
+
pointer-events: none;
|
|
1658
|
+
}
|
|
1659
|
+
[data-blocks] [data-contact-form] [data-cf-rule]::after {
|
|
1660
|
+
content: "";
|
|
1661
|
+
position: absolute;
|
|
1662
|
+
inset: auto 0 0 0;
|
|
1663
|
+
height: 2px;
|
|
1664
|
+
background: var(--accent);
|
|
1665
|
+
transform: scaleX(0);
|
|
1666
|
+
transform-origin: left;
|
|
1667
|
+
transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
|
|
1668
|
+
}
|
|
1669
|
+
[data-blocks] [data-contact-form] [data-cf-grow]:focus-within [data-cf-rule]::after {
|
|
1670
|
+
transform: scaleX(1);
|
|
1671
|
+
}
|
|
1672
|
+
[data-blocks] [data-contact-form] [data-invalid] [data-cf-rule] {
|
|
1673
|
+
height: 2px;
|
|
1674
|
+
background: var(--ink);
|
|
1675
|
+
}
|
|
1676
|
+
/* Each message has its slot from the start, one line high, so saying it moves nothing. */
|
|
1677
|
+
[data-blocks] [data-contact-form] [data-cf-msg] {
|
|
1678
|
+
display: block;
|
|
1679
|
+
min-height: 1.95rem;
|
|
1680
|
+
padding-top: 0.6rem;
|
|
1681
|
+
font-family: var(--face-body), ui-sans-serif, system-ui, sans-serif;
|
|
1682
|
+
font-size: 0.875rem;
|
|
1683
|
+
font-weight: 400;
|
|
1684
|
+
line-height: 1.5;
|
|
1685
|
+
letter-spacing: 0;
|
|
1686
|
+
color: var(--ink);
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
/* lines: the label rests inside the field and lifts to a small label above it when the field is focused or filled. */
|
|
1690
|
+
[data-blocks] [data-contact-form="lines"] [data-cf-float] {
|
|
1691
|
+
--cf-size: clamp(1.25rem, 1.05rem + 0.7vw, 1.75rem);
|
|
1692
|
+
position: relative;
|
|
1693
|
+
padding-top: calc(var(--cf-size) * 0.9);
|
|
1694
|
+
}
|
|
1695
|
+
[data-blocks] [data-contact-form="lines"] [data-cf-float] :is([data-cf-grow], label) {
|
|
1696
|
+
font-family: var(--font-display);
|
|
1697
|
+
font-size: var(--cf-size);
|
|
1698
|
+
line-height: 1.35;
|
|
1699
|
+
letter-spacing: -0.01em;
|
|
1700
|
+
}
|
|
1701
|
+
[data-blocks] [data-contact-form="lines"] [data-cf-float] > label {
|
|
1702
|
+
position: absolute;
|
|
1703
|
+
left: 0;
|
|
1704
|
+
top: calc(var(--cf-size) * 0.9);
|
|
1705
|
+
color: var(--ink-muted);
|
|
1706
|
+
white-space: nowrap;
|
|
1707
|
+
transform-origin: 0 0;
|
|
1708
|
+
pointer-events: none;
|
|
1709
|
+
transition:
|
|
1710
|
+
transform 0.5s cubic-bezier(0.16, 1, 0.3, 1),
|
|
1711
|
+
color 0.5s cubic-bezier(0.16, 1, 0.3, 1);
|
|
1712
|
+
}
|
|
1713
|
+
[data-blocks] [data-contact-form="lines"] [data-cf-float]:focus-within > label,
|
|
1714
|
+
[data-blocks] [data-contact-form="lines"] [data-cf-float]:has(:is(input, textarea):not(:placeholder-shown)) > label {
|
|
1715
|
+
transform: translateY(calc(var(--cf-size) * -0.9)) scale(0.58);
|
|
1716
|
+
color: var(--ink);
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
/* sentence: the blanks inline at display size; each message hangs under its blank. Below 768 px the clauses stack,
|
|
1720
|
+
each fragment the label above its own field. */
|
|
1721
|
+
[data-blocks] [data-contact-form="sentence"] [data-cf-sentence] {
|
|
1722
|
+
font-size: clamp(2rem, 0.9rem + 3.6vw, 4.25rem);
|
|
1723
|
+
line-height: 1.75;
|
|
1724
|
+
letter-spacing: -0.02em;
|
|
1725
|
+
}
|
|
1726
|
+
[data-blocks] [data-contact-form="sentence"] [data-cf-blank] {
|
|
1727
|
+
position: relative;
|
|
1728
|
+
display: inline-block;
|
|
1729
|
+
max-width: 100%;
|
|
1730
|
+
line-height: 1.1;
|
|
1731
|
+
vertical-align: baseline;
|
|
1732
|
+
}
|
|
1733
|
+
/* The text area's first line sits on the sentence's own line: top-aligned, down by half the difference in leading. */
|
|
1734
|
+
[data-blocks] [data-contact-form="sentence"] [data-cf-blank][data-multiline] {
|
|
1735
|
+
vertical-align: top;
|
|
1736
|
+
margin-top: 0.25em;
|
|
1737
|
+
}
|
|
1738
|
+
@media (min-width: 768px) {
|
|
1739
|
+
[data-blocks] [data-contact-form="sentence"] [data-cf-msg] {
|
|
1740
|
+
position: absolute;
|
|
1741
|
+
left: 0;
|
|
1742
|
+
top: calc(100% + 0.05em);
|
|
1743
|
+
min-height: 0;
|
|
1744
|
+
padding-top: 0.15rem;
|
|
1745
|
+
line-height: 1.2;
|
|
1746
|
+
white-space: nowrap;
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
@media (max-width: 767px) {
|
|
1750
|
+
[data-blocks] [data-contact-form="sentence"] [data-cf-sentence] {
|
|
1751
|
+
font-size: clamp(1.625rem, 1rem + 2.6vw, 2rem);
|
|
1752
|
+
line-height: 1.2;
|
|
1753
|
+
}
|
|
1754
|
+
[data-blocks] [data-contact-form="sentence"] [data-cf-clause] {
|
|
1755
|
+
display: block;
|
|
1756
|
+
}
|
|
1757
|
+
[data-blocks] [data-contact-form="sentence"] [data-cf-clause] + [data-cf-clause] {
|
|
1758
|
+
margin-top: 1rem;
|
|
1759
|
+
}
|
|
1760
|
+
[data-blocks] [data-contact-form="sentence"] [data-cf-clause] > label {
|
|
1761
|
+
display: block;
|
|
1762
|
+
margin-bottom: 0.3em;
|
|
1763
|
+
}
|
|
1764
|
+
[data-blocks] [data-contact-form="sentence"] [data-cf-blank],
|
|
1765
|
+
[data-blocks] [data-contact-form="sentence"] [data-cf-blank][data-multiline] {
|
|
1766
|
+
display: block;
|
|
1767
|
+
margin-top: 0;
|
|
1768
|
+
}
|
|
1769
|
+
[data-blocks] [data-contact-form="sentence"] [data-cf-blank] [data-cf-grow] {
|
|
1770
|
+
display: grid;
|
|
1771
|
+
width: 100%;
|
|
1772
|
+
}
|
|
1773
|
+
[data-blocks] [data-contact-form="sentence"] [data-cf-comma] {
|
|
1774
|
+
display: none;
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
/* The choice: real radio inputs as quiet rings; the checked one takes a small dot in the accent. */
|
|
1779
|
+
[data-blocks] [data-contact-form] [data-cf-radio] {
|
|
1780
|
+
appearance: none;
|
|
1781
|
+
display: inline-grid;
|
|
1782
|
+
place-content: center;
|
|
1783
|
+
flex-shrink: 0;
|
|
1784
|
+
width: 0.9em;
|
|
1785
|
+
height: 0.9em;
|
|
1786
|
+
margin: 0;
|
|
1787
|
+
border: 1px solid var(--ink-muted);
|
|
1788
|
+
border-radius: 50%;
|
|
1789
|
+
background: transparent;
|
|
1790
|
+
cursor: pointer;
|
|
1791
|
+
transition: border-color 0.25s ease;
|
|
1792
|
+
}
|
|
1793
|
+
[data-blocks] [data-contact-form] [data-cf-radio]::before {
|
|
1794
|
+
content: "";
|
|
1795
|
+
width: 0.46em;
|
|
1796
|
+
height: 0.46em;
|
|
1797
|
+
border-radius: 50%;
|
|
1798
|
+
background: var(--accent);
|
|
1799
|
+
transform: scale(0);
|
|
1800
|
+
transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
|
1801
|
+
}
|
|
1802
|
+
[data-blocks] [data-contact-form] [data-cf-radio]:checked,
|
|
1803
|
+
[data-blocks] [data-contact-form] [data-cf-choice]:hover [data-cf-radio] {
|
|
1804
|
+
border-color: var(--ink);
|
|
1805
|
+
}
|
|
1806
|
+
[data-blocks] [data-contact-form] [data-cf-radio]:checked::before {
|
|
1807
|
+
transform: scale(1);
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
/* The send link: its arrow nudges on hover and focus, and travels while the message is on its way. */
|
|
1811
|
+
[data-blocks] [data-contact-form] [data-cf-arrow] {
|
|
1812
|
+
padding-right: 0.25em;
|
|
1813
|
+
margin-right: -0.25em;
|
|
1814
|
+
}
|
|
1815
|
+
[data-blocks] [data-contact-form] [data-cf-arrow] svg {
|
|
1816
|
+
transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
|
|
1817
|
+
}
|
|
1818
|
+
[data-blocks] [data-contact-form] button:is(:hover, :focus-visible) [data-cf-arrow] svg {
|
|
1819
|
+
transform: translateX(0.22em);
|
|
1820
|
+
}
|
|
1821
|
+
[data-blocks] [data-contact-form][data-state="sending"] [data-cf-send] {
|
|
1822
|
+
cursor: progress;
|
|
1823
|
+
}
|
|
1824
|
+
[data-blocks] [data-contact-form][data-state="sending"] [data-cf-send] [data-cf-arrow] svg {
|
|
1825
|
+
animation: cf-travel 1.1s cubic-bezier(0.65, 0, 0.35, 1) infinite;
|
|
1826
|
+
}
|
|
1827
|
+
/* The arrow leaves on the right and comes back in from the left; the arrow's own overflow clip does the hiding. */
|
|
1828
|
+
@keyframes cf-travel {
|
|
1829
|
+
0% { transform: translateX(0); }
|
|
1830
|
+
50% { transform: translateX(110%); }
|
|
1831
|
+
50.01% { transform: translateX(-110%); }
|
|
1832
|
+
100% { transform: translateX(0); }
|
|
1833
|
+
}
|
|
1834
|
+
/* The link buttons say focus with their own full underline, never the kit's boxed outline. */
|
|
1835
|
+
[data-blocks] [data-contact-form] [data-cf-link]:focus-visible {
|
|
1836
|
+
outline: none;
|
|
1837
|
+
}
|
|
1838
|
+
[data-blocks] [data-contact-form] [data-cf-fields] {
|
|
1839
|
+
transition: opacity 0.3s ease;
|
|
1840
|
+
}
|
|
1841
|
+
[data-blocks] [data-contact-form][data-state="sending"] [data-cf-fields] {
|
|
1842
|
+
opacity: 0.5;
|
|
1843
|
+
pointer-events: none;
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
/* The form and the thank-you share one cell: the stage keeps the form's height, so the exchange moves nothing. */
|
|
1847
|
+
[data-blocks] [data-contact-form] [data-cf-stage] {
|
|
1848
|
+
display: grid;
|
|
1849
|
+
}
|
|
1850
|
+
[data-blocks] [data-contact-form] [data-cf-stage] > * {
|
|
1851
|
+
grid-area: 1 / 1;
|
|
1852
|
+
min-width: 0;
|
|
1853
|
+
}
|
|
1854
|
+
[data-blocks] [data-contact-form] [data-cf-form] {
|
|
1855
|
+
transition:
|
|
1856
|
+
opacity 0.35s ease,
|
|
1857
|
+
transform 0.6s cubic-bezier(0.16, 1, 0.3, 1),
|
|
1858
|
+
visibility 0s;
|
|
1859
|
+
}
|
|
1860
|
+
[data-blocks] [data-contact-form][data-state="sent"] [data-cf-form] {
|
|
1861
|
+
opacity: 0;
|
|
1862
|
+
transform: translateY(-0.75rem);
|
|
1863
|
+
visibility: hidden;
|
|
1864
|
+
transition:
|
|
1865
|
+
opacity 0.35s ease,
|
|
1866
|
+
transform 0.6s cubic-bezier(0.16, 1, 0.3, 1),
|
|
1867
|
+
visibility 0s 0.35s;
|
|
1868
|
+
}
|
|
1869
|
+
[data-blocks] [data-contact-form] [data-cf-thanks] {
|
|
1870
|
+
visibility: hidden;
|
|
1871
|
+
opacity: 0;
|
|
1872
|
+
transition:
|
|
1873
|
+
opacity 0.3s ease,
|
|
1874
|
+
visibility 0s 0.3s;
|
|
1875
|
+
}
|
|
1876
|
+
[data-blocks] [data-contact-form][data-state="sent"] [data-cf-thanks] {
|
|
1877
|
+
visibility: visible;
|
|
1878
|
+
opacity: 1;
|
|
1879
|
+
transition:
|
|
1880
|
+
opacity 0.2s ease 0.2s,
|
|
1881
|
+
visibility 0s 0.2s;
|
|
1882
|
+
}
|
|
1883
|
+
[data-blocks] [data-contact-form] [data-cf-thanks] [data-cf-rise] {
|
|
1884
|
+
transform: translateY(105%);
|
|
1885
|
+
transition: transform 1s cubic-bezier(0.16, 1, 0.3, 1) 0.25s;
|
|
1886
|
+
}
|
|
1887
|
+
[data-blocks] [data-contact-form] [data-cf-thanks] [data-cf-soft] {
|
|
1888
|
+
opacity: 0;
|
|
1889
|
+
transform: translateY(0.75rem);
|
|
1890
|
+
transition:
|
|
1891
|
+
opacity 0.6s ease 0.55s,
|
|
1892
|
+
transform 0.9s cubic-bezier(0.16, 1, 0.3, 1) 0.55s;
|
|
1893
|
+
}
|
|
1894
|
+
[data-blocks] [data-contact-form][data-state="sent"] [data-cf-thanks] :is([data-cf-rise], [data-cf-soft]) {
|
|
1895
|
+
opacity: 1;
|
|
1896
|
+
transform: none;
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
/* steps: every question shares one cell, so the stage is as tall as the tallest from the first paint. With scripts the
|
|
1900
|
+
first question shows and the rest wait; if the script never runs they stand stacked after 3 s. Without scripts they
|
|
1901
|
+
stand stacked from the start. */
|
|
1902
|
+
[data-blocks] [data-contact-form="steps"] [data-cf-fill] {
|
|
1903
|
+
transition: transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);
|
|
1904
|
+
}
|
|
1905
|
+
.sq-js [data-blocks] [data-contact-form="steps"] [data-cf-steps],
|
|
1906
|
+
[data-blocks] [data-contact-form="steps"][data-ready] [data-cf-steps] {
|
|
1907
|
+
display: grid;
|
|
1908
|
+
}
|
|
1909
|
+
.sq-js [data-blocks] [data-contact-form="steps"] [data-cf-step],
|
|
1910
|
+
[data-blocks] [data-contact-form="steps"][data-ready] [data-cf-step] {
|
|
1911
|
+
grid-area: 1 / 1;
|
|
1912
|
+
}
|
|
1913
|
+
.sq-js [data-blocks] [data-contact-form="steps"]:not([data-ready]) [data-cf-step]:not(:first-child) {
|
|
1914
|
+
visibility: hidden;
|
|
1915
|
+
animation: cf-unstack 0s 3s forwards;
|
|
1916
|
+
}
|
|
1917
|
+
@keyframes cf-unstack {
|
|
1918
|
+
to { visibility: visible; grid-area: auto; }
|
|
1919
|
+
}
|
|
1920
|
+
[data-blocks] [data-contact-form="steps"][data-ready] [data-cf-step] {
|
|
1921
|
+
transition:
|
|
1922
|
+
opacity 0.45s ease 0.1s,
|
|
1923
|
+
transform 0.8s cubic-bezier(0.16, 1, 0.3, 1),
|
|
1924
|
+
visibility 0s;
|
|
1925
|
+
}
|
|
1926
|
+
[data-blocks] [data-contact-form="steps"][data-ready] [data-cf-step]:not([data-pos="current"]) {
|
|
1927
|
+
opacity: 0;
|
|
1928
|
+
visibility: hidden;
|
|
1929
|
+
transition:
|
|
1930
|
+
opacity 0.25s ease,
|
|
1931
|
+
transform 0.6s cubic-bezier(0.16, 1, 0.3, 1),
|
|
1932
|
+
visibility 0s 0.6s;
|
|
1933
|
+
}
|
|
1934
|
+
[data-blocks] [data-contact-form="steps"][data-ready] [data-cf-step][data-pos="before"] {
|
|
1935
|
+
transform: translateY(-2.5rem);
|
|
1936
|
+
}
|
|
1937
|
+
[data-blocks] [data-contact-form="steps"][data-ready] [data-cf-step][data-pos="after"] {
|
|
1938
|
+
transform: translateY(2.5rem);
|
|
1939
|
+
}
|
|
1940
|
+
[data-blocks] [data-contact-form="steps"] [data-cf-grow] {
|
|
1941
|
+
line-height: 1.15;
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
/* Reduced motion: the same states as cuts and short fades, nothing travels. */
|
|
1945
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1946
|
+
[data-blocks] [data-contact-form] [data-cf-rule]::after {
|
|
1947
|
+
transform: none;
|
|
1948
|
+
opacity: 0;
|
|
1949
|
+
transition: opacity 0.15s ease;
|
|
1950
|
+
}
|
|
1951
|
+
[data-blocks] [data-contact-form] [data-cf-grow]:focus-within [data-cf-rule]::after {
|
|
1952
|
+
transform: none;
|
|
1953
|
+
opacity: 1;
|
|
1954
|
+
}
|
|
1955
|
+
[data-blocks] [data-contact-form="lines"] [data-cf-float] > label,
|
|
1956
|
+
[data-blocks] [data-contact-form] [data-cf-radio]::before,
|
|
1957
|
+
[data-blocks] [data-contact-form] [data-cf-arrow] svg,
|
|
1958
|
+
[data-blocks] [data-contact-form="steps"] [data-cf-fill] {
|
|
1959
|
+
transition: none;
|
|
1960
|
+
}
|
|
1961
|
+
[data-blocks] [data-contact-form] button:is(:hover, :focus-visible) [data-cf-arrow] svg {
|
|
1962
|
+
transform: none;
|
|
1963
|
+
}
|
|
1964
|
+
[data-blocks] [data-contact-form][data-state="sending"] [data-cf-send] [data-cf-arrow] svg {
|
|
1965
|
+
animation: none;
|
|
1966
|
+
opacity: 0.45;
|
|
1967
|
+
}
|
|
1968
|
+
[data-blocks] [data-contact-form][data-state="sent"] [data-cf-form],
|
|
1969
|
+
[data-blocks] [data-contact-form] [data-cf-thanks] :is([data-cf-rise], [data-cf-soft]),
|
|
1970
|
+
[data-blocks] [data-contact-form="steps"][data-ready] [data-cf-step][data-pos] {
|
|
1971
|
+
transform: none;
|
|
1972
|
+
}
|
|
1973
|
+
[data-blocks] [data-contact-form] [data-cf-thanks] :is([data-cf-rise], [data-cf-soft]) {
|
|
1974
|
+
transition: none;
|
|
1975
|
+
}
|
|
1976
|
+
[data-blocks] [data-contact-form="steps"][data-ready] [data-cf-step] {
|
|
1977
|
+
transition:
|
|
1978
|
+
opacity 0.15s ease,
|
|
1979
|
+
visibility 0s;
|
|
1980
|
+
}
|
|
1981
|
+
[data-blocks] [data-contact-form="steps"][data-ready] [data-cf-step]:not([data-pos="current"]) {
|
|
1982
|
+
transition:
|
|
1983
|
+
opacity 0.15s ease,
|
|
1984
|
+
visibility 0s 0.15s;
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
/* --- hero-index variants --- */
|
|
1989
|
+
/* venster: the names stand on the page ground in the ink, their place lines in the muted ink; the photograph in its
|
|
1990
|
+
window carries no veil, since no name stands on it. */
|
|
1991
|
+
[data-blocks] [data-hero-index][data-variant="venster"] [data-veil] {
|
|
1992
|
+
display: none;
|
|
1993
|
+
}
|
|
1994
|
+
[data-blocks] [data-hero-index][data-variant="venster"] :is([data-list], [data-foot]) {
|
|
1995
|
+
color: var(--ink);
|
|
1996
|
+
}
|
|
1997
|
+
[data-blocks] [data-hero-index][data-variant="venster"] [data-meta] {
|
|
1998
|
+
color: var(--ink-muted);
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
/* --- hero-split variants --- */
|
|
2002
|
+
/* opening: on wide screens with motion and scripts, from the first paint until the script writes the window, the
|
|
2003
|
+
photograph waits as a strip at the outer edge (16% of the screen out of its 58%, the photo behind it at the cell's
|
|
2004
|
+
full width) and the headline's copies are clipped to that seam. Without scripts it is the split; if the script
|
|
2005
|
+
never runs the strip stays, a whole composition with nothing hidden. */
|
|
2006
|
+
@media (min-width: 768px) and (prefers-reduced-motion: no-preference) {
|
|
2007
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-split][data-variant="opening"]:not([data-ready]) [data-photo-box] {
|
|
2008
|
+
width: 27.5862%;
|
|
2009
|
+
}
|
|
2010
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-split][data-variant="opening"]:not([data-ready]) [data-drift] {
|
|
2011
|
+
width: 362.5%;
|
|
2012
|
+
}
|
|
2013
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-split][data-variant="opening"][data-photo="right"]:not([data-ready]) [data-copy="ground"] {
|
|
2014
|
+
clip-path: inset(0 16% 0 0) !important;
|
|
2015
|
+
}
|
|
2016
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-split][data-variant="opening"][data-photo="right"]:not([data-ready]) [data-copy="photo"] {
|
|
2017
|
+
clip-path: inset(0 0 0 84%) !important;
|
|
2018
|
+
}
|
|
2019
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-split][data-variant="opening"][data-photo="left"]:not([data-ready]) [data-copy="ground"] {
|
|
2020
|
+
clip-path: inset(0 0 0 16%) !important;
|
|
2021
|
+
}
|
|
2022
|
+
:is(.js, .sq-js) [data-blocks] [data-hero-split][data-variant="opening"][data-photo="left"]:not([data-ready]) [data-copy="photo"] {
|
|
2023
|
+
clip-path: inset(0 84% 0 0) !important;
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
/* --- about-studio --- */
|
|
2028
|
+
/* Display type at a light weight, whatever the pairing asks. Each fact's rule is drawn by the script through
|
|
2029
|
+
--as-draw (0 to 1, plain numbers); without it the rule stands whole. doorkijk's window width is shared by the frame
|
|
2030
|
+
and the grid track it sits in. With motion and scripts the statement waits unseen until it is cut into lines, the
|
|
2031
|
+
copy and the photographs wait clear (a photograph until it is decoded), and all of it arrives after 3s if the script
|
|
2032
|
+
never runs. */
|
|
2033
|
+
[data-blocks] [data-about-studio] .font-display {
|
|
2034
|
+
font-weight: min(var(--display-weight, 300), 300);
|
|
2035
|
+
}
|
|
2036
|
+
[data-blocks] [data-about-studio] [data-as-rule]::before {
|
|
2037
|
+
content: "";
|
|
2038
|
+
position: absolute;
|
|
2039
|
+
inset-inline: 0;
|
|
2040
|
+
top: 0;
|
|
2041
|
+
height: 1px;
|
|
2042
|
+
background: var(--line);
|
|
2043
|
+
clip-path: inset(0 calc((1 - var(--as-draw, 1)) * 100%) 0 0);
|
|
2044
|
+
}
|
|
2045
|
+
[data-blocks] [data-about-studio="doorkijk"] {
|
|
2046
|
+
--dk-w: min(62vw, 34svh);
|
|
2047
|
+
}
|
|
2048
|
+
@media (min-width: 768px) {
|
|
2049
|
+
[data-blocks] [data-about-studio="doorkijk"] {
|
|
2050
|
+
--dk-w: min(34vw, 54svh);
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
2054
|
+
[data-blocks] [data-about-studio] :is([data-as-layer], [data-as-col="1"]) {
|
|
2055
|
+
will-change: transform;
|
|
2056
|
+
}
|
|
2057
|
+
:is(.js, .sq-js) [data-blocks] [data-about-studio]:not([data-ready]) [data-as-head] {
|
|
2058
|
+
visibility: hidden;
|
|
2059
|
+
animation: struq-show 0s 3s forwards;
|
|
2060
|
+
}
|
|
2061
|
+
:is(.js, .sq-js) [data-blocks] [data-about-studio]:not([data-ready]) :is([data-as-soft], [data-as-photo]) {
|
|
2062
|
+
opacity: 0;
|
|
2063
|
+
animation: struq-fade 0s 3s forwards;
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
/* --- approach --- */
|
|
2068
|
+
/* Display type at a light weight. Once the script runs (data-stacked) the notes of zin and schuif share one cell, so
|
|
2069
|
+
only the current one stands; without it they read as a plain list. schuif's later photographs wait shut, so without
|
|
2070
|
+
scripts the first one stands alone. With motion and scripts the titles and names wait unseen until they are cut into
|
|
2071
|
+
lines, draad's line, ticks and copy wait clear, zin's notes and schuif's photographs wait unseen, and all of it
|
|
2072
|
+
arrives after 3s if the script never runs. */
|
|
2073
|
+
[data-blocks] [data-approach] .font-display {
|
|
2074
|
+
font-weight: min(var(--display-weight, 300), 300);
|
|
2075
|
+
}
|
|
2076
|
+
[data-blocks] [data-approach][data-stacked] [data-ap-notes] > * {
|
|
2077
|
+
grid-area: 1 / 1;
|
|
2078
|
+
}
|
|
2079
|
+
[data-blocks] [data-approach="schuif"] [data-ap-layer] + [data-ap-layer] {
|
|
2080
|
+
clip-path: inset(0 0 0 100%);
|
|
2081
|
+
}
|
|
2082
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
2083
|
+
[data-blocks] [data-approach="schuif"] [data-ap-handle] {
|
|
2084
|
+
will-change: transform;
|
|
2085
|
+
}
|
|
2086
|
+
:is(.js, .sq-js) [data-blocks] [data-approach]:not([data-ready]) :is([data-ap-title], [data-ap-head]) {
|
|
2087
|
+
visibility: hidden;
|
|
2088
|
+
animation: struq-show 0s 3s forwards;
|
|
2089
|
+
}
|
|
2090
|
+
:is(.js, .sq-js) [data-blocks] [data-approach="draad"]:not([data-ready]) :is([data-ap-soft], [data-ap-ink], [data-ap-tick]) {
|
|
2091
|
+
opacity: 0;
|
|
2092
|
+
animation: struq-fade 0s 3s forwards;
|
|
2093
|
+
}
|
|
2094
|
+
:is(.js, .sq-js) [data-blocks] [data-approach="zin"]:not([data-ready]) [data-ap-notes] {
|
|
2095
|
+
visibility: hidden;
|
|
2096
|
+
animation: struq-show 0s 3s forwards;
|
|
2097
|
+
}
|
|
2098
|
+
:is(.js, .sq-js) [data-blocks] [data-approach="schuif"]:not([data-ready]) [data-ap-layer] {
|
|
2099
|
+
opacity: 0;
|
|
2100
|
+
animation: struq-fade 0s 3s forwards;
|
|
2101
|
+
}
|
|
2102
|
+
}
|