@cogenta/theme-canonical 0.1.2 → 0.2.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/dist/src/render/blocks/collection-list.js +1 -1
- package/dist/src/render/blocks/collection-list.js.map +1 -1
- package/dist/src/render/blocks/cta.d.ts.map +1 -1
- package/dist/src/render/blocks/cta.js +3 -1
- package/dist/src/render/blocks/cta.js.map +1 -1
- package/dist/src/render/blocks/faq.d.ts.map +1 -1
- package/dist/src/render/blocks/faq.js +1 -1
- package/dist/src/render/blocks/faq.js.map +1 -1
- package/dist/src/render/blocks/feature-grid.js +1 -1
- package/dist/src/render/blocks/feature-grid.js.map +1 -1
- package/dist/src/render/blocks/hero.d.ts.map +1 -1
- package/dist/src/render/blocks/hero.js +5 -1
- package/dist/src/render/blocks/hero.js.map +1 -1
- package/dist/src/render/blocks/logos.d.ts.map +1 -1
- package/dist/src/render/blocks/logos.js +1 -1
- package/dist/src/render/blocks/logos.js.map +1 -1
- package/dist/src/render/blocks/media-figure.js +1 -1
- package/dist/src/render/blocks/media-figure.js.map +1 -1
- package/dist/src/render/blocks/quote.d.ts.map +1 -1
- package/dist/src/render/blocks/quote.js +4 -2
- package/dist/src/render/blocks/quote.js.map +1 -1
- package/dist/src/render/blocks/stats.d.ts.map +1 -1
- package/dist/src/render/blocks/stats.js +1 -1
- package/dist/src/render/blocks/stats.js.map +1 -1
- package/dist/src/render/render-block.d.ts.map +1 -1
- package/dist/src/render/render-block.js +34 -1
- package/dist/src/render/render-block.js.map +1 -1
- package/package.json +4 -3
- package/src/render/blocks/collection-list.ts +1 -1
- package/src/render/blocks/cta.ts +8 -2
- package/src/render/blocks/faq.ts +5 -1
- package/src/render/blocks/feature-grid.ts +1 -1
- package/src/render/blocks/hero.ts +7 -3
- package/src/render/blocks/logos.ts +5 -1
- package/src/render/blocks/media-figure.ts +1 -1
- package/src/render/blocks/quote.ts +5 -3
- package/src/render/blocks/stats.ts +5 -1
- package/src/render/render-block.ts +34 -1
- package/src/styles/base.css +341 -0
- package/src/styles/blocks.css +890 -0
- package/src/styles/theme.css +20 -601
- package/src/styles/tokens.css +339 -0
|
@@ -0,0 +1,890 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The twelve vocabulary blocks.
|
|
3
|
+
*
|
|
4
|
+
* One section per block, in the contract-B order. Nothing here declares a
|
|
5
|
+
* colour, a size, a radius or a duration of its own: a block composes the
|
|
6
|
+
* design system from `tokens.css` and nothing else, which is what keeps a skin
|
|
7
|
+
* swap a whole-theme change rather than a per-block one.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/* --- hero ----------------------------------------------------------------
|
|
11
|
+
The page's first impression, so it is the one block that gets a decorative
|
|
12
|
+
element of its own: a soft wash of the skin's accent behind the media. It is
|
|
13
|
+
drawn on a pseudo-element and sized to stay inside the block's own box —
|
|
14
|
+
a decoration that widens the page produces a horizontal scrollbar, which is
|
|
15
|
+
the classic way a hero breaks a phone.
|
|
16
|
+
------------------------------------------------------------------------- */
|
|
17
|
+
|
|
18
|
+
.cg-hero {
|
|
19
|
+
position: relative;
|
|
20
|
+
/* A stacking context of its own, so `z-index: -1` on the wash puts it behind
|
|
21
|
+
the hero's content and not behind the page background. */
|
|
22
|
+
isolation: isolate;
|
|
23
|
+
display: grid;
|
|
24
|
+
gap: var(--cg-gap-loose);
|
|
25
|
+
align-items: center;
|
|
26
|
+
margin-block-start: var(--cg-s10);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.cg-hero::before {
|
|
30
|
+
content: "";
|
|
31
|
+
position: absolute;
|
|
32
|
+
inset-block-start: 0;
|
|
33
|
+
inset-inline-end: var(--cg-gutter);
|
|
34
|
+
inline-size: min(30rem, 70%);
|
|
35
|
+
aspect-ratio: 1;
|
|
36
|
+
z-index: -1;
|
|
37
|
+
background: radial-gradient(circle at 60% 40%, var(--cg-accent-soft), transparent 68%);
|
|
38
|
+
pointer-events: none;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@media (min-width: 60rem) {
|
|
42
|
+
.cg-hero {
|
|
43
|
+
/* Not 1fr 1fr: the copy carries the weight, and an even split makes both
|
|
44
|
+
halves look accidental. */
|
|
45
|
+
grid-template-columns: 1.05fr 0.95fr;
|
|
46
|
+
gap: var(--cg-s16);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* A badge, not a line of small text: it is the one label above the h1 and it
|
|
51
|
+
has to look deliberate. */
|
|
52
|
+
.cg-hero__eyebrow {
|
|
53
|
+
display: inline-block;
|
|
54
|
+
margin: 0 0 var(--cg-s5);
|
|
55
|
+
padding: var(--cg-s1) var(--cg-s3);
|
|
56
|
+
background: var(--cg-accent-soft);
|
|
57
|
+
color: var(--cg-accent-soft-fg);
|
|
58
|
+
border-radius: var(--cg-radius-pill);
|
|
59
|
+
box-shadow: var(--cg-ring-accent);
|
|
60
|
+
font-size: var(--cg-text-xs);
|
|
61
|
+
font-weight: var(--cg-weight-semibold);
|
|
62
|
+
letter-spacing: var(--cg-tracking-wide);
|
|
63
|
+
text-transform: uppercase;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.cg-hero__title {
|
|
67
|
+
margin: 0;
|
|
68
|
+
font-size: var(--cg-text-3xl);
|
|
69
|
+
line-height: var(--cg-leading-tight);
|
|
70
|
+
letter-spacing: var(--cg-tracking-tight);
|
|
71
|
+
font-weight: var(--cg-weight-bold);
|
|
72
|
+
/* Display type wraps into a shape, not into a ragged column. */
|
|
73
|
+
text-wrap: balance;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* Above 60rem the title is allowed one step larger than the page title — the
|
|
77
|
+
hero *is* the page title there, and the extra step is what separates a hero
|
|
78
|
+
from a heading with a picture next to it. */
|
|
79
|
+
@media (min-width: 60rem) {
|
|
80
|
+
.cg-hero__title {
|
|
81
|
+
font-size: calc(var(--cg-text-3xl) * 1.15);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.cg-hero__subtitle {
|
|
86
|
+
margin: var(--cg-s5) 0 0;
|
|
87
|
+
max-inline-size: var(--cg-measure-narrow);
|
|
88
|
+
font-size: var(--cg-text-lg);
|
|
89
|
+
line-height: var(--cg-leading-relaxed);
|
|
90
|
+
color: var(--cg-ink-muted);
|
|
91
|
+
text-wrap: pretty;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.cg-hero__media img {
|
|
95
|
+
inline-size: 100%;
|
|
96
|
+
border-radius: var(--cg-radius-xl);
|
|
97
|
+
box-shadow: var(--cg-elevation-3), var(--cg-ring);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* --- prose --------------------------------------------------------------- */
|
|
101
|
+
|
|
102
|
+
.cg-prose {
|
|
103
|
+
max-inline-size: var(--cg-measure);
|
|
104
|
+
margin-inline: auto;
|
|
105
|
+
/* Body copy is the one place in the theme that reads for minutes rather than
|
|
106
|
+
seconds, so it gets the loosest leading of the scale. */
|
|
107
|
+
line-height: var(--cg-leading-relaxed);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* A block's first and last child must not push the block's own rhythm around:
|
|
111
|
+
without these, a prose block that opens on a heading sits a full step lower
|
|
112
|
+
than one that opens on a paragraph. */
|
|
113
|
+
.cg-prose > :first-child {
|
|
114
|
+
margin-block-start: 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.cg-prose > :last-child {
|
|
118
|
+
margin-block-end: 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.cg-prose h2,
|
|
122
|
+
.cg-prose h3,
|
|
123
|
+
.cg-prose h4 {
|
|
124
|
+
letter-spacing: var(--cg-tracking-snug);
|
|
125
|
+
line-height: var(--cg-leading-snug);
|
|
126
|
+
font-weight: var(--cg-weight-bold);
|
|
127
|
+
text-wrap: balance;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* Asymmetric on purpose: the space above a heading belongs to the section it
|
|
131
|
+
opens, so it is much larger than the space below it. */
|
|
132
|
+
.cg-prose h2 {
|
|
133
|
+
margin-block: var(--cg-s12) var(--cg-s4);
|
|
134
|
+
font-size: var(--cg-text-xl);
|
|
135
|
+
letter-spacing: var(--cg-tracking-tight);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.cg-prose h3 {
|
|
139
|
+
margin-block: var(--cg-s10) var(--cg-s3);
|
|
140
|
+
font-size: var(--cg-text-lg);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.cg-prose h4 {
|
|
144
|
+
margin-block: var(--cg-s8) var(--cg-s2);
|
|
145
|
+
font-size: var(--cg-text-md);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.cg-prose p {
|
|
149
|
+
margin-block: var(--cg-s5);
|
|
150
|
+
/* Stops the one-word last line, which is the most visible defect in a
|
|
151
|
+
justified-looking column and costs nothing to avoid. */
|
|
152
|
+
text-wrap: pretty;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.cg-prose ul,
|
|
156
|
+
.cg-prose ol {
|
|
157
|
+
margin-block: var(--cg-s5);
|
|
158
|
+
padding-inline-start: var(--cg-s6);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.cg-prose li {
|
|
162
|
+
margin-block: var(--cg-s2);
|
|
163
|
+
padding-inline-start: var(--cg-s1);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/* The marker is the only decoration a list gets, so it is the one that carries
|
|
167
|
+
the accent. */
|
|
168
|
+
.cg-prose li::marker {
|
|
169
|
+
color: var(--cg-accent);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.cg-prose strong {
|
|
173
|
+
font-weight: var(--cg-weight-semibold);
|
|
174
|
+
color: var(--cg-ink);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* A quoted passage inside an article, not the `quote` block: a panel with a
|
|
178
|
+
rule, sized down from the pull quote so the two never compete. */
|
|
179
|
+
.cg-prose blockquote {
|
|
180
|
+
margin-block: var(--cg-s8);
|
|
181
|
+
margin-inline: 0;
|
|
182
|
+
padding: var(--cg-s5) var(--cg-s6);
|
|
183
|
+
border-inline-start: 3px solid var(--cg-accent);
|
|
184
|
+
border-start-end-radius: var(--cg-radius-md);
|
|
185
|
+
border-end-end-radius: var(--cg-radius-md);
|
|
186
|
+
background: var(--cg-surface-sunken);
|
|
187
|
+
color: var(--cg-ink-muted);
|
|
188
|
+
font-family: var(--cogenta-font-serif);
|
|
189
|
+
font-size: var(--cg-text-lg);
|
|
190
|
+
line-height: var(--cg-leading-normal);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.cg-prose blockquote > :first-child {
|
|
194
|
+
margin-block-start: 0;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.cg-prose blockquote > :last-child {
|
|
198
|
+
margin-block-end: 0;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.cg-prose code {
|
|
202
|
+
font-family: var(--cogenta-font-mono);
|
|
203
|
+
/* `em`, not a scale step: inline code has to match the size of the text it
|
|
204
|
+
sits in, and monospace faces read larger at the same nominal size. */
|
|
205
|
+
font-size: 0.875em;
|
|
206
|
+
background: var(--cg-surface-sunken);
|
|
207
|
+
border-radius: var(--cg-radius-sm);
|
|
208
|
+
box-shadow: var(--cg-ring);
|
|
209
|
+
padding: 0.15em 0.4em;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.cg-prose__figure {
|
|
213
|
+
margin-block: var(--cg-s8);
|
|
214
|
+
margin-inline: 0;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.cg-prose__figure img {
|
|
218
|
+
inline-size: 100%;
|
|
219
|
+
border-radius: var(--cg-radius-lg);
|
|
220
|
+
box-shadow: var(--cg-elevation-2);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.cg-prose__figure figcaption {
|
|
224
|
+
margin-block-start: var(--cg-s3);
|
|
225
|
+
color: var(--cg-ink-subtle);
|
|
226
|
+
font-size: var(--cg-text-sm);
|
|
227
|
+
line-height: var(--cg-leading-normal);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* --- mediaFigure --------------------------------------------------------- */
|
|
231
|
+
|
|
232
|
+
.cg-figure {
|
|
233
|
+
margin-inline: auto;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.cg-figure[data-align="start"],
|
|
237
|
+
.cg-figure[data-align="end"] {
|
|
238
|
+
max-inline-size: calc(var(--cg-measure) / 2);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.cg-figure[data-align="start"] {
|
|
242
|
+
float: inline-start;
|
|
243
|
+
margin-inline-end: var(--cg-s6);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.cg-figure[data-align="end"] {
|
|
247
|
+
float: inline-end;
|
|
248
|
+
margin-inline-start: var(--cg-s6);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.cg-figure[data-align="full"] {
|
|
252
|
+
max-inline-size: none;
|
|
253
|
+
padding-inline: 0;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.cg-figure__media {
|
|
257
|
+
inline-size: 100%;
|
|
258
|
+
aspect-ratio: var(--cg-ratio, auto);
|
|
259
|
+
border-radius: var(--cg-radius-lg);
|
|
260
|
+
box-shadow: var(--cg-elevation-2), var(--cg-ring);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* Full bleed keeps its corners square: a rounded corner against the viewport
|
|
264
|
+
edge reads as a mistake, not as a radius. */
|
|
265
|
+
.cg-figure[data-align="full"] .cg-figure__media {
|
|
266
|
+
border-radius: 0;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/* An indented caption with a rule, so it reads as annotation rather than as a
|
|
270
|
+
stray paragraph that happens to follow a picture. */
|
|
271
|
+
.cg-figure__caption {
|
|
272
|
+
margin-block-start: var(--cg-s4);
|
|
273
|
+
padding-inline-start: var(--cg-s4);
|
|
274
|
+
border-inline-start: 2px solid var(--cg-line-strong);
|
|
275
|
+
color: var(--cg-ink-subtle);
|
|
276
|
+
font-size: var(--cg-text-sm);
|
|
277
|
+
line-height: var(--cg-leading-normal);
|
|
278
|
+
text-wrap: pretty;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.cg-figure[data-align="full"] .cg-figure__caption {
|
|
282
|
+
max-inline-size: var(--cg-measure);
|
|
283
|
+
margin-inline: auto;
|
|
284
|
+
padding-inline: var(--cg-gutter);
|
|
285
|
+
border-inline-start: 0;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.cg-figure__credit {
|
|
289
|
+
color: var(--cg-ink-subtle);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.cg-figure__credit::before {
|
|
293
|
+
content: " — ";
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* --- featureGrid --------------------------------------------------------- */
|
|
297
|
+
|
|
298
|
+
.cg-features__items {
|
|
299
|
+
display: grid;
|
|
300
|
+
gap: var(--cg-gap);
|
|
301
|
+
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.cg-feature {
|
|
305
|
+
position: relative;
|
|
306
|
+
padding: var(--cg-s6);
|
|
307
|
+
background: var(--cg-surface-raised);
|
|
308
|
+
border-radius: var(--cg-radius-lg);
|
|
309
|
+
box-shadow: var(--cg-ring);
|
|
310
|
+
transition:
|
|
311
|
+
box-shadow var(--cg-duration) var(--cg-ease),
|
|
312
|
+
transform var(--cg-duration) var(--cg-ease);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.cg-feature:hover {
|
|
316
|
+
box-shadow: var(--cg-elevation-2), var(--cg-ring-accent);
|
|
317
|
+
transform: translateY(-2px);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/* The whole card becomes the link's hit area, without a second link for a
|
|
321
|
+
screen reader to announce: the overlay is drawn from the one anchor the
|
|
322
|
+
markup already has, whose accessible name is the feature's title. */
|
|
323
|
+
.cg-feature__link::after {
|
|
324
|
+
content: "";
|
|
325
|
+
position: absolute;
|
|
326
|
+
inset: 0;
|
|
327
|
+
border-radius: inherit;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.cg-feature__link {
|
|
331
|
+
color: inherit;
|
|
332
|
+
text-decoration: none;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.cg-feature:hover .cg-feature__link {
|
|
336
|
+
color: var(--cg-accent);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/* The vocabulary's `icon` names a symbol, never markup, and this theme ships no
|
|
340
|
+
icon set — so the chip is what the name gets: a tinted, ringed square that
|
|
341
|
+
holds the space an icon would, rather than a broken glyph or an invented one. */
|
|
342
|
+
.cg-feature__icon {
|
|
343
|
+
display: block;
|
|
344
|
+
inline-size: var(--cg-s10);
|
|
345
|
+
block-size: var(--cg-s10);
|
|
346
|
+
margin-block-end: var(--cg-s5);
|
|
347
|
+
background: var(--cg-accent-soft);
|
|
348
|
+
border-radius: var(--cg-radius-md);
|
|
349
|
+
box-shadow: var(--cg-ring-accent);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.cg-feature__title {
|
|
353
|
+
margin: 0 0 var(--cg-s2);
|
|
354
|
+
font-size: var(--cg-text-md);
|
|
355
|
+
font-weight: var(--cg-weight-semibold);
|
|
356
|
+
line-height: var(--cg-leading-snug);
|
|
357
|
+
letter-spacing: var(--cg-tracking-snug);
|
|
358
|
+
text-wrap: balance;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.cg-feature__text {
|
|
362
|
+
margin: 0;
|
|
363
|
+
color: var(--cg-ink-muted);
|
|
364
|
+
line-height: var(--cg-leading-relaxed);
|
|
365
|
+
text-wrap: pretty;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/* --- cta ----------------------------------------------------------------- */
|
|
369
|
+
|
|
370
|
+
.cg-cta {
|
|
371
|
+
position: relative;
|
|
372
|
+
overflow: hidden;
|
|
373
|
+
padding: var(--cg-s16) var(--cg-s8);
|
|
374
|
+
background: var(--cg-surface-tinted);
|
|
375
|
+
border-radius: var(--cg-radius-xl);
|
|
376
|
+
box-shadow: var(--cg-ring-accent);
|
|
377
|
+
text-align: center;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/* A single diagonal sheen of the accent. `overflow: hidden` on the panel keeps
|
|
381
|
+
it inside the radius, so it never leaks past a rounded corner. */
|
|
382
|
+
.cg-cta::before {
|
|
383
|
+
content: "";
|
|
384
|
+
position: absolute;
|
|
385
|
+
inset: 0;
|
|
386
|
+
background: linear-gradient(135deg, var(--cg-accent-soft), transparent 55%);
|
|
387
|
+
pointer-events: none;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/* The panel's own children have to sit above that sheen. */
|
|
391
|
+
.cg-cta > * {
|
|
392
|
+
position: relative;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.cg-cta__title {
|
|
396
|
+
margin: 0;
|
|
397
|
+
/* One step above a section heading: this block asks for something, and the
|
|
398
|
+
ask has to outrank the headings around it. */
|
|
399
|
+
font-size: var(--cg-text-2xl);
|
|
400
|
+
line-height: var(--cg-leading-snug);
|
|
401
|
+
letter-spacing: var(--cg-tracking-tight);
|
|
402
|
+
font-weight: var(--cg-weight-bold);
|
|
403
|
+
text-wrap: balance;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.cg-cta__text {
|
|
407
|
+
margin: var(--cg-s4) auto 0;
|
|
408
|
+
max-inline-size: var(--cg-measure-narrow);
|
|
409
|
+
color: var(--cg-ink-muted);
|
|
410
|
+
font-size: var(--cg-text-lg);
|
|
411
|
+
line-height: var(--cg-leading-relaxed);
|
|
412
|
+
text-wrap: pretty;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.cg-cta .cg-actions {
|
|
416
|
+
justify-content: center;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/* --- gallery ------------------------------------------------------------- */
|
|
420
|
+
|
|
421
|
+
.cg-gallery__items {
|
|
422
|
+
display: grid;
|
|
423
|
+
gap: var(--cg-gap-tight);
|
|
424
|
+
grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/* The frame, shared by all three layouts: it is what clips the hover zoom
|
|
428
|
+
below, so the picture can grow without the tile growing with it. */
|
|
429
|
+
.cg-gallery__item {
|
|
430
|
+
overflow: hidden;
|
|
431
|
+
border-radius: var(--cg-radius-lg);
|
|
432
|
+
box-shadow: var(--cg-ring);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.cg-gallery__item img {
|
|
436
|
+
inline-size: 100%;
|
|
437
|
+
block-size: 100%;
|
|
438
|
+
transition: transform var(--cg-duration-slow) var(--cg-ease);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.cg-gallery__item:hover img {
|
|
442
|
+
transform: scale(1.04);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/* A grid of pictures needs one shape, or the rows read as an accident. Masonry
|
|
446
|
+
is the one layout that opts out, since its whole point is not to. */
|
|
447
|
+
.cg-gallery:not([data-layout="masonry"]) .cg-gallery__item img {
|
|
448
|
+
aspect-ratio: 4 / 3;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.cg-gallery[data-layout="masonry"] .cg-gallery__items {
|
|
452
|
+
display: block;
|
|
453
|
+
columns: 3 14rem;
|
|
454
|
+
column-gap: var(--cg-gap-tight);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
.cg-gallery[data-layout="masonry"] .cg-gallery__item {
|
|
458
|
+
break-inside: avoid;
|
|
459
|
+
margin-block-end: var(--cg-gap-tight);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.cg-gallery[data-layout="carousel"] .cg-gallery__items {
|
|
463
|
+
display: grid;
|
|
464
|
+
grid-auto-flow: column;
|
|
465
|
+
grid-auto-columns: minmax(16rem, 40%);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.cg-gallery[data-layout="carousel"] .cg-gallery__item {
|
|
469
|
+
scroll-snap-align: start;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.cg-collection img {
|
|
473
|
+
inline-size: 100%;
|
|
474
|
+
border-radius: var(--cg-radius-md);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/* --- quote --------------------------------------------------------------- */
|
|
478
|
+
|
|
479
|
+
.cg-quote {
|
|
480
|
+
max-inline-size: var(--cg-page-narrow);
|
|
481
|
+
margin-inline: auto;
|
|
482
|
+
text-align: center;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/* The quotation mark, drawn rather than typed. The `/ ""` gives it an empty
|
|
486
|
+
alternative text, so a screen reader that supports it announces nothing —
|
|
487
|
+
the mark is decoration, and the `<blockquote>` already says what it means.
|
|
488
|
+
A browser without alt-text support reads one quote character, which is
|
|
489
|
+
harmless; hiding it with `aria-hidden` is impossible on a pseudo-element. */
|
|
490
|
+
.cg-quote::before {
|
|
491
|
+
content: "\201C" / "";
|
|
492
|
+
display: block;
|
|
493
|
+
margin-block-end: calc(var(--cg-s4) * -1);
|
|
494
|
+
color: var(--cg-accent-soft);
|
|
495
|
+
font-family: var(--cogenta-font-serif);
|
|
496
|
+
font-size: calc(var(--cg-text-3xl) * 2);
|
|
497
|
+
line-height: 1;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.cg-quote__text {
|
|
501
|
+
margin: 0;
|
|
502
|
+
font-family: var(--cogenta-font-serif);
|
|
503
|
+
/* A pull quote, so it outranks body copy by two steps and sits one below the
|
|
504
|
+
hero. Balanced wrap matters more here than anywhere: a centred quote with
|
|
505
|
+
a two-word last line looks broken. */
|
|
506
|
+
font-size: var(--cg-text-2xl);
|
|
507
|
+
line-height: var(--cg-leading-snug);
|
|
508
|
+
letter-spacing: var(--cg-tracking-snug);
|
|
509
|
+
text-wrap: balance;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.cg-quote__attribution {
|
|
513
|
+
display: flex;
|
|
514
|
+
align-items: center;
|
|
515
|
+
justify-content: center;
|
|
516
|
+
gap: var(--cg-s3);
|
|
517
|
+
margin-block-start: var(--cg-s8);
|
|
518
|
+
color: var(--cg-ink-subtle);
|
|
519
|
+
font-size: var(--cg-text-sm);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.cg-quote__avatar {
|
|
523
|
+
inline-size: var(--cg-s12);
|
|
524
|
+
block-size: var(--cg-s12);
|
|
525
|
+
border-radius: var(--cg-radius-pill);
|
|
526
|
+
box-shadow: var(--cg-ring);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.cg-quote__author {
|
|
530
|
+
font-weight: var(--cg-weight-semibold);
|
|
531
|
+
color: var(--cg-ink);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/* A hairline between name and role, instead of a comma the markup does not
|
|
535
|
+
have and the theme has no business inventing. */
|
|
536
|
+
.cg-quote__role::before {
|
|
537
|
+
content: "";
|
|
538
|
+
display: inline-block;
|
|
539
|
+
inline-size: var(--cg-s3);
|
|
540
|
+
border-block-start: 1px solid var(--cg-line-strong);
|
|
541
|
+
vertical-align: middle;
|
|
542
|
+
margin-inline-end: var(--cg-s3);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/* --- faq ----------------------------------------------------------------- */
|
|
546
|
+
|
|
547
|
+
.cg-faq {
|
|
548
|
+
max-inline-size: var(--cg-page-narrow);
|
|
549
|
+
margin-inline: auto;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.cg-faq__items {
|
|
553
|
+
background: var(--cg-surface-raised);
|
|
554
|
+
border-radius: var(--cg-radius-lg);
|
|
555
|
+
box-shadow: var(--cg-ring);
|
|
556
|
+
/* Clips the first and last rows to the container's radius, so the list reads
|
|
557
|
+
as one panel rather than as rows that happen to be stacked. */
|
|
558
|
+
overflow: hidden;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.cg-faq__item + .cg-faq__item {
|
|
562
|
+
border-block-start: 1px solid var(--cg-line-soft);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
.cg-faq__question {
|
|
566
|
+
display: flex;
|
|
567
|
+
align-items: center;
|
|
568
|
+
gap: var(--cg-s4);
|
|
569
|
+
padding: var(--cg-s5) var(--cg-s6);
|
|
570
|
+
font-weight: var(--cg-weight-semibold);
|
|
571
|
+
cursor: pointer;
|
|
572
|
+
/* The marker is replaced below; both properties are needed, and the vendor
|
|
573
|
+
one still is on WebKit. */
|
|
574
|
+
list-style: none;
|
|
575
|
+
transition: background-color var(--cg-duration) var(--cg-ease);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
.cg-faq__question::-webkit-details-marker {
|
|
579
|
+
display: none;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
.cg-faq__question:hover {
|
|
583
|
+
background: var(--cg-accent-soft);
|
|
584
|
+
color: var(--cg-accent-soft-fg);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/* A chevron drawn from two borders — no icon set, no glyph that depends on a
|
|
588
|
+
font shipping it. It is a pseudo-element on a `<summary>` whose expanded
|
|
589
|
+
state the browser already announces, so it stays purely visual. */
|
|
590
|
+
.cg-faq__question::after {
|
|
591
|
+
content: "";
|
|
592
|
+
flex: none;
|
|
593
|
+
margin-inline-start: auto;
|
|
594
|
+
inline-size: 0.5em;
|
|
595
|
+
block-size: 0.5em;
|
|
596
|
+
border-inline-end: 2px solid currentColor;
|
|
597
|
+
border-block-end: 2px solid currentColor;
|
|
598
|
+
transform: translateY(-0.15em) rotate(45deg);
|
|
599
|
+
transition: transform var(--cg-duration) var(--cg-ease);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
.cg-faq__details[open] .cg-faq__question::after {
|
|
603
|
+
transform: translateY(0.1em) rotate(-135deg);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.cg-faq__details[open] .cg-faq__question {
|
|
607
|
+
color: var(--cg-accent);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
.cg-faq__answer {
|
|
611
|
+
padding: 0 var(--cg-s6) var(--cg-s6);
|
|
612
|
+
color: var(--cg-ink-muted);
|
|
613
|
+
line-height: var(--cg-leading-relaxed);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
.cg-faq__answer > :first-child {
|
|
617
|
+
margin-block-start: 0;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
.cg-faq__answer > :last-child {
|
|
621
|
+
margin-block-end: 0;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/* --- stats --------------------------------------------------------------- */
|
|
625
|
+
|
|
626
|
+
.cg-stats__items {
|
|
627
|
+
display: grid;
|
|
628
|
+
gap: var(--cg-gap);
|
|
629
|
+
grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
|
|
630
|
+
margin: 0;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/* Reading order is label then figure; the stylesheet shows the figure first.
|
|
634
|
+
The markup is what a screen reader follows, so the two must not be
|
|
635
|
+
conflated — `column-reverse` reorders the paint, never the `<dl>`. */
|
|
636
|
+
.cg-stat {
|
|
637
|
+
display: flex;
|
|
638
|
+
flex-direction: column-reverse;
|
|
639
|
+
gap: var(--cg-s2);
|
|
640
|
+
padding: var(--cg-s6);
|
|
641
|
+
background: var(--cg-surface-sunken);
|
|
642
|
+
border-radius: var(--cg-radius-lg);
|
|
643
|
+
box-shadow: var(--cg-ring);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
.cg-stat__value {
|
|
647
|
+
display: flex;
|
|
648
|
+
align-items: baseline;
|
|
649
|
+
gap: var(--cg-s1);
|
|
650
|
+
margin: 0;
|
|
651
|
+
color: var(--cg-accent);
|
|
652
|
+
font-size: var(--cg-text-3xl);
|
|
653
|
+
font-weight: var(--cg-weight-bold);
|
|
654
|
+
line-height: var(--cg-leading-tight);
|
|
655
|
+
letter-spacing: var(--cg-tracking-tight);
|
|
656
|
+
/* Figures that change between pages must not change width with the digits. */
|
|
657
|
+
font-variant-numeric: tabular-nums;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
.cg-stat__unit {
|
|
661
|
+
font-size: var(--cg-text-lg);
|
|
662
|
+
font-weight: var(--cg-weight-medium);
|
|
663
|
+
color: var(--cg-ink-muted);
|
|
664
|
+
letter-spacing: var(--cg-tracking-normal);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/* Small caps under a large figure: the label is a caption for the number, and
|
|
668
|
+
at body size it competes with it. */
|
|
669
|
+
.cg-stat__label {
|
|
670
|
+
color: var(--cg-ink-muted);
|
|
671
|
+
font-size: var(--cg-text-xs);
|
|
672
|
+
font-weight: var(--cg-weight-semibold);
|
|
673
|
+
letter-spacing: var(--cg-tracking-wide);
|
|
674
|
+
text-transform: uppercase;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
/* --- logos --------------------------------------------------------------- */
|
|
678
|
+
|
|
679
|
+
/* A grid, not a wrapping row: a flex row leaves the last line ragged, and a
|
|
680
|
+
row of logos that does not line up reads as an oversight rather than as a
|
|
681
|
+
list of names. */
|
|
682
|
+
.cg-logos__items {
|
|
683
|
+
display: grid;
|
|
684
|
+
grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
|
|
685
|
+
gap: var(--cg-gap);
|
|
686
|
+
align-items: center;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
.cg-logo {
|
|
690
|
+
display: flex;
|
|
691
|
+
align-items: center;
|
|
692
|
+
justify-content: center;
|
|
693
|
+
padding: var(--cg-s4);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
.cg-logo__image {
|
|
697
|
+
/* One optical height for every logo, whatever its own proportions: `contain`
|
|
698
|
+
keeps a wide wordmark and a square mark at the same weight on the page. */
|
|
699
|
+
block-size: var(--cg-s10);
|
|
700
|
+
inline-size: auto;
|
|
701
|
+
max-inline-size: 100%;
|
|
702
|
+
object-fit: contain;
|
|
703
|
+
/* Held back so the logos read as a supporting row rather than as eight
|
|
704
|
+
competing brands, and brought fully forward on hover. Opacity, not
|
|
705
|
+
`grayscale`: a filtered logo is a logo whose owner did not approve it. */
|
|
706
|
+
opacity: 0.72;
|
|
707
|
+
transition: opacity var(--cg-duration) var(--cg-ease);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
.cg-logo__link {
|
|
711
|
+
display: inline-flex;
|
|
712
|
+
align-items: center;
|
|
713
|
+
justify-content: center;
|
|
714
|
+
inline-size: 100%;
|
|
715
|
+
border-radius: var(--cg-radius-md);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
.cg-logo:hover .cg-logo__image,
|
|
719
|
+
.cg-logo__link:focus-visible .cg-logo__image {
|
|
720
|
+
opacity: 1;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/* --- collectionList ------------------------------------------------------ */
|
|
724
|
+
|
|
725
|
+
.cg-collection__items {
|
|
726
|
+
display: grid;
|
|
727
|
+
gap: var(--cg-gap);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
.cg-collection[data-layout="grid"] .cg-collection__items {
|
|
731
|
+
grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
.cg-collection[data-layout="carousel"] .cg-collection__items {
|
|
735
|
+
grid-auto-flow: column;
|
|
736
|
+
grid-auto-columns: minmax(18rem, 45%);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
.cg-collection[data-layout="carousel"] .cg-entry {
|
|
740
|
+
scroll-snap-align: start;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
.cg-entry {
|
|
744
|
+
position: relative;
|
|
745
|
+
padding: var(--cg-s6);
|
|
746
|
+
background: var(--cg-surface-raised);
|
|
747
|
+
border-radius: var(--cg-radius-lg);
|
|
748
|
+
box-shadow: var(--cg-ring);
|
|
749
|
+
transition:
|
|
750
|
+
box-shadow var(--cg-duration) var(--cg-ease),
|
|
751
|
+
transform var(--cg-duration) var(--cg-ease);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
.cg-entry:hover {
|
|
755
|
+
box-shadow: var(--cg-elevation-2), var(--cg-ring-accent);
|
|
756
|
+
transform: translateY(-2px);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/* Same overlay as `featureGrid`: the card is the hit area, drawn from the one
|
|
760
|
+
link the markup has, so nothing extra is announced. */
|
|
761
|
+
.cg-entry__link {
|
|
762
|
+
color: inherit;
|
|
763
|
+
text-decoration: none;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
.cg-entry__link::after {
|
|
767
|
+
content: "";
|
|
768
|
+
position: absolute;
|
|
769
|
+
inset: 0;
|
|
770
|
+
border-radius: inherit;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
.cg-entry:hover .cg-entry__link {
|
|
774
|
+
color: var(--cg-accent);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/* Date first in the paint, title under it: a list of entries is scanned by
|
|
778
|
+
recency, and the markup still reads title then date. */
|
|
779
|
+
.cg-entry__body {
|
|
780
|
+
display: flex;
|
|
781
|
+
flex-direction: column;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
.cg-entry__title {
|
|
785
|
+
order: 2;
|
|
786
|
+
margin: 0;
|
|
787
|
+
font-size: var(--cg-text-lg);
|
|
788
|
+
font-weight: var(--cg-weight-semibold);
|
|
789
|
+
line-height: var(--cg-leading-snug);
|
|
790
|
+
letter-spacing: var(--cg-tracking-snug);
|
|
791
|
+
text-wrap: balance;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
.cg-entry__date {
|
|
795
|
+
order: 1;
|
|
796
|
+
margin-block-end: var(--cg-s2);
|
|
797
|
+
color: var(--cg-ink-subtle);
|
|
798
|
+
font-size: var(--cg-text-xs);
|
|
799
|
+
font-weight: var(--cg-weight-semibold);
|
|
800
|
+
letter-spacing: var(--cg-tracking-wide);
|
|
801
|
+
text-transform: uppercase;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
.cg-entry__excerpt {
|
|
805
|
+
order: 3;
|
|
806
|
+
margin: var(--cg-s3) 0 0;
|
|
807
|
+
color: var(--cg-ink-muted);
|
|
808
|
+
font-size: var(--cg-text-sm);
|
|
809
|
+
line-height: var(--cg-leading-relaxed);
|
|
810
|
+
/* Three lines, so cards in a row keep their heights close whatever the
|
|
811
|
+
editor wrote. Nothing is lost: the entry's own page carries the full text. */
|
|
812
|
+
display: -webkit-box;
|
|
813
|
+
-webkit-box-orient: vertical;
|
|
814
|
+
-webkit-line-clamp: 3;
|
|
815
|
+
line-clamp: 3;
|
|
816
|
+
overflow: hidden;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
.cg-collection__empty {
|
|
820
|
+
padding: var(--cg-s8);
|
|
821
|
+
background: var(--cg-surface-sunken);
|
|
822
|
+
border-radius: var(--cg-radius-lg);
|
|
823
|
+
color: var(--cg-ink-subtle);
|
|
824
|
+
text-align: center;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/* --- embed --------------------------------------------------------------- */
|
|
828
|
+
|
|
829
|
+
.cg-embed {
|
|
830
|
+
aspect-ratio: var(--cg-ratio, 16 / 9);
|
|
831
|
+
max-inline-size: var(--cg-measure);
|
|
832
|
+
margin-inline: auto;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
.cg-embed__frame {
|
|
836
|
+
inline-size: 100%;
|
|
837
|
+
block-size: 100%;
|
|
838
|
+
border: 0;
|
|
839
|
+
border-radius: var(--cg-radius-md);
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/* The consent placeholder is a real card, not a grey box: it says what would be
|
|
843
|
+
loaded and offers a way out to the source. It requests nothing third-party. */
|
|
844
|
+
.cg-embed__placeholder {
|
|
845
|
+
display: flex;
|
|
846
|
+
flex-direction: column;
|
|
847
|
+
align-items: center;
|
|
848
|
+
justify-content: center;
|
|
849
|
+
gap: var(--cg-s5);
|
|
850
|
+
block-size: 100%;
|
|
851
|
+
padding: var(--cg-s8);
|
|
852
|
+
background: var(--cg-surface-sunken);
|
|
853
|
+
color: var(--cg-ink-muted);
|
|
854
|
+
/* Dashed, and kept dashed: it is the one place in the theme where the border
|
|
855
|
+
says "nothing has loaded here yet" rather than "this is a panel". */
|
|
856
|
+
border: 1px dashed var(--cg-line-strong);
|
|
857
|
+
border-radius: var(--cg-radius-lg);
|
|
858
|
+
text-align: center;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
.cg-embed__notice {
|
|
862
|
+
margin: 0;
|
|
863
|
+
max-inline-size: var(--cg-measure-narrow);
|
|
864
|
+
line-height: var(--cg-leading-relaxed);
|
|
865
|
+
text-wrap: pretty;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
/* The way out is an action, not a sentence with a link in it: this card exists
|
|
869
|
+
because the visitor has to choose, and a choice needs something to press. */
|
|
870
|
+
.cg-embed__link {
|
|
871
|
+
display: inline-flex;
|
|
872
|
+
align-items: center;
|
|
873
|
+
min-block-size: 44px;
|
|
874
|
+
padding: var(--cg-s3) var(--cg-s6);
|
|
875
|
+
background: var(--cg-surface-raised);
|
|
876
|
+
color: var(--cg-accent);
|
|
877
|
+
border-radius: var(--cg-radius-md);
|
|
878
|
+
box-shadow: var(--cg-ring-accent);
|
|
879
|
+
font-size: var(--cg-text-sm);
|
|
880
|
+
font-weight: var(--cg-weight-semibold);
|
|
881
|
+
text-decoration: none;
|
|
882
|
+
transition:
|
|
883
|
+
background-color var(--cg-duration) var(--cg-ease),
|
|
884
|
+
color var(--cg-duration) var(--cg-ease);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
.cg-embed__link:hover {
|
|
888
|
+
background: var(--cg-accent);
|
|
889
|
+
color: var(--cg-accent-fg);
|
|
890
|
+
}
|