@cronos-labs/ui 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/FilterPillGroup/index.d.ts +1 -0
  2. package/dist/FilterPillGroup/index.js +1 -0
  3. package/dist/FilterPillGroup/styles.d.ts +8 -0
  4. package/dist/FilterPillGroup/styles.js +36 -0
  5. package/dist/Footer/index.d.ts +34 -0
  6. package/dist/Footer/index.js +48 -0
  7. package/dist/Footer/styles.d.ts +97 -0
  8. package/dist/Footer/styles.js +326 -0
  9. package/dist/Header/NavDropdown.d.ts +17 -0
  10. package/dist/Header/NavDropdown.js +39 -0
  11. package/dist/Header/helper.d.ts +1 -0
  12. package/dist/Header/helper.js +1 -0
  13. package/dist/Header/index.d.ts +46 -0
  14. package/dist/Header/index.js +162 -0
  15. package/dist/Header/styles.d.ts +158 -0
  16. package/dist/Header/styles.js +770 -0
  17. package/dist/LocaleSelector/index.d.ts +9 -0
  18. package/dist/LocaleSelector/index.js +62 -0
  19. package/dist/LocaleSelector/styles.d.ts +33 -0
  20. package/dist/LocaleSelector/styles.js +156 -0
  21. package/dist/SearchField/index.d.ts +14 -0
  22. package/dist/SearchField/index.js +6 -0
  23. package/dist/SearchField/styles.d.ts +20 -0
  24. package/dist/SearchField/styles.js +85 -0
  25. package/dist/SectionAnchorNav/index.d.ts +11 -0
  26. package/dist/SectionAnchorNav/index.js +41 -0
  27. package/dist/SectionAnchorNav/styles.d.ts +22 -0
  28. package/dist/SectionAnchorNav/styles.js +74 -0
  29. package/dist/Seo/index.d.ts +18 -0
  30. package/dist/Seo/index.js +53 -0
  31. package/dist/Seo/types.d.ts +4 -0
  32. package/dist/Seo/types.js +1 -0
  33. package/dist/config/eslintConfig.d.ts +6 -0
  34. package/dist/config/eslintConfig.js +31 -0
  35. package/dist/config/prettierConfig.d.ts +8 -0
  36. package/dist/config/prettierConfig.js +8 -0
  37. package/dist/format.d.ts +3 -0
  38. package/dist/format.js +9 -0
  39. package/dist/index.d.ts +21 -0
  40. package/dist/index.js +18 -0
  41. package/dist/locale/config.d.ts +5 -0
  42. package/dist/locale/config.js +103 -0
  43. package/dist/locale/hooks.d.ts +3 -0
  44. package/dist/locale/hooks.js +36 -0
  45. package/dist/locale/localePreference.d.ts +7 -0
  46. package/dist/locale/localePreference.js +21 -0
  47. package/dist/locale/localization.d.ts +11 -0
  48. package/dist/locale/localization.js +12 -0
  49. package/dist/locale/routes.d.ts +5 -0
  50. package/dist/locale/routes.js +32 -0
  51. package/dist/locale/types.d.ts +25 -0
  52. package/dist/locale/types.js +1 -0
  53. package/dist/navigationContent.d.ts +59 -0
  54. package/dist/navigationContent.js +552 -0
  55. package/dist/scrollToSectionHeading.d.ts +1 -0
  56. package/dist/scrollToSectionHeading.js +12 -0
  57. package/dist/tokens.d.ts +252 -0
  58. package/dist/tokens.js +253 -0
  59. package/dist/typography.d.ts +11 -0
  60. package/dist/typography.js +106 -0
  61. package/eslint-config.js +1 -0
  62. package/package.json +98 -0
  63. package/prettier-config.js +1 -0
@@ -0,0 +1,770 @@
1
+ import styled from 'styled-components';
2
+ export const HeaderFrame = styled.div `
3
+ position: fixed;
4
+ top: 0;
5
+ left: 0;
6
+ right: 0;
7
+ z-index: ${({ theme }) => theme.zIndex.sticky};
8
+ isolation: isolate;
9
+ padding-top: ${({ theme }) => theme.sizes.xxs};
10
+ padding-bottom: ${({ theme }) => theme.sizes.xxs};
11
+
12
+ &::before {
13
+ content: '';
14
+ position: absolute;
15
+ inset: 0;
16
+ z-index: -1;
17
+ background-color: ${({ $backgroundColor, $mobileBackgroundColor, $contrast }) => $contrast ? 'transparent' : ($mobileBackgroundColor ?? $backgroundColor)};
18
+ transition: ${({ $shouldAnimatePresentation, theme }) => $shouldAnimatePresentation
19
+ ? `background-color ${theme.motion.duration.surface} ${theme.motion.easing.emphasized}`
20
+ : 'none'};
21
+ }
22
+
23
+ ${({ theme }) => theme.mediaQueries.lg} {
24
+ padding-top: ${({ theme }) => theme.sizes.xs};
25
+ padding-bottom: ${({ theme }) => theme.sizes.sm};
26
+
27
+ &::before {
28
+ background-color: ${({ $backgroundColor, $contrast }) => $contrast ? 'transparent' : $backgroundColor};
29
+ }
30
+ }
31
+ `;
32
+ const scaleExpr = 'clamp(0, (var(--home-transition, 0) - 0.05) / 0.75, 1) * 9';
33
+ const bandHalf = `calc(7.5vw * (${scaleExpr}))`;
34
+ const blackTopEdge = `calc(70vh - (var(--home-transition, 0) * 20vh) - (16.34vw * (${scaleExpr})))`;
35
+ const verticalGradient = `linear-gradient(
36
+ to bottom,
37
+ #000 0,
38
+ #000 ${blackTopEdge},
39
+ #fff ${blackTopEdge},
40
+ #fff 100%
41
+ )`;
42
+ const columnGradient = `linear-gradient(
43
+ 90deg,
44
+ #000 0,
45
+ #000 calc(50vw - ${bandHalf}),
46
+ #fff calc(50vw - ${bandHalf}),
47
+ #fff calc(50vw + ${bandHalf}),
48
+ #000 calc(50vw + ${bandHalf})
49
+ )`;
50
+ const splitBackground = `
51
+ background-image: ${verticalGradient}, ${columnGradient};
52
+ background-blend-mode: multiply;
53
+ background-attachment: fixed, fixed;
54
+ background-size: 100vw 100vh, 100vw 100vh;
55
+ background-repeat: no-repeat, no-repeat;
56
+ `;
57
+ /**
58
+ * Builds a viewport-fixed gradient that swaps two colours bottom-to-top at the
59
+ * dark shape's rising edge (no column constraint).
60
+ *
61
+ * @param top - Colour shown above the rising edge.
62
+ * @param bottom - Colour shown below the rising edge.
63
+ * @returns A CSS declaration block (`background-image` + fixed attachment).
64
+ */
65
+ const verticalSwap = (top, bottom) => `
66
+ background-image: linear-gradient(
67
+ to bottom,
68
+ ${top} 0,
69
+ ${top} ${blackTopEdge},
70
+ ${bottom} ${blackTopEdge},
71
+ ${bottom} 100%
72
+ );
73
+ background-attachment: fixed;
74
+ background-size: 100vw 100vh;
75
+ background-repeat: no-repeat;
76
+ `;
77
+ /**
78
+ * As {@link verticalSwap}, but clips the gradient to the glyphs so it replaces
79
+ * the text colour instead of painting a background.
80
+ *
81
+ * @param top - Glyph colour above the rising edge.
82
+ * @param bottom - Glyph colour below the rising edge.
83
+ * @returns A CSS declaration block with `background-clip: text`.
84
+ */
85
+ const verticalSwapText = (top, bottom) => `
86
+ color: transparent;
87
+ ${verticalSwap(top, bottom)}
88
+ -webkit-background-clip: text;
89
+ background-clip: text;
90
+ `;
91
+ export const HeaderShell = styled.header `
92
+ width: min(
93
+ calc(100% - (2 * ${({ theme }) => theme.layout.pagePaddingInline})),
94
+ ${({ theme }) => theme.layout.landingWideWidth}
95
+ );
96
+ margin: 0 auto;
97
+ display: grid;
98
+ grid-template-columns: 1fr auto;
99
+ align-items: center;
100
+ gap: ${({ theme }) => theme.sizes.xl};
101
+ color: ${({ $textColor, $mobileTextColor }) => $mobileTextColor ?? $textColor};
102
+ transition: ${({ $shouldAnimatePresentation, theme }) => $shouldAnimatePresentation
103
+ ? `color ${theme.motion.duration.surface} ${theme.motion.easing.emphasized}`
104
+ : 'none'};
105
+
106
+ ${({ theme }) => theme.mediaQueries.lg} {
107
+ grid-template-columns:
108
+ calc(
109
+ ${({ theme }) => theme.layout.brandLogoMarkSize} +
110
+ ${({ theme }) => theme.layout.brandLogoGap} +
111
+ ${({ theme }) => theme.layout.brandWordmarkWidth}
112
+ )
113
+ minmax(0, 1fr)
114
+ max-content;
115
+ color: ${({ $textColor }) => $textColor};
116
+ }
117
+ `;
118
+ export const HeaderBrandLink = styled.a `
119
+ display: inline-flex;
120
+ align-items: center;
121
+ gap: ${({ theme }) => theme.layout.brandLogoGap};
122
+ width: fit-content;
123
+ text-decoration: none;
124
+
125
+ &:focus-visible {
126
+ outline: 2px solid currentColor;
127
+ outline-offset: 6px;
128
+ border-radius: 4px;
129
+ }
130
+ `;
131
+ export const LogoMark = styled.img `
132
+ display: block;
133
+ width: ${({ theme }) => theme.layout.brandLogoMarkSize};
134
+ height: ${({ theme }) => theme.layout.brandLogoMarkSize};
135
+ object-fit: contain;
136
+ filter: ${({ $variant, $mobileVariant }) => ($mobileVariant ?? $variant) === 'inverse' ? 'invert(1) brightness(100)' : 'none'};
137
+ transition: ${({ $shouldAnimatePresentation, theme }) => $shouldAnimatePresentation
138
+ ? `filter ${theme.motion.duration.surface} ${theme.motion.easing.emphasized}`
139
+ : 'none'};
140
+
141
+ ${({ theme }) => theme.mediaQueries.lg} {
142
+ filter: ${({ $variant }) => ($variant === 'inverse' ? 'invert(1) brightness(100)' : 'none')};
143
+ }
144
+ `;
145
+ export const Wordmark = styled.img `
146
+ width: ${({ theme }) => theme.layout.brandWordmarkWidth};
147
+ height: ${({ theme }) => theme.layout.brandWordmarkHeight};
148
+ object-fit: contain;
149
+ filter: ${({ $variant, $mobileVariant }) => ($mobileVariant ?? $variant) === 'inverse' ? 'invert(1) brightness(100)' : 'none'};
150
+ transition: ${({ $shouldAnimatePresentation, theme }) => $shouldAnimatePresentation
151
+ ? `filter ${theme.motion.duration.surface} ${theme.motion.easing.emphasized}`
152
+ : 'none'};
153
+
154
+ ${({ theme }) => theme.mediaQueries.lg} {
155
+ filter: ${({ $variant }) => ($variant === 'inverse' ? 'invert(1) brightness(100)' : 'none')};
156
+ }
157
+ `;
158
+ const splitGlyphMask = (url) => `
159
+ ${splitBackground}
160
+ -webkit-mask: url("${url}") no-repeat center / contain;
161
+ mask: url("${url}") no-repeat center / contain;
162
+ `;
163
+ export const BrandMarkMask = styled.span `
164
+ display: block;
165
+ width: ${({ theme }) => theme.layout.brandLogoMarkSize};
166
+ height: ${({ theme }) => theme.layout.brandLogoMarkSize};
167
+ ${({ $url }) => splitGlyphMask($url)}
168
+ `;
169
+ export const BrandWordmarkMask = styled.span `
170
+ display: block;
171
+ width: ${({ theme }) => theme.layout.brandWordmarkWidth};
172
+ height: ${({ theme }) => theme.layout.brandWordmarkHeight};
173
+ ${({ $url }) => splitGlyphMask($url)}
174
+
175
+ @media (max-width: 1023px) {
176
+ background: #000;
177
+ }
178
+ `;
179
+ export const Nav = styled.nav `
180
+ display: flex;
181
+ justify-content: center;
182
+ align-items: center;
183
+ gap: 50px;
184
+ display: none;
185
+
186
+ > a,
187
+ > span[aria-disabled='true'] {
188
+ display: inline-flex;
189
+ align-items: center;
190
+ gap: 5px;
191
+ font-size: 1rem;
192
+ font-weight: ${({ theme }) => theme.fontWeight.semibold};
193
+ letter-spacing: -0.02em;
194
+ line-height: ${({ theme }) => theme.typography.lineHeight.tight};
195
+ color: inherit;
196
+ transition:
197
+ opacity ${({ theme }) => theme.motion.duration.fast}
198
+ ${({ theme }) => theme.motion.easing.standard},
199
+ transform ${({ theme }) => theme.motion.duration.fast}
200
+ ${({ theme }) => theme.motion.easing.standard};
201
+ }
202
+
203
+ > a {
204
+ &:hover {
205
+ opacity: 0.7;
206
+ transform: translateY(-1px);
207
+ }
208
+
209
+ &:focus-visible {
210
+ outline: 2px solid currentColor;
211
+ outline-offset: 6px;
212
+ text-decoration: underline;
213
+ text-decoration-thickness: 2px;
214
+ text-underline-offset: 6px;
215
+ }
216
+ }
217
+
218
+ > span[aria-disabled='true'] {
219
+ cursor: default;
220
+ color: ${({ $variant, theme }) => $variant === 'inverse' ? 'rgb(255 255 255 / 0.48)' : theme.colors.textDarkSubtle};
221
+ opacity: ${({ $variant }) => ($variant === 'inverse' ? 1 : 0.58)};
222
+ }
223
+
224
+ > span[aria-disabled='true'] > span {
225
+ background: ${({ $variant }) => $variant === 'inverse' ? '#272727' : 'rgb(255 255 255 / 0.42)'};
226
+ color: ${({ $variant }) => $variant === 'inverse' ? 'rgb(255 255 255 / 0.5)' : 'currentColor'};
227
+ box-shadow: ${({ $variant }) => $variant === 'inverse' ? 'none' : 'inset 0 0 0 1px rgb(0 0 0 / 0.08)'};
228
+ }
229
+
230
+ ${({ $contrast }) => $contrast
231
+ ? `
232
+ && > a {
233
+ color: transparent;
234
+ ${splitBackground}
235
+ -webkit-background-clip: text;
236
+ background-clip: text;
237
+ }
238
+
239
+ && > span[aria-disabled='true'] {
240
+ opacity: calc(0.58 + 0.42 * clamp(0, (var(--home-transition, 0) - 0.18) / 0.07, 1));
241
+ ${verticalSwapText('#7d828b', 'rgb(255 255 255 / 0.48)')}
242
+ }
243
+
244
+ && > span[aria-disabled='true'] > span {
245
+ background-color: transparent;
246
+ ${verticalSwap('rgb(255 255 255 / 0.42)', '#272727')}
247
+ box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.08);
248
+ }
249
+
250
+ && > span[aria-disabled='true'] > span > span {
251
+ ${verticalSwapText('#7d828b', 'rgb(255 255 255 / 0.5)')}
252
+ }
253
+ `
254
+ : ''}
255
+
256
+ ${({ theme }) => theme.mediaQueries.lg} {
257
+ display: flex;
258
+ }
259
+ `;
260
+ export const NavDropdown = styled.div `
261
+ position: relative;
262
+ display: inline-flex;
263
+ align-items: center;
264
+ `;
265
+ export const NavDropdownTrigger = styled.button `
266
+ display: inline-flex;
267
+ align-items: center;
268
+ gap: 5px;
269
+ margin: 0;
270
+ padding: 0;
271
+ border: 0;
272
+ background: transparent;
273
+ color: inherit;
274
+ cursor: pointer;
275
+ font-family: inherit;
276
+ font-size: 1rem;
277
+ font-weight: ${({ theme }) => theme.fontWeight.semibold};
278
+ letter-spacing: -0.02em;
279
+ line-height: ${({ theme }) => theme.typography.lineHeight.tight};
280
+ transition:
281
+ opacity ${({ theme }) => theme.motion.duration.fast}
282
+ ${({ theme }) => theme.motion.easing.standard},
283
+ transform ${({ theme }) => theme.motion.duration.fast}
284
+ ${({ theme }) => theme.motion.easing.standard};
285
+
286
+ &:hover {
287
+ opacity: 0.7;
288
+ transform: translateY(-1px);
289
+ }
290
+
291
+ &:focus-visible {
292
+ outline: 2px solid currentColor;
293
+ outline-offset: 6px;
294
+ border-radius: 4px;
295
+ }
296
+ `;
297
+ export const NavDropdownLabel = styled.span `
298
+ ${({ $contrast }) => $contrast
299
+ ? `
300
+ color: transparent;
301
+ ${splitBackground}
302
+ -webkit-background-clip: text;
303
+ background-clip: text;
304
+ `
305
+ : ''}
306
+ `;
307
+ export const NavDropdownChevron = styled.span `
308
+ position: relative;
309
+ width: 12px;
310
+ height: 12px;
311
+ flex: 0 0 auto;
312
+
313
+ &::before {
314
+ content: '';
315
+ position: absolute;
316
+ top: 50%;
317
+ left: 50%;
318
+ width: 7px;
319
+ height: 7px;
320
+ border-right: 2px solid currentColor;
321
+ border-bottom: 2px solid currentColor;
322
+ /* Offset the rotated caret so its visual mass stays centred in both states. */
323
+ transform: translate(-50%, ${({ $open }) => ($open ? '-25%' : '-75%')})
324
+ rotate(${({ $open }) => ($open ? '225deg' : '45deg')});
325
+ transform-origin: center;
326
+ transition: transform ${({ theme }) => theme.motion.duration.fast}
327
+ ${({ theme }) => theme.motion.easing.standard};
328
+ }
329
+ `;
330
+ export const NavDropdownMenu = styled.div `
331
+ position: absolute;
332
+ top: calc(100% + 12px);
333
+ left: 50%;
334
+ transform: translateX(-50%);
335
+ /* Stay mounted and toggle display so the menu never lays out in-flow on first
336
+ open (which caused a one-frame layout shift when it mounted on hover). */
337
+ display: ${({ $open }) => ($open ? 'block' : 'none')};
338
+ z-index: ${({ theme }) => theme.zIndex.overlay};
339
+ width: max-content;
340
+ min-width: 180px;
341
+ padding: 4px;
342
+ border: 1px solid #f2f2f3;
343
+ border-radius: 12px;
344
+ background: ${({ theme }) => theme.colors.light};
345
+ color: ${({ theme }) => theme.colors.ink};
346
+ box-shadow:
347
+ 0 15px 20px -5px rgb(38 38 38 / 0.1),
348
+ 0 5px 10px -5px rgb(38 38 38 / 0.05);
349
+
350
+ /* Invisible bridge so the pointer can cross the gap without closing on hover. */
351
+ &::before {
352
+ content: '';
353
+ position: absolute;
354
+ top: -12px;
355
+ left: 0;
356
+ right: 0;
357
+ height: 12px;
358
+ }
359
+ `;
360
+ export const NavDropdownList = styled.div `
361
+ display: grid;
362
+ gap: 2px;
363
+ `;
364
+ export const NavDropdownOption = styled.a `
365
+ display: flex;
366
+ align-items: center;
367
+ gap: 8px;
368
+ width: 100%;
369
+ min-height: 44px;
370
+ padding: 12px;
371
+ border-radius: 8px;
372
+ background: transparent;
373
+ color: inherit;
374
+ font-size: 1rem;
375
+ font-weight: ${({ theme }) => theme.fontWeight.semibold};
376
+ letter-spacing: -0.02em;
377
+ line-height: ${({ theme }) => theme.typography.lineHeight.tight};
378
+ text-align: left;
379
+ text-decoration: none;
380
+ white-space: nowrap;
381
+
382
+ &:hover {
383
+ background: ${({ theme }) => theme.colors.brandSky};
384
+ }
385
+
386
+ &:focus-visible {
387
+ outline: 2px solid currentColor;
388
+ outline-offset: -3px;
389
+ }
390
+ `;
391
+ export const NavBadge = styled.span `
392
+ display: inline-flex;
393
+ align-items: center;
394
+ justify-content: center;
395
+ min-width: 44px;
396
+ height: 18px;
397
+ padding: 0 8px;
398
+ border-radius: ${({ theme }) => theme.borderRadius.pill};
399
+ background: ${({ theme }) => theme.colors.accentSky};
400
+ color: ${({ theme }) => theme.colors.light};
401
+ font-size: 0.5rem;
402
+ font-weight: ${({ theme }) => theme.fontWeight.black};
403
+ letter-spacing: 0.1em;
404
+ line-height: ${({ theme }) => theme.typography.lineHeight.tight};
405
+ text-transform: uppercase;
406
+ white-space: nowrap;
407
+ `;
408
+ export const NavBadgeLabel = styled.span `
409
+ display: inline-block;
410
+ `;
411
+ export const HeaderAction = styled.a `
412
+ width: auto;
413
+ min-height: 44px;
414
+ justify-self: end;
415
+ display: none;
416
+ align-items: center;
417
+ justify-content: center;
418
+ gap: 4px;
419
+ padding: 11px 18px;
420
+ border-radius: ${({ theme }) => theme.borderRadius.capsule};
421
+ background-color: ${({ $variant, theme }) => $variant === 'inverse' ? theme.colors.light : theme.colors.brandSky};
422
+ background-image: ${({ $variant, theme }) => $variant === 'inverse'
423
+ ? 'none'
424
+ : `linear-gradient(90deg, ${theme.colors.light} 0, ${theme.colors.light} calc(50vw + ${bandHalf}), transparent calc(50vw + ${bandHalf}))`};
425
+ background-attachment: fixed;
426
+ background-size: 100vw 100vh;
427
+ background-repeat: no-repeat;
428
+ color: ${({ theme }) => theme.colors.ink};
429
+ font-size: 0.92rem;
430
+ font-weight: ${({ theme }) => theme.fontWeight.bold};
431
+ letter-spacing: 0;
432
+ line-height: 1.25;
433
+ white-space: nowrap;
434
+ transition:
435
+ transform ${({ theme }) => theme.motion.duration.fast}
436
+ ${({ theme }) => theme.motion.easing.standard},
437
+ opacity ${({ theme }) => theme.motion.duration.fast}
438
+ ${({ theme }) => theme.motion.easing.standard};
439
+
440
+ &:hover {
441
+ opacity: 0.96;
442
+ transform: translateY(-1px);
443
+ }
444
+
445
+ &:focus-visible {
446
+ outline: 2px solid
447
+ ${({ $variant, theme }) => ($variant === 'inverse' ? theme.colors.light : theme.colors.ink)};
448
+ outline-offset: 3px;
449
+ }
450
+
451
+ ${({ theme }) => theme.mediaQueries.lg} {
452
+ display: inline-flex;
453
+ min-width: ${({ theme }) => theme.layout.headerActionWidth};
454
+ width: max-content;
455
+ min-height: 50px;
456
+ padding: 12px;
457
+ font-size: 1rem;
458
+ }
459
+ `;
460
+ export const HeaderActionArrow = styled.span `
461
+ font-size: 1rem;
462
+ line-height: 1;
463
+ justify-self: end;
464
+ `;
465
+ export const HeaderMobileToggle = styled.button `
466
+ display: inline-flex;
467
+ align-items: center;
468
+ justify-content: center;
469
+ width: 40px;
470
+ height: 40px;
471
+ padding: 0;
472
+ border: 0;
473
+ border-radius: ${({ theme }) => theme.borderRadius.pill};
474
+ background: ${({ theme }) => theme.colors.light};
475
+ color: ${({ theme }) => theme.colors.ink};
476
+ box-shadow: ${({ theme }) => theme.shadow.button};
477
+ transition:
478
+ transform ${({ theme }) => theme.motion.duration.fast}
479
+ ${({ theme }) => theme.motion.easing.standard},
480
+ box-shadow ${({ theme }) => theme.motion.duration.fast}
481
+ ${({ theme }) => theme.motion.easing.standard};
482
+
483
+ &:hover {
484
+ transform: translateY(-1px);
485
+ box-shadow: ${({ theme }) => theme.shadow.md};
486
+ }
487
+
488
+ &:focus-visible {
489
+ outline: none;
490
+ box-shadow:
491
+ 0 0 0 3px ${({ theme }) => theme.colors.focusRing},
492
+ ${({ theme }) => theme.shadow.button};
493
+ }
494
+
495
+ ${({ theme }) => theme.mediaQueries.lg} {
496
+ display: none;
497
+ }
498
+ `;
499
+ export const HeaderMobileToggleIcon = styled.span `
500
+ position: relative;
501
+ display: block;
502
+ width: 20px;
503
+ height: 20px;
504
+
505
+ &::before,
506
+ &::after,
507
+ span {
508
+ content: '';
509
+ position: absolute;
510
+ left: 2px;
511
+ right: 2px;
512
+ height: 2px;
513
+ border-radius: ${({ theme }) => theme.borderRadius.pill};
514
+ background: currentColor;
515
+ transition:
516
+ transform ${({ theme }) => theme.motion.duration.fast}
517
+ ${({ theme }) => theme.motion.easing.standard},
518
+ top ${({ theme }) => theme.motion.duration.fast}
519
+ ${({ theme }) => theme.motion.easing.standard},
520
+ opacity ${({ theme }) => theme.motion.duration.fast}
521
+ ${({ theme }) => theme.motion.easing.standard};
522
+ }
523
+
524
+ &::before {
525
+ top: ${({ $open }) => ($open ? '9px' : '5px')};
526
+ transform: ${({ $open }) => ($open ? 'rotate(45deg)' : 'none')};
527
+ }
528
+
529
+ span {
530
+ top: 9px;
531
+ opacity: ${({ $open }) => ($open ? 0 : 1)};
532
+ }
533
+
534
+ &::after {
535
+ top: ${({ $open }) => ($open ? '9px' : '13px')};
536
+ transform: ${({ $open }) => ($open ? 'rotate(-45deg)' : 'none')};
537
+ }
538
+ `;
539
+ export const HeaderMobileOverlay = styled.div `
540
+ position: fixed;
541
+ inset: 0;
542
+ z-index: ${({ theme }) => theme.zIndex.overlay};
543
+ display: flex;
544
+ align-items: flex-end;
545
+ justify-content: center;
546
+ padding: 62px 6px 6px;
547
+ background: rgb(0 0 0 / 0.08);
548
+ backdrop-filter: blur(6px);
549
+ -webkit-backdrop-filter: blur(6px);
550
+
551
+ ${({ theme }) => theme.mediaQueries.lg} {
552
+ display: none;
553
+ }
554
+ `;
555
+ export const HeaderMobileSheet = styled.div `
556
+ width: min(calc(100% - 12px), 375px);
557
+ padding: 16px;
558
+ padding-bottom: 36px;
559
+ border-radius: 34px 34px 62px 62px;
560
+ background: ${({ theme }) => theme.colors.brandSky};
561
+ box-shadow: ${({ theme }) => theme.shadow.overlayModal};
562
+ `;
563
+ export const HeaderMobileNav = styled.nav `
564
+ display: grid;
565
+ gap: 0;
566
+ `;
567
+ export const HeaderMobileNavLink = styled.a `
568
+ display: inline-flex;
569
+ align-items: center;
570
+ gap: 6px;
571
+ min-height: 52px;
572
+ padding: 16px;
573
+ color: ${({ theme }) => theme.colors.ink};
574
+ font-size: ${({ theme }) => theme.typography.fontSize.body};
575
+ font-weight: ${({ theme }) => theme.fontWeight.bold};
576
+ letter-spacing: -0.02em;
577
+ line-height: ${({ theme }) => theme.typography.lineHeight.compact};
578
+ text-decoration: none;
579
+
580
+ &:focus-visible {
581
+ outline: 2px solid currentColor;
582
+ outline-offset: -4px;
583
+ border-radius: ${({ theme }) => theme.borderRadius.control};
584
+ }
585
+ `;
586
+ export const HeaderMobileNavGroup = styled.div `
587
+ display: grid;
588
+ gap: 0;
589
+ `;
590
+ export const HeaderMobileNavGroupToggle = styled.button `
591
+ display: flex;
592
+ align-items: center;
593
+ justify-content: space-between;
594
+ gap: 6px;
595
+ width: 100%;
596
+ min-height: 52px;
597
+ margin: 0;
598
+ padding: 16px;
599
+ border: 0;
600
+ background: transparent;
601
+ color: ${({ theme }) => theme.colors.ink};
602
+ cursor: pointer;
603
+ font-family: inherit;
604
+ font-size: ${({ theme }) => theme.typography.fontSize.body};
605
+ font-weight: ${({ theme }) => theme.fontWeight.bold};
606
+ letter-spacing: -0.02em;
607
+ line-height: ${({ theme }) => theme.typography.lineHeight.compact};
608
+ text-align: left;
609
+
610
+ &:focus-visible {
611
+ outline: 2px solid currentColor;
612
+ outline-offset: -4px;
613
+ border-radius: ${({ theme }) => theme.borderRadius.control};
614
+ }
615
+ `;
616
+ export const HeaderMobileNavGroupChevron = styled.span `
617
+ position: relative;
618
+ width: 20px;
619
+ height: 20px;
620
+ flex: 0 0 auto;
621
+ opacity: 0.5;
622
+
623
+ &::before {
624
+ content: '';
625
+ position: absolute;
626
+ top: 50%;
627
+ left: 50%;
628
+ width: 8px;
629
+ height: 8px;
630
+ border-right: 2px solid currentColor;
631
+ border-bottom: 2px solid currentColor;
632
+ /* Offset the rotated caret so its visual mass stays centred in both states. */
633
+ transform: translate(-50%, ${({ $open }) => ($open ? '-25%' : '-75%')})
634
+ rotate(${({ $open }) => ($open ? '225deg' : '45deg')});
635
+ transform-origin: center;
636
+ transition: transform ${({ theme }) => theme.motion.duration.fast}
637
+ ${({ theme }) => theme.motion.easing.standard};
638
+ }
639
+ `;
640
+ /* Animated disclosure: grid-template-rows 0fr -> 1fr gives a smooth height
641
+ transition without measuring the content. The inner element clips the rows. */
642
+ export const HeaderMobileNavGroupPanel = styled.div `
643
+ display: grid;
644
+ grid-template-rows: ${({ $open }) => ($open ? '1fr' : '0fr')};
645
+ transition: grid-template-rows ${({ theme }) => theme.motion.duration.normal}
646
+ ${({ theme }) => theme.motion.easing.emphasized};
647
+ `;
648
+ export const HeaderMobileNavGroupPanelInner = styled.div `
649
+ overflow: hidden;
650
+ min-height: 0;
651
+ opacity: ${({ $open }) => ($open ? 1 : 0)};
652
+ transition: opacity ${({ theme }) => theme.motion.duration.fast}
653
+ ${({ theme }) => theme.motion.easing.standard};
654
+ `;
655
+ export const HeaderMobileNavSubList = styled.div `
656
+ display: grid;
657
+ gap: 2px;
658
+ margin: 2px 0 10px 26px;
659
+ padding-left: 14px;
660
+ border-left: 1.5px solid rgb(0 0 0 / 0.12);
661
+ `;
662
+ export const HeaderMobileNavSubLink = styled.a `
663
+ display: flex;
664
+ align-items: center;
665
+ gap: 6px;
666
+ min-height: 40px;
667
+ padding: 8px 12px;
668
+ border-radius: ${({ theme }) => theme.borderRadius.control};
669
+ color: ${({ theme }) => theme.colors.textDarkSubtle};
670
+ font-size: ${({ theme }) => theme.typography.fontSize.bodySm};
671
+ font-weight: ${({ theme }) => theme.fontWeight.semibold};
672
+ letter-spacing: -0.02em;
673
+ line-height: ${({ theme }) => theme.typography.lineHeight.compact};
674
+ text-decoration: none;
675
+ transition:
676
+ color ${({ theme }) => theme.motion.duration.fast}
677
+ ${({ theme }) => theme.motion.easing.standard},
678
+ background-color ${({ theme }) => theme.motion.duration.fast}
679
+ ${({ theme }) => theme.motion.easing.standard};
680
+
681
+ &:hover {
682
+ color: ${({ theme }) => theme.colors.ink};
683
+ background: rgb(255 255 255 / 0.4);
684
+ }
685
+
686
+ &:focus-visible {
687
+ outline: 2px solid ${({ theme }) => theme.colors.ink};
688
+ outline-offset: -4px;
689
+ }
690
+ `;
691
+ export const HeaderMobileNavDisabledItem = styled.span `
692
+ display: inline-flex;
693
+ align-items: center;
694
+ gap: 6px;
695
+ min-height: 52px;
696
+ padding: 16px;
697
+ color: ${({ theme }) => theme.colors.textDarkSubtle};
698
+ cursor: default;
699
+ font-size: ${({ theme }) => theme.typography.fontSize.body};
700
+ font-weight: ${({ theme }) => theme.fontWeight.bold};
701
+ letter-spacing: 0;
702
+ line-height: ${({ theme }) => theme.typography.lineHeight.compact};
703
+ opacity: 0.62;
704
+ `;
705
+ export const HeaderMobileSoonBadge = styled.span `
706
+ display: inline-flex;
707
+ align-items: center;
708
+ justify-content: center;
709
+ min-width: 44px;
710
+ height: 18px;
711
+ padding: 0 10px;
712
+ margin-left: 4px;
713
+ border-radius: ${({ theme }) => theme.borderRadius.pill};
714
+ background: rgb(255 255 255 / 0.42);
715
+ color: currentColor;
716
+ box-shadow: inset 0 0 0 1px rgb(0 0 0 / 0.08);
717
+ font-size: 0.5rem;
718
+ font-weight: ${({ theme }) => theme.fontWeight.black};
719
+ letter-spacing: 0.1em;
720
+ line-height: ${({ theme }) => theme.typography.lineHeight.tight};
721
+ text-transform: uppercase;
722
+ white-space: nowrap;
723
+ `;
724
+ export const HeaderMobileLocaleSlot = styled.div `
725
+ width: 100%;
726
+ margin: 8px 0 12px;
727
+ padding: 0 16px;
728
+ `;
729
+ export const HeaderMobileWaitlistLink = styled.a `
730
+ position: relative;
731
+ display: flex;
732
+ align-items: center;
733
+ justify-content: space-between;
734
+ gap: ${({ theme }) => theme.sizes.md};
735
+ width: 100%;
736
+ height: 48px;
737
+ margin-top: 8px;
738
+ padding-left: 20px;
739
+ border-radius: ${({ theme }) => theme.borderRadius.capsule};
740
+ background: rgb(199 242 255 / 0.3);
741
+ color: ${({ theme }) => theme.colors.textDarkSubtle};
742
+ text-decoration: none;
743
+ overflow: hidden;
744
+
745
+ &:focus-visible {
746
+ outline: 2px solid ${({ theme }) => theme.colors.ink};
747
+ outline-offset: 4px;
748
+ }
749
+ `;
750
+ export const HeaderMobileWaitlistLabel = styled.span `
751
+ display: block;
752
+ min-width: 0;
753
+ font-size: ${({ theme }) => theme.typography.fontSize.bodySm};
754
+ font-weight: ${({ theme }) => theme.fontWeight.default};
755
+ line-height: 1.2857142857;
756
+ color: ${({ theme }) => theme.colors.textDarkSubtle};
757
+ `;
758
+ export const HeaderMobileWaitlistArrow = styled.span `
759
+ display: inline-flex;
760
+ align-items: center;
761
+ justify-content: center;
762
+ width: 72px;
763
+ height: 48px;
764
+ flex: 0 0 auto;
765
+ border-radius: ${({ theme }) => theme.borderRadius.capsule};
766
+ background: ${({ theme }) => theme.colors.surfaceDark};
767
+ color: ${({ theme }) => theme.colors.light};
768
+ font-size: ${({ theme }) => theme.typography.fontSize.bodyXl};
769
+ line-height: 1;
770
+ `;