@tribe-nest/forge 3.53.0 → 3.57.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 (74) hide show
  1. package/package.json +1 -1
  2. package/src/contexts/AppAuthContext.tsx +3 -2
  3. package/src/contexts/AudioPlayerContext.tsx +3 -2
  4. package/src/contexts/CartContext.tsx +3 -2
  5. package/src/contexts/PublicAuthContext.tsx +3 -2
  6. package/src/data/queries/useCoachingProducts.ts +15 -1
  7. package/src/data/queries/useCourses.ts +15 -1
  8. package/src/data/queries/useEvents.ts +26 -1
  9. package/src/data/queries/useMembership.ts +176 -4
  10. package/src/data/queries/useMyTickets.ts +49 -0
  11. package/src/data/queries/usePaymentFlow.ts +8 -0
  12. package/src/data/queries/useProducts.ts +15 -1
  13. package/src/data/queries/useSubscriptions.ts +21 -1
  14. package/src/i18n/de.json +116 -1
  15. package/src/i18n/en.json +116 -1
  16. package/src/i18n/index.ts +3 -2
  17. package/src/index.ts +22 -0
  18. package/src/provider/ForgeAppProvider.tsx +8 -1
  19. package/src/provider/ForgeProvider.tsx +3 -2
  20. package/src/provider/SiteConfigProvider.tsx +3 -2
  21. package/src/runtime/RemotePage.tsx +110 -0
  22. package/src/runtime/hostRuntime.ts +84 -0
  23. package/src/runtime/pages.spec.ts +41 -0
  24. package/src/runtime/pages.ts +183 -0
  25. package/src/runtime/pagesClient.ts +127 -0
  26. package/src/runtime/registry.spec.ts +45 -0
  27. package/src/runtime/registry.ts +102 -0
  28. package/src/server/index.ts +74 -0
  29. package/src/server/jobs.ts +4 -1
  30. package/src/server/platformEvents.generated.ts +152 -0
  31. package/src/types/models.ts +185 -0
  32. package/src/ui/headless/event/_tests/ticketApproval.spec.ts +137 -0
  33. package/src/ui/headless/event/_tests/useEventCheckoutApproval.spec.tsx +250 -0
  34. package/src/ui/headless/event/ticketApproval.ts +109 -0
  35. package/src/ui/headless/event/useEventCheckout.ts +74 -4
  36. package/src/ui/headless/index.ts +34 -0
  37. package/src/ui/headless/membership/_tests/membershipApplication.spec.ts +85 -0
  38. package/src/ui/headless/membership/_tests/membershipCheckoutRefetch.spec.tsx +2 -0
  39. package/src/ui/headless/membership/_tests/membershipTrial.spec.ts +61 -0
  40. package/src/ui/headless/membership/_tests/useMembershipCheckoutApplication.spec.tsx +253 -0
  41. package/src/ui/headless/membership/_tests/useMembershipCheckoutQuestionnaire.spec.tsx +241 -0
  42. package/src/ui/headless/membership/_tests/useMembershipCheckoutTrial.spec.tsx +165 -0
  43. package/src/ui/headless/membership/membershipApplication.ts +109 -0
  44. package/src/ui/headless/membership/membershipQuestionnaire.ts +44 -0
  45. package/src/ui/headless/membership/membershipTrial.ts +53 -0
  46. package/src/ui/headless/membership/useMembershipCheckout.ts +227 -8
  47. package/src/ui/index.ts +27 -0
  48. package/src/ui/payment/ForgePaymentProvider.tsx +29 -0
  49. package/src/ui/payment/ForgeStripePayment.tsx +110 -5
  50. package/src/ui/payment/_tests/ForgeStripePaymentTrial.spec.tsx +101 -0
  51. package/src/ui/payment/_tests/stripeConfirmOutcome.spec.ts +61 -0
  52. package/src/ui/payment/_tests/stripeIntentKind.spec.ts +43 -0
  53. package/src/ui/payment/stripeConfirmOutcome.ts +50 -0
  54. package/src/ui/payment/stripeIntentKind.ts +32 -0
  55. package/src/ui/shell/TribeNestApp.tsx +6 -0
  56. package/src/ui/styled/AccountDashboard.tsx +239 -15
  57. package/src/ui/styled/DocumentSigningPage.tsx +516 -0
  58. package/src/ui/styled/EventConfirmation.tsx +106 -9
  59. package/src/ui/styled/EventTickets.tsx +107 -13
  60. package/src/ui/styled/LoginForm.tsx +5 -3
  61. package/src/ui/styled/MembershipCheckout.tsx +596 -256
  62. package/src/ui/styled/MembershipTierCallout.tsx +17 -3
  63. package/src/ui/styled/MembershipTiers.tsx +148 -6
  64. package/src/ui/styled/ProductGrid.tsx +14 -2
  65. package/src/ui/styled/ReviewRequestPage.tsx +214 -0
  66. package/src/ui/styled/SignupForm.tsx +4 -2
  67. package/src/ui/styled/_tests/AccountDashboardTrial.spec.tsx +105 -0
  68. package/src/ui/styled/_tests/MembershipCheckout.spec.tsx +106 -0
  69. package/src/ui/styled/_tests/membershipTiersCuratedAccess.spec.tsx +139 -0
  70. package/src/ui/styled/_tests/membershipTiersTrial.spec.tsx +97 -0
  71. package/src/ui/styled/forge-utilities.css +310 -0
  72. package/src/ui/theme/ForgeThemeProvider.tsx +3 -2
  73. package/src/utils/_tests/ticketOrderOutcome.spec.ts +82 -0
  74. package/src/utils/ticketOrderOutcome.ts +57 -1
@@ -6,31 +6,59 @@
6
6
  --fg-color-red-500: oklch(63.7% 0.237 25.331);
7
7
  --fg-color-black: #000;
8
8
  --fg-spacing: 0.25rem;
9
+ --fg-container-md: 28rem;
10
+ --fg-container-xl: 36rem;
11
+ --fg-container-2xl: 42rem;
12
+ --fg-container-3xl: 48rem;
9
13
  --fg-container-4xl: 56rem;
10
14
  --fg-container-6xl: 72rem;
11
15
  --fg-text-xs: 0.75rem;
12
16
  --fg-text-xs--line-height: calc(1 / 0.75);
13
17
  --fg-text-sm: 0.875rem;
14
18
  --fg-text-sm--line-height: calc(1.25 / 0.875);
19
+ --fg-text-base: 1rem;
20
+ --fg-text-base--line-height: calc(1.5 / 1);
15
21
  --fg-text-lg: 1.125rem;
16
22
  --fg-text-lg--line-height: calc(1.75 / 1.125);
17
23
  --fg-text-xl: 1.25rem;
18
24
  --fg-text-xl--line-height: calc(1.75 / 1.25);
19
25
  --fg-text-2xl: 1.5rem;
20
26
  --fg-text-2xl--line-height: calc(2 / 1.5);
27
+ --fg-text-3xl: 1.875rem;
28
+ --fg-text-3xl--line-height: calc(2.25 / 1.875);
29
+ --fg-font-weight-normal: 400;
21
30
  --fg-font-weight-medium: 500;
22
31
  --fg-font-weight-semibold: 600;
23
32
  --fg-font-weight-bold: 700;
33
+ --fg-font-weight-extrabold: 800;
34
+ --fg-tracking-wide: 0.025em;
35
+ --fg-leading-relaxed: 1.625;
24
36
  --fg-radius-md: 0.375rem;
25
37
  --fg-radius-lg: 0.5rem;
38
+ --fg-radius-xl: 0.75rem;
26
39
  --fg-default-transition-duration: 150ms;
27
40
  --fg-default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
41
+ --fg-color-primary: var(--forge-primary, #6d28d9);
42
+ --fg-color-background: var(--forge-background, #ffffff);
43
+ --fg-color-surface: var(--forge-surface, #ffffff);
44
+ --fg-color-text: var(--forge-text, #0f0f10);
45
+ --fg-color-muted: var(--forge-muted, #6b7280);
46
+ --fg-color-border: var(--forge-border, #e5e7eb);
28
47
  }
29
48
  }
30
49
  @layer utilities {
50
+ .fg\:pointer-events-none {
51
+ pointer-events: none;
52
+ }
53
+ .fg\:absolute {
54
+ position: absolute;
55
+ }
31
56
  .fg\:fixed {
32
57
  position: fixed;
33
58
  }
59
+ .fg\:relative {
60
+ position: relative;
61
+ }
34
62
  .fg\:sticky {
35
63
  position: sticky;
36
64
  }
@@ -61,18 +89,42 @@
61
89
  .fg\:mx-auto {
62
90
  margin-inline: auto;
63
91
  }
92
+ .fg\:my-6 {
93
+ margin-block: calc(var(--fg-spacing) * 6);
94
+ }
95
+ .fg\:mt-0\.5 {
96
+ margin-top: calc(var(--fg-spacing) * 0.5);
97
+ }
64
98
  .fg\:mt-1 {
65
99
  margin-top: calc(var(--fg-spacing) * 1);
66
100
  }
101
+ .fg\:mt-1\.5 {
102
+ margin-top: calc(var(--fg-spacing) * 1.5);
103
+ }
104
+ .fg\:mt-2 {
105
+ margin-top: calc(var(--fg-spacing) * 2);
106
+ }
107
+ .fg\:mt-3 {
108
+ margin-top: calc(var(--fg-spacing) * 3);
109
+ }
67
110
  .fg\:mt-4 {
68
111
  margin-top: calc(var(--fg-spacing) * 4);
69
112
  }
113
+ .fg\:mt-5 {
114
+ margin-top: calc(var(--fg-spacing) * 5);
115
+ }
70
116
  .fg\:mt-6 {
71
117
  margin-top: calc(var(--fg-spacing) * 6);
72
118
  }
119
+ .fg\:mt-8 {
120
+ margin-top: calc(var(--fg-spacing) * 8);
121
+ }
73
122
  .fg\:mb-1 {
74
123
  margin-bottom: calc(var(--fg-spacing) * 1);
75
124
  }
125
+ .fg\:mb-1\.5 {
126
+ margin-bottom: calc(var(--fg-spacing) * 1.5);
127
+ }
76
128
  .fg\:mb-2 {
77
129
  margin-bottom: calc(var(--fg-spacing) * 2);
78
130
  }
@@ -82,6 +134,9 @@
82
134
  .fg\:mb-4 {
83
135
  margin-bottom: calc(var(--fg-spacing) * 4);
84
136
  }
137
+ .fg\:mb-5 {
138
+ margin-bottom: calc(var(--fg-spacing) * 5);
139
+ }
85
140
  .fg\:mb-6 {
86
141
  margin-bottom: calc(var(--fg-spacing) * 6);
87
142
  }
@@ -103,6 +158,9 @@
103
158
  .fg\:hidden {
104
159
  display: none;
105
160
  }
161
+ .fg\:inline-flex {
162
+ display: inline-flex;
163
+ }
106
164
  .fg\:h-0\.5 {
107
165
  height: calc(var(--fg-spacing) * 0.5);
108
166
  }
@@ -112,6 +170,12 @@
112
170
  .fg\:h-8 {
113
171
  height: calc(var(--fg-spacing) * 8);
114
172
  }
173
+ .fg\:h-12 {
174
+ height: calc(var(--fg-spacing) * 12);
175
+ }
176
+ .fg\:h-\[70vh\] {
177
+ height: 70vh;
178
+ }
115
179
  .fg\:h-full {
116
180
  height: 100%;
117
181
  }
@@ -121,18 +185,33 @@
121
185
  .fg\:w-8 {
122
186
  width: calc(var(--fg-spacing) * 8);
123
187
  }
188
+ .fg\:w-10 {
189
+ width: calc(var(--fg-spacing) * 10);
190
+ }
124
191
  .fg\:w-52 {
125
192
  width: calc(var(--fg-spacing) * 52);
126
193
  }
127
194
  .fg\:w-full {
128
195
  width: 100%;
129
196
  }
197
+ .fg\:max-w-2xl {
198
+ max-width: var(--fg-container-2xl);
199
+ }
200
+ .fg\:max-w-3xl {
201
+ max-width: var(--fg-container-3xl);
202
+ }
130
203
  .fg\:max-w-4xl {
131
204
  max-width: var(--fg-container-4xl);
132
205
  }
133
206
  .fg\:max-w-6xl {
134
207
  max-width: var(--fg-container-6xl);
135
208
  }
209
+ .fg\:max-w-md {
210
+ max-width: var(--fg-container-md);
211
+ }
212
+ .fg\:max-w-xl {
213
+ max-width: var(--fg-container-xl);
214
+ }
136
215
  .fg\:min-w-0 {
137
216
  min-width: calc(var(--fg-spacing) * 0);
138
217
  }
@@ -145,6 +224,9 @@
145
224
  .fg\:cursor-pointer {
146
225
  cursor: pointer;
147
226
  }
227
+ .fg\:resize-y {
228
+ resize: vertical;
229
+ }
148
230
  .fg\:grid-cols-1 {
149
231
  grid-template-columns: repeat(1, minmax(0, 1fr));
150
232
  }
@@ -172,18 +254,33 @@
172
254
  .fg\:justify-center {
173
255
  justify-content: center;
174
256
  }
257
+ .fg\:justify-end {
258
+ justify-content: flex-end;
259
+ }
260
+ .fg\:gap-0\.5 {
261
+ gap: calc(var(--fg-spacing) * 0.5);
262
+ }
175
263
  .fg\:gap-1 {
176
264
  gap: calc(var(--fg-spacing) * 1);
177
265
  }
266
+ .fg\:gap-1\.5 {
267
+ gap: calc(var(--fg-spacing) * 1.5);
268
+ }
178
269
  .fg\:gap-2 {
179
270
  gap: calc(var(--fg-spacing) * 2);
180
271
  }
272
+ .fg\:gap-2\.5 {
273
+ gap: calc(var(--fg-spacing) * 2.5);
274
+ }
181
275
  .fg\:gap-3 {
182
276
  gap: calc(var(--fg-spacing) * 3);
183
277
  }
184
278
  .fg\:gap-4 {
185
279
  gap: calc(var(--fg-spacing) * 4);
186
280
  }
281
+ .fg\:gap-5 {
282
+ gap: calc(var(--fg-spacing) * 5);
283
+ }
187
284
  .fg\:gap-6 {
188
285
  gap: calc(var(--fg-spacing) * 6);
189
286
  }
@@ -218,6 +315,9 @@
218
315
  margin-inline-end: calc(calc(var(--fg-spacing) * 4) * calc(1 - var(--tw-space-x-reverse)));
219
316
  }
220
317
  }
318
+ .fg\:overflow-hidden {
319
+ overflow: hidden;
320
+ }
221
321
  .fg\:overflow-x-auto {
222
322
  overflow-x: auto;
223
323
  }
@@ -233,31 +333,117 @@
233
333
  .fg\:rounded-md {
234
334
  border-radius: var(--fg-radius-md);
235
335
  }
336
+ .fg\:rounded-xl {
337
+ border-radius: var(--fg-radius-xl);
338
+ }
339
+ .fg\:border {
340
+ border-style: var(--tw-border-style);
341
+ border-width: 1px;
342
+ }
236
343
  .fg\:border-2 {
237
344
  border-style: var(--tw-border-style);
238
345
  border-width: 2px;
239
346
  }
347
+ .fg\:border-dashed {
348
+ --tw-border-style: dashed;
349
+ border-style: dashed;
350
+ }
351
+ .fg\:border-border {
352
+ border-color: var(--fg-color-border);
353
+ }
354
+ .fg\:border-primary {
355
+ border-color: var(--fg-color-primary);
356
+ }
357
+ .fg\:border-primary\/40 {
358
+ border-color: var(--fg-color-primary);
359
+ @supports (color: color-mix(in lab, red, red)) {
360
+ border-color: color-mix(in oklab, var(--fg-color-primary) 40%, transparent);
361
+ }
362
+ }
363
+ .fg\:bg-background {
364
+ background-color: var(--fg-color-background);
365
+ }
240
366
  .fg\:bg-black {
241
367
  background-color: var(--fg-color-black);
242
368
  }
369
+ .fg\:bg-primary {
370
+ background-color: var(--fg-color-primary);
371
+ }
372
+ .fg\:bg-primary\/5 {
373
+ background-color: var(--fg-color-primary);
374
+ @supports (color: color-mix(in lab, red, red)) {
375
+ background-color: color-mix(in oklab, var(--fg-color-primary) 5%, transparent);
376
+ }
377
+ }
378
+ .fg\:bg-primary\/10 {
379
+ background-color: var(--fg-color-primary);
380
+ @supports (color: color-mix(in lab, red, red)) {
381
+ background-color: color-mix(in oklab, var(--fg-color-primary) 10%, transparent);
382
+ }
383
+ }
384
+ .fg\:bg-surface {
385
+ background-color: var(--fg-color-surface);
386
+ }
387
+ .fg\:bg-transparent {
388
+ background-color: transparent;
389
+ }
390
+ .fg\:p-0\.5 {
391
+ padding: calc(var(--fg-spacing) * 0.5);
392
+ }
243
393
  .fg\:p-3 {
244
394
  padding: calc(var(--fg-spacing) * 3);
245
395
  }
246
396
  .fg\:p-4 {
247
397
  padding: calc(var(--fg-spacing) * 4);
248
398
  }
399
+ .fg\:p-5 {
400
+ padding: calc(var(--fg-spacing) * 5);
401
+ }
249
402
  .fg\:p-6 {
250
403
  padding: calc(var(--fg-spacing) * 6);
251
404
  }
405
+ .fg\:p-8 {
406
+ padding: calc(var(--fg-spacing) * 8);
407
+ }
408
+ .fg\:px-2 {
409
+ padding-inline: calc(var(--fg-spacing) * 2);
410
+ }
411
+ .fg\:px-3 {
412
+ padding-inline: calc(var(--fg-spacing) * 3);
413
+ }
252
414
  .fg\:px-4 {
253
415
  padding-inline: calc(var(--fg-spacing) * 4);
254
416
  }
417
+ .fg\:py-0\.5 {
418
+ padding-block: calc(var(--fg-spacing) * 0.5);
419
+ }
420
+ .fg\:py-1\.5 {
421
+ padding-block: calc(var(--fg-spacing) * 1.5);
422
+ }
423
+ .fg\:py-2 {
424
+ padding-block: calc(var(--fg-spacing) * 2);
425
+ }
426
+ .fg\:py-2\.5 {
427
+ padding-block: calc(var(--fg-spacing) * 2.5);
428
+ }
255
429
  .fg\:py-3 {
256
430
  padding-block: calc(var(--fg-spacing) * 3);
257
431
  }
432
+ .fg\:py-5 {
433
+ padding-block: calc(var(--fg-spacing) * 5);
434
+ }
435
+ .fg\:py-6 {
436
+ padding-block: calc(var(--fg-spacing) * 6);
437
+ }
438
+ .fg\:py-8 {
439
+ padding-block: calc(var(--fg-spacing) * 8);
440
+ }
258
441
  .fg\:py-12 {
259
442
  padding-block: calc(var(--fg-spacing) * 12);
260
443
  }
444
+ .fg\:py-16 {
445
+ padding-block: calc(var(--fg-spacing) * 16);
446
+ }
261
447
  .fg\:pt-2 {
262
448
  padding-top: calc(var(--fg-spacing) * 2);
263
449
  }
@@ -286,6 +472,14 @@
286
472
  font-size: var(--fg-text-2xl);
287
473
  line-height: var(--tw-leading, var(--fg-text-2xl--line-height));
288
474
  }
475
+ .fg\:text-3xl {
476
+ font-size: var(--fg-text-3xl);
477
+ line-height: var(--tw-leading, var(--fg-text-3xl--line-height));
478
+ }
479
+ .fg\:text-base {
480
+ font-size: var(--fg-text-base);
481
+ line-height: var(--tw-leading, var(--fg-text-base--line-height));
482
+ }
289
483
  .fg\:text-lg {
290
484
  font-size: var(--fg-text-lg);
291
485
  line-height: var(--tw-leading, var(--fg-text-lg--line-height));
@@ -302,24 +496,80 @@
302
496
  font-size: var(--fg-text-xs);
303
497
  line-height: var(--tw-leading, var(--fg-text-xs--line-height));
304
498
  }
499
+ .fg\:leading-none {
500
+ --tw-leading: 1;
501
+ line-height: 1;
502
+ }
503
+ .fg\:leading-relaxed {
504
+ --tw-leading: var(--fg-leading-relaxed);
505
+ line-height: var(--fg-leading-relaxed);
506
+ }
305
507
  .fg\:font-bold {
306
508
  --tw-font-weight: var(--fg-font-weight-bold);
307
509
  font-weight: var(--fg-font-weight-bold);
308
510
  }
511
+ .fg\:font-extrabold {
512
+ --tw-font-weight: var(--fg-font-weight-extrabold);
513
+ font-weight: var(--fg-font-weight-extrabold);
514
+ }
309
515
  .fg\:font-medium {
310
516
  --tw-font-weight: var(--fg-font-weight-medium);
311
517
  font-weight: var(--fg-font-weight-medium);
312
518
  }
519
+ .fg\:font-normal {
520
+ --tw-font-weight: var(--fg-font-weight-normal);
521
+ font-weight: var(--fg-font-weight-normal);
522
+ }
313
523
  .fg\:font-semibold {
314
524
  --tw-font-weight: var(--fg-font-weight-semibold);
315
525
  font-weight: var(--fg-font-weight-semibold);
316
526
  }
527
+ .fg\:tracking-wide {
528
+ --tw-tracking: var(--fg-tracking-wide);
529
+ letter-spacing: var(--fg-tracking-wide);
530
+ }
531
+ .fg\:whitespace-pre-line {
532
+ white-space: pre-line;
533
+ }
534
+ .fg\:text-muted {
535
+ color: var(--fg-color-muted);
536
+ }
537
+ .fg\:text-muted\/50 {
538
+ color: var(--fg-color-muted);
539
+ @supports (color: color-mix(in lab, red, red)) {
540
+ color: color-mix(in oklab, var(--fg-color-muted) 50%, transparent);
541
+ }
542
+ }
543
+ .fg\:text-primary {
544
+ color: var(--fg-color-primary);
545
+ }
317
546
  .fg\:text-red-500 {
318
547
  color: var(--fg-color-red-500);
319
548
  }
549
+ .fg\:text-text {
550
+ color: var(--fg-color-text);
551
+ }
552
+ .fg\:text-text\/85 {
553
+ color: var(--fg-color-text);
554
+ @supports (color: color-mix(in lab, red, red)) {
555
+ color: color-mix(in oklab, var(--fg-color-text) 85%, transparent);
556
+ }
557
+ }
558
+ .fg\:text-text\/90 {
559
+ color: var(--fg-color-text);
560
+ @supports (color: color-mix(in lab, red, red)) {
561
+ color: color-mix(in oklab, var(--fg-color-text) 90%, transparent);
562
+ }
563
+ }
564
+ .fg\:uppercase {
565
+ text-transform: uppercase;
566
+ }
320
567
  .fg\:underline {
321
568
  text-decoration-line: underline;
322
569
  }
570
+ .fg\:accent-primary {
571
+ accent-color: var(--fg-color-primary);
572
+ }
323
573
  .fg\:shadow-md {
324
574
  --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
325
575
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -329,6 +579,25 @@
329
579
  transition-timing-function: var(--tw-ease, var(--fg-default-transition-timing-function));
330
580
  transition-duration: var(--tw-duration, var(--fg-default-transition-duration));
331
581
  }
582
+ .fg\:outline-none {
583
+ --tw-outline-style: none;
584
+ outline-style: none;
585
+ }
586
+ .fg\:focus\:border-primary {
587
+ &:focus {
588
+ border-color: var(--fg-color-primary);
589
+ }
590
+ }
591
+ .fg\:disabled\:opacity-40 {
592
+ &:disabled {
593
+ opacity: 40%;
594
+ }
595
+ }
596
+ .fg\:disabled\:opacity-50 {
597
+ &:disabled {
598
+ opacity: 50%;
599
+ }
600
+ }
332
601
  .fg\:sm\:block {
333
602
  @media (width >= 40rem) {
334
603
  display: block;
@@ -344,6 +613,21 @@
344
613
  grid-template-columns: repeat(2, minmax(0, 1fr));
345
614
  }
346
615
  }
616
+ .fg\:sm\:flex-row {
617
+ @media (width >= 40rem) {
618
+ flex-direction: row;
619
+ }
620
+ }
621
+ .fg\:sm\:items-center {
622
+ @media (width >= 40rem) {
623
+ align-items: center;
624
+ }
625
+ }
626
+ .fg\:sm\:justify-between {
627
+ @media (width >= 40rem) {
628
+ justify-content: space-between;
629
+ }
630
+ }
347
631
  .fg\:md\:static {
348
632
  @media (width >= 48rem) {
349
633
  position: static;
@@ -479,6 +763,22 @@
479
763
  gap: calc(var(--fg-spacing) * 8);
480
764
  }
481
765
  }
766
+ .fg\:lg\:p-8 {
767
+ @media (width >= 64rem) {
768
+ padding: calc(var(--fg-spacing) * 8);
769
+ }
770
+ }
771
+ .fg\:lg\:py-12 {
772
+ @media (width >= 64rem) {
773
+ padding-block: calc(var(--fg-spacing) * 12);
774
+ }
775
+ }
776
+ .fg\:lg\:text-3xl {
777
+ @media (width >= 64rem) {
778
+ font-size: var(--fg-text-3xl);
779
+ line-height: var(--tw-leading, var(--fg-text-3xl--line-height));
780
+ }
781
+ }
482
782
  }
483
783
  @property --tw-space-y-reverse {
484
784
  syntax: "*";
@@ -495,10 +795,18 @@
495
795
  inherits: false;
496
796
  initial-value: solid;
497
797
  }
798
+ @property --tw-leading {
799
+ syntax: "*";
800
+ inherits: false;
801
+ }
498
802
  @property --tw-font-weight {
499
803
  syntax: "*";
500
804
  inherits: false;
501
805
  }
806
+ @property --tw-tracking {
807
+ syntax: "*";
808
+ inherits: false;
809
+ }
502
810
  @property --tw-shadow {
503
811
  syntax: "*";
504
812
  inherits: false;
@@ -570,7 +878,9 @@
570
878
  --tw-space-y-reverse: 0;
571
879
  --tw-space-x-reverse: 0;
572
880
  --tw-border-style: solid;
881
+ --tw-leading: initial;
573
882
  --tw-font-weight: initial;
883
+ --tw-tracking: initial;
574
884
  --tw-shadow: 0 0 #0000;
575
885
  --tw-shadow-color: initial;
576
886
  --tw-shadow-alpha: 100%;
@@ -1,4 +1,5 @@
1
- import { createContext, useContext, type ReactNode } from "react";
1
+ import { useContext, type ReactNode } from "react";
2
+ import { createSharedContext } from "../../runtime/registry";
2
3
  import { readableTextOn } from "./contrast";
3
4
 
4
5
  /**
@@ -81,7 +82,7 @@ export function resolveThemeTokens(t: ForgeTheme): ResolvedThemeTokens {
81
82
  };
82
83
  }
83
84
 
84
- const ForgeThemeContext = createContext<ForgeTheme | null>(null);
85
+ const ForgeThemeContext = createSharedContext<ForgeTheme | null>("theme", null);
85
86
 
86
87
  export function ForgeThemeProvider({ theme, children }: { theme: ForgeTheme; children: ReactNode }) {
87
88
  const r = resolveThemeTokens(theme);
@@ -2,6 +2,7 @@ import { describe, it, expect } from "vitest";
2
2
  import {
3
3
  getTicketOrderOutcome,
4
4
  isTicketOrderRefunded,
5
+ ticketRequestFailureKind,
5
6
  TICKET_ORDER_REFUNDED_COPY,
6
7
  } from "../ticketOrderOutcome";
7
8
  import { OrderStatus } from "../../types/models";
@@ -124,3 +125,84 @@ describe("TICKET_ORDER_REFUNDED_COPY", () => {
124
125
  expect(TICKET_ORDER_REFUNDED_COPY.body.toLowerCase()).toContain("will not admit entry");
125
126
  });
126
127
  });
128
+
129
+ /**
130
+ * Ticket requests (curated access, feature 3). A request is neither paid nor
131
+ * abandoned while the organiser decides, and when it ends without a sale the
132
+ * fan has to be told WHICH way it ended: "you were not approved", "nobody
133
+ * answered in time" and "you were approved but the card failed" are three
134
+ * different pieces of news, and the last one comes with an invitation to buy
135
+ * again that the other two must not carry.
136
+ */
137
+ describe("getTicketOrderOutcome: ticket requests", () => {
138
+ it("calls a pending request awaiting_approval, and the transient approved state too", () => {
139
+ expect(getTicketOrderOutcome({ status: OrderStatus.PendingApproval })).toBe("awaiting_approval");
140
+ // Settlement in flight is still "waiting" from the fan's side.
141
+ expect(getTicketOrderOutcome({ status: OrderStatus.Approved })).toBe("awaiting_approval");
142
+ });
143
+
144
+ it("calls a declined request declined, not not_completed", () => {
145
+ expect(getTicketOrderOutcome({ status: OrderStatus.Declined })).toBe("declined");
146
+ });
147
+
148
+ it("does not let the refund columns turn a request into a refund", () => {
149
+ // Nothing is captured on a request, so these are at their defaults; even
150
+ // a nonsense payload must not read as money having gone back.
151
+ expect(
152
+ getTicketOrderOutcome({ status: OrderStatus.PendingApproval, refundedAmountCents: "0", refundState: "none" }),
153
+ ).toBe("awaiting_approval");
154
+ });
155
+
156
+ it("keeps an expired request and a failed settlement as not_completed", () => {
157
+ expect(getTicketOrderOutcome({ status: OrderStatus.Cancelled, approvalFailureReason: "expired" })).toBe(
158
+ "not_completed",
159
+ );
160
+ expect(getTicketOrderOutcome({ status: OrderStatus.PaymentFailed, approvalFailureReason: "card_declined" })).toBe(
161
+ "not_completed",
162
+ );
163
+ });
164
+
165
+ it("still calls an approved-and-paid request paid", () => {
166
+ expect(getTicketOrderOutcome({ status: OrderStatus.Paid, approvalRequestedAt: "2026-09-01T00:00:00Z" })).toBe(
167
+ "paid",
168
+ );
169
+ });
170
+ });
171
+
172
+ describe("ticketRequestFailureKind", () => {
173
+ it("names the three ways a request ends without a sale", () => {
174
+ expect(ticketRequestFailureKind({ status: OrderStatus.Declined })).toBe("declined");
175
+ expect(ticketRequestFailureKind({ status: OrderStatus.Cancelled, approvalFailureReason: "expired" })).toBe(
176
+ "expired",
177
+ );
178
+ expect(
179
+ ticketRequestFailureKind({ status: OrderStatus.PaymentFailed, approvalFailureReason: "card_declined" }),
180
+ ).toBe("payment_failed");
181
+ });
182
+
183
+ it("recognises an approved-but-unsettled request off the requested-at stamp when no reason came", () => {
184
+ expect(
185
+ ticketRequestFailureKind({ status: OrderStatus.PaymentFailed, approvalRequestedAt: "2026-09-01T00:00:00Z" }),
186
+ ).toBe("payment_failed");
187
+ });
188
+
189
+ it("is null for every order that was never a request, so their copy is unchanged", () => {
190
+ // The abandoned sweep writes `cancelled` with no approval columns at all.
191
+ expect(ticketRequestFailureKind(abandoned)).toBeNull();
192
+ expect(ticketRequestFailureKind({ status: OrderStatus.Cancelled, approvalFailureReason: null })).toBeNull();
193
+ // An ordinary declined card is `payment_failed` with neither signal.
194
+ expect(ticketRequestFailureKind({ status: OrderStatus.PaymentFailed })).toBeNull();
195
+ expect(ticketRequestFailureKind(refundVoided)).toBeNull();
196
+ });
197
+
198
+ it("is null while the request is still open, and once it succeeded", () => {
199
+ expect(ticketRequestFailureKind({ status: OrderStatus.PendingApproval })).toBeNull();
200
+ expect(ticketRequestFailureKind({ status: OrderStatus.Approved })).toBeNull();
201
+ expect(ticketRequestFailureKind({ status: OrderStatus.Paid, approvalRequestedAt: "2026-09-01T00:00:00Z" })).toBeNull();
202
+ });
203
+
204
+ it("does not read a cancelled row with some OTHER failure reason as expired", () => {
205
+ // Only the sweep's own stamp means "lapsed undecided".
206
+ expect(ticketRequestFailureKind({ status: OrderStatus.Cancelled, approvalFailureReason: "event_cancelled" })).toBeNull();
207
+ });
208
+ });