@contractspec/bundle.marketing 2.1.0 → 2.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 (87) hide show
  1. package/.turbo/turbo-build.log +73 -73
  2. package/CHANGELOG.md +49 -0
  3. package/README.md +6 -1
  4. package/dist/browser/components/marketing/CofounderPage.js +2 -2
  5. package/dist/browser/components/marketing/ContactClient.js +57 -961
  6. package/dist/browser/components/marketing/DesignPartnerPage.js +2 -2
  7. package/dist/browser/components/marketing/LandingPage.js +6 -6
  8. package/dist/browser/components/marketing/PricingClient.js +107 -1127
  9. package/dist/browser/components/marketing/ProductClientPage.js +2 -2
  10. package/dist/browser/components/marketing/index.js +157 -1064
  11. package/dist/browser/components/marketing/pricing-thinking-modal.js +3 -3
  12. package/dist/browser/components/marketing/sections/CorePositioningSection.js +2 -2
  13. package/dist/browser/components/marketing/sections/CtaSection.js +2 -2
  14. package/dist/browser/components/marketing/sections/HeroMarketingSection.js +2 -2
  15. package/dist/browser/components/marketing/studio-signup-section.js +87 -0
  16. package/dist/browser/components/templates/TemplatesClientPage.js +175 -1082
  17. package/dist/browser/components/templates/index.js +181 -1088
  18. package/dist/browser/index.js +505 -1157
  19. package/dist/browser/registry/engine.js +158 -1080
  20. package/dist/browser/registry/index.js +158 -1080
  21. package/dist/browser/registry/registry-docs.js +2 -17
  22. package/dist/browser/registry/registry-landing.js +156 -1063
  23. package/dist/browser/registry/registry.js +158 -1080
  24. package/dist/browser/registry/utils.js +158 -1080
  25. package/dist/components/marketing/CofounderPage.js +2 -2
  26. package/dist/components/marketing/ContactClient.js +57 -961
  27. package/dist/components/marketing/DesignPartnerPage.js +2 -2
  28. package/dist/components/marketing/LandingPage.js +6 -6
  29. package/dist/components/marketing/PricingClient.js +107 -1127
  30. package/dist/components/marketing/ProductClientPage.js +2 -2
  31. package/dist/components/marketing/index.d.ts +1 -1
  32. package/dist/components/marketing/index.js +157 -1064
  33. package/dist/components/marketing/pricing-thinking-modal.js +3 -3
  34. package/dist/components/marketing/sections/CorePositioningSection.js +2 -2
  35. package/dist/components/marketing/sections/CtaSection.js +2 -2
  36. package/dist/components/marketing/sections/HeroMarketingSection.js +2 -2
  37. package/dist/components/marketing/studio-signup-section.d.ts +5 -0
  38. package/dist/components/marketing/studio-signup-section.js +82 -0
  39. package/dist/components/templates/TemplatesClientPage.js +175 -1082
  40. package/dist/components/templates/index.js +181 -1088
  41. package/dist/index.js +505 -1157
  42. package/dist/node/components/marketing/CofounderPage.js +2 -2
  43. package/dist/node/components/marketing/ContactClient.js +57 -961
  44. package/dist/node/components/marketing/DesignPartnerPage.js +2 -2
  45. package/dist/node/components/marketing/LandingPage.js +6 -6
  46. package/dist/node/components/marketing/PricingClient.js +107 -1127
  47. package/dist/node/components/marketing/ProductClientPage.js +2 -2
  48. package/dist/node/components/marketing/index.js +157 -1064
  49. package/dist/node/components/marketing/pricing-thinking-modal.js +3 -3
  50. package/dist/node/components/marketing/sections/CorePositioningSection.js +2 -2
  51. package/dist/node/components/marketing/sections/CtaSection.js +2 -2
  52. package/dist/node/components/marketing/sections/HeroMarketingSection.js +2 -2
  53. package/dist/node/components/marketing/studio-signup-section.js +82 -0
  54. package/dist/node/components/templates/TemplatesClientPage.js +175 -1082
  55. package/dist/node/components/templates/index.js +181 -1088
  56. package/dist/node/index.js +505 -1157
  57. package/dist/node/registry/engine.js +158 -1080
  58. package/dist/node/registry/index.js +158 -1080
  59. package/dist/node/registry/registry-docs.js +2 -17
  60. package/dist/node/registry/registry-landing.js +156 -1063
  61. package/dist/node/registry/registry.js +158 -1080
  62. package/dist/node/registry/utils.js +158 -1080
  63. package/dist/registry/engine.js +158 -1080
  64. package/dist/registry/index.js +158 -1080
  65. package/dist/registry/registry-docs.js +2 -17
  66. package/dist/registry/registry-landing.js +156 -1063
  67. package/dist/registry/registry.js +158 -1080
  68. package/dist/registry/utils.js +158 -1080
  69. package/package.json +34 -34
  70. package/src/components/marketing/CofounderPage.tsx +2 -2
  71. package/src/components/marketing/ContactClient.tsx +3 -3
  72. package/src/components/marketing/DesignPartnerPage.tsx +3 -3
  73. package/src/components/marketing/PricingClient.tsx +39 -38
  74. package/src/components/marketing/ProductClientPage.tsx +2 -2
  75. package/src/components/marketing/index.ts +1 -1
  76. package/src/components/marketing/pricing-thinking-modal.tsx +5 -5
  77. package/src/components/marketing/sections/CorePositioningSection.tsx +2 -2
  78. package/src/components/marketing/sections/CtaSection.tsx +2 -2
  79. package/src/components/marketing/sections/HeroMarketingSection.tsx +2 -2
  80. package/src/components/marketing/studio-signup-section.tsx +56 -0
  81. package/src/components/templates/TemplatesClientPage.tsx +12 -9
  82. package/src/registry/registry-docs.ts +0 -40
  83. package/dist/browser/components/marketing/waitlist-section.js +0 -1104
  84. package/dist/components/marketing/waitlist-section.d.ts +0 -7
  85. package/dist/components/marketing/waitlist-section.js +0 -1099
  86. package/dist/node/components/marketing/waitlist-section.js +0 -1099
  87. package/src/components/marketing/waitlist-section.tsx +0 -606
@@ -1,606 +0,0 @@
1
- 'use client';
2
-
3
- import { useEffect, useState } from 'react';
4
- import { useForm } from 'react-hook-form';
5
- import { zodResolver } from '@hookform/resolvers/zod';
6
- import z from 'zod';
7
- import { AlertCircle, CheckCircle } from 'lucide-react';
8
- import { joinWaitlist } from '../../libs/email/waitlist';
9
- import { submitWaitlistApplication } from '../../libs/email/waitlist-application';
10
- import { Button } from '@contractspec/lib.design-system';
11
- import { Textarea } from '@contractspec/lib.design-system';
12
- import { Label } from '@contractspec/lib.ui-kit-web/ui/label';
13
- import {
14
- Select,
15
- SelectContent,
16
- SelectItem,
17
- SelectTrigger,
18
- SelectValue,
19
- } from '@contractspec/lib.ui-kit-web/ui/select';
20
- import { Checkbox } from '@contractspec/lib.ui-kit-web/ui/checkbox';
21
- import { Switch } from '@contractspec/lib.ui-kit-web/ui/switch';
22
- import { Input } from '@contractspec/lib.design-system';
23
-
24
- interface WaitlistSectionProps {
25
- variant?: 'default' | 'compact';
26
- context?: 'pricing' | 'contact';
27
- scrollToId?: string;
28
- }
29
-
30
- // Zod schemas
31
- const simpleWaitlistSchema = z.object({
32
- email: z.email('Please enter a valid email address'),
33
- });
34
-
35
- const designPartnerSchema = z.object({
36
- name: z.string().min(1, 'Name is required'),
37
- email: z.email('Please enter a valid email address'),
38
- company: z.string().optional(),
39
- role: z.string().optional(),
40
- useCase: z.string().optional(),
41
- currentStack: z.string().optional(),
42
- whatBuilding: z.string().min(1, 'Please tell us what you are building'),
43
- whatSolving: z
44
- .string()
45
- .min(1, 'Please tell us what ContractSpec will solve for you'),
46
- teamSize: z.string().optional(),
47
- timeline: z.string().optional(),
48
- openToSessions: z.boolean().default(false),
49
- okayWithCaseStudies: z.boolean().default(false),
50
- });
51
-
52
- type SimpleWaitlistFormData = z.infer<typeof simpleWaitlistSchema>;
53
- type DesignPartnerFormData = z.infer<typeof designPartnerSchema>;
54
-
55
- export function WaitlistSection({
56
- variant = 'default',
57
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
58
- context = 'pricing',
59
- }: WaitlistSectionProps) {
60
- const [isDesignPartner, setIsDesignPartner] = useState(false);
61
- const [submitResult, setSubmitResult] = useState<{
62
- success: boolean;
63
- text: string;
64
- } | null>(null);
65
- const [isPending, setIsPending] = useState(false);
66
-
67
- // Simple waitlist form
68
- const simpleForm = useForm<SimpleWaitlistFormData>({
69
- resolver: zodResolver(simpleWaitlistSchema),
70
- defaultValues: {
71
- email: '',
72
- },
73
- });
74
-
75
- // Design partner form
76
- const designPartnerForm = useForm({
77
- resolver: zodResolver<
78
- z.input<typeof designPartnerSchema>,
79
- unknown,
80
- z.output<typeof designPartnerSchema>
81
- >(designPartnerSchema),
82
- defaultValues: {
83
- name: '',
84
- email: '',
85
- company: '',
86
- role: '',
87
- useCase: '',
88
- currentStack: '',
89
- whatBuilding: '',
90
- whatSolving: '',
91
- teamSize: '',
92
- timeline: '',
93
- openToSessions: false,
94
- okayWithCaseStudies: false,
95
- },
96
- });
97
-
98
- // Sync email between forms
99
- const simpleEmail = simpleForm.watch('email');
100
- const designPartnerEmail = designPartnerForm.watch('email');
101
-
102
- // Sync from simple form to design partner form
103
- useEffect(() => {
104
- const currentDesignPartnerEmail = designPartnerForm.getValues('email');
105
- if (simpleEmail && simpleEmail !== currentDesignPartnerEmail) {
106
- designPartnerForm.setValue('email', simpleEmail, { shouldDirty: false });
107
- }
108
- }, [simpleEmail, designPartnerForm]);
109
-
110
- // Sync from design partner form to simple form
111
- useEffect(() => {
112
- const currentSimpleEmail = simpleForm.getValues('email');
113
- if (designPartnerEmail && designPartnerEmail !== currentSimpleEmail) {
114
- simpleForm.setValue('email', designPartnerEmail, { shouldDirty: false });
115
- }
116
- }, [designPartnerEmail, simpleForm]);
117
-
118
- const handleSimpleSubmit = async (data: SimpleWaitlistFormData) => {
119
- setIsPending(true);
120
- setSubmitResult(null);
121
-
122
- try {
123
- const formData = new FormData();
124
- formData.set('email', data.email);
125
-
126
- const result = await joinWaitlist(formData);
127
-
128
- if (result.success) {
129
- setSubmitResult({
130
- success: true,
131
- text: 'Thanks for joining the waitlist! Check your inbox for a confirmation.',
132
- });
133
- simpleForm.reset();
134
- } else {
135
- setSubmitResult({
136
- success: false,
137
- text: result.text || 'Failed to join waitlist. Please try again.',
138
- });
139
- }
140
- } catch (_error) {
141
- setSubmitResult({
142
- success: false,
143
- text: 'Failed to join waitlist. Please try again.',
144
- });
145
- } finally {
146
- setIsPending(false);
147
- }
148
- };
149
-
150
- const handleDesignPartnerSubmit = async (data: DesignPartnerFormData) => {
151
- setIsPending(true);
152
- setSubmitResult(null);
153
-
154
- try {
155
- const formData = new FormData();
156
- formData.set('email', data.email);
157
- formData.set('name', data.name);
158
- if (data.company) formData.set('company', data.company);
159
- if (data.role) formData.set('role', data.role);
160
- if (data.useCase) formData.set('useCase', data.useCase);
161
- if (data.currentStack) formData.set('currentStack', data.currentStack);
162
- formData.set('whatBuilding', data.whatBuilding);
163
- formData.set('whatSolving', data.whatSolving);
164
- if (data.teamSize) formData.set('teamSize', data.teamSize);
165
- if (data.timeline) formData.set('timeline', data.timeline);
166
- if (data.openToSessions) formData.set('openToSessions', 'on');
167
- if (data.okayWithCaseStudies) formData.set('okayWithCaseStudies', 'on');
168
-
169
- const result = await submitWaitlistApplication(formData);
170
-
171
- if (result.success) {
172
- setSubmitResult({
173
- success: true,
174
- text: "You're on the list. Thanks for applying. We're slowly onboarding design partners in waves. If your use case is a good fit, we'll reach out personally.",
175
- });
176
- designPartnerForm.reset();
177
- } else {
178
- setSubmitResult({
179
- success: false,
180
- text:
181
- result.text || 'Failed to submit application. Please try again.',
182
- });
183
- }
184
- } catch (_error) {
185
- setSubmitResult({
186
- success: false,
187
- text: 'Failed to submit application. Please try again.',
188
- });
189
- } finally {
190
- setIsPending(false);
191
- }
192
- };
193
-
194
- const onSubmit = isDesignPartner
195
- ? designPartnerForm.handleSubmit(handleDesignPartnerSubmit)
196
- : simpleForm.handleSubmit(handleSimpleSubmit);
197
-
198
- const isCompact = variant === 'compact';
199
-
200
- return (
201
- <div
202
- id="waitlist"
203
- className={isCompact ? 'space-y-4' : 'card-subtle space-y-6 p-8'}
204
- >
205
- {!isCompact && (
206
- <div className="space-y-4">
207
- <div className="inline-flex items-center gap-2 rounded-full border border-violet-500/20 bg-violet-500/10 px-3 py-1">
208
- <span className="text-sm font-medium text-violet-300">
209
- {isDesignPartner
210
- ? 'Design Partner Waitlist'
211
- : 'Join the Waitlist'}
212
- </span>
213
- </div>
214
- <h2 className="text-2xl font-bold">
215
- {isDesignPartner
216
- ? 'Apply for early access to ContractSpec'
217
- : 'Get early access to ContractSpec'}
218
- </h2>
219
- <p className="text-muted-foreground text-sm">
220
- {isDesignPartner
221
- ? "Tell us what you're building. We'll prioritize teams where ContractSpec can have a big impact, and where we can learn the most."
222
- : 'Join the waitlist to be notified when ContractSpec becomes available.'}
223
- </p>
224
- </div>
225
- )}
226
-
227
- {!isCompact && (
228
- <div className="border-border bg-muted/20 flex items-center justify-between gap-4 rounded-lg border p-4">
229
- <div className="space-y-1">
230
- <Label
231
- htmlFor="design-partner-toggle"
232
- className="text-sm font-medium"
233
- >
234
- Apply as a design partner
235
- </Label>
236
- <p className="text-muted-foreground text-xs">
237
- {isDesignPartner
238
- ? 'Get hands-on support, influence the roadmap, and founding discount'
239
- : 'Get priority access, 1:1 onboarding, and help shape ContractSpec'}
240
- </p>
241
- </div>
242
- <Switch
243
- id="design-partner-toggle"
244
- checked={isDesignPartner}
245
- onCheckedChange={setIsDesignPartner}
246
- disabled={isPending || submitResult?.success}
247
- />
248
- </div>
249
- )}
250
-
251
- {!isCompact && isDesignPartner && (
252
- <div className="space-y-2">
253
- <p className="text-sm font-medium">Benefits:</p>
254
- <ul className="text-muted-foreground space-y-1 text-sm">
255
- <li>• Early access to ContractSpec Studio</li>
256
- <li>• 1:1 onboarding and architecture sessions</li>
257
- <li>• Priority support via direct channels</li>
258
- <li>• Influence over roadmap and features</li>
259
- <li>• Founding discount when paid plans launch</li>
260
- </ul>
261
- </div>
262
- )}
263
-
264
- <form onSubmit={onSubmit} className="space-y-4">
265
- {isDesignPartner ? (
266
- <>
267
- <div className="grid gap-4 md:grid-cols-2">
268
- <div className="space-y-2">
269
- <Label htmlFor="waitlist-name" className="text-sm font-medium">
270
- Name <span className="text-red-400">*</span>
271
- </Label>
272
- <Input
273
- id="waitlist-name"
274
- {...designPartnerForm.register('name')}
275
- type="text"
276
- placeholder="Your name"
277
- disabled={isPending || submitResult?.success}
278
- />
279
- {designPartnerForm.formState.errors.name && (
280
- <p className="text-xs text-red-400">
281
- {designPartnerForm.formState.errors.name.message}
282
- </p>
283
- )}
284
- </div>
285
-
286
- <div className="space-y-2">
287
- <Label htmlFor="waitlist-email" className="text-sm font-medium">
288
- Email <span className="text-red-400">*</span>
289
- </Label>
290
- <Input
291
- id="waitlist-email"
292
- {...designPartnerForm.register('email')}
293
- type="email"
294
- placeholder="your@email.com"
295
- disabled={isPending || submitResult?.success}
296
- />
297
- {designPartnerForm.formState.errors.email && (
298
- <p className="text-xs text-red-400">
299
- {designPartnerForm.formState.errors.email.message}
300
- </p>
301
- )}
302
- </div>
303
- </div>
304
-
305
- <div className="grid gap-4 md:grid-cols-2">
306
- <div className="space-y-2">
307
- <Label
308
- htmlFor="waitlist-company"
309
- className="text-sm font-medium"
310
- >
311
- Company / Project Name
312
- </Label>
313
- <Input
314
- id="waitlist-company"
315
- {...designPartnerForm.register('company')}
316
- type="text"
317
- placeholder="Your company or project"
318
- disabled={isPending || submitResult?.success}
319
- />
320
- </div>
321
-
322
- <div className="space-y-2">
323
- <Label htmlFor="waitlist-role" className="text-sm font-medium">
324
- Role
325
- </Label>
326
- <Select
327
- value={designPartnerForm.watch('role') || ''}
328
- onValueChange={(value) =>
329
- designPartnerForm.setValue('role', value)
330
- }
331
- disabled={isPending || submitResult?.success}
332
- >
333
- <SelectTrigger id="waitlist-role" className="w-full">
334
- <SelectValue placeholder="Select your role" />
335
- </SelectTrigger>
336
- <SelectContent>
337
- <SelectItem value="founder">Founder</SelectItem>
338
- <SelectItem value="cto">CTO</SelectItem>
339
- <SelectItem value="lead-engineer">Lead Engineer</SelectItem>
340
- <SelectItem value="engineer">Engineer</SelectItem>
341
- <SelectItem value="product-manager">
342
- Product Manager
343
- </SelectItem>
344
- <SelectItem value="other">Other</SelectItem>
345
- </SelectContent>
346
- </Select>
347
- </div>
348
- </div>
349
-
350
- <div className="grid gap-4 md:grid-cols-2">
351
- <div className="space-y-2">
352
- <Label
353
- htmlFor="waitlist-use-case"
354
- className="text-sm font-medium"
355
- >
356
- Primary use case
357
- </Label>
358
- <Select
359
- value={designPartnerForm.watch('useCase') || ''}
360
- onValueChange={(value) =>
361
- designPartnerForm.setValue('useCase', value)
362
- }
363
- disabled={isPending || submitResult?.success}
364
- >
365
- <SelectTrigger id="waitlist-use-case" className="w-full">
366
- <SelectValue placeholder="Select a use case" />
367
- </SelectTrigger>
368
- <SelectContent>
369
- <SelectItem value="api-platform">API platform</SelectItem>
370
- <SelectItem value="ai-ops">AI operations</SelectItem>
371
- <SelectItem value="integration-hub">
372
- Integration hub
373
- </SelectItem>
374
- <SelectItem value="internal-tools">
375
- Internal tools
376
- </SelectItem>
377
- <SelectItem value="data-pipelines">
378
- Data pipelines
379
- </SelectItem>
380
- <SelectItem value="other">Other</SelectItem>
381
- </SelectContent>
382
- </Select>
383
- </div>
384
-
385
- <div className="space-y-2">
386
- <Label
387
- htmlFor="waitlist-current-stack"
388
- className="text-sm font-medium"
389
- >
390
- Current stack
391
- </Label>
392
- <Input
393
- id="waitlist-current-stack"
394
- {...designPartnerForm.register('currentStack')}
395
- type="text"
396
- placeholder="e.g. Next.js, Postgres, OpenAPI"
397
- disabled={isPending || submitResult?.success}
398
- />
399
- </div>
400
- </div>
401
-
402
- <div className="space-y-2">
403
- <Label
404
- htmlFor="waitlist-what-building"
405
- className="text-sm font-medium"
406
- >
407
- What are you building with AI today?{' '}
408
- <span className="text-red-400">*</span>
409
- </Label>
410
- <Textarea
411
- id="waitlist-what-building"
412
- {...designPartnerForm.register('whatBuilding')}
413
- placeholder="Tell us about your project..."
414
- disabled={isPending || submitResult?.success}
415
- rows={4}
416
- />
417
- {designPartnerForm.formState.errors.whatBuilding && (
418
- <p className="text-xs text-red-400">
419
- {designPartnerForm.formState.errors.whatBuilding.message}
420
- </p>
421
- )}
422
- </div>
423
-
424
- <div className="space-y-2">
425
- <Label
426
- htmlFor="waitlist-what-solving"
427
- className="text-sm font-medium"
428
- >
429
- What do you hope ContractSpec will solve for you?{' '}
430
- <span className="text-red-400">*</span>
431
- </Label>
432
- <Textarea
433
- id="waitlist-what-solving"
434
- {...designPartnerForm.register('whatSolving')}
435
- placeholder="What problems are you trying to solve?"
436
- disabled={isPending || submitResult?.success}
437
- rows={4}
438
- />
439
- {designPartnerForm.formState.errors.whatSolving && (
440
- <p className="text-xs text-red-400">
441
- {designPartnerForm.formState.errors.whatSolving.message}
442
- </p>
443
- )}
444
- </div>
445
-
446
- <div className="grid gap-4 md:grid-cols-2">
447
- <div className="space-y-2">
448
- <Label
449
- htmlFor="waitlist-team-size"
450
- className="text-sm font-medium"
451
- >
452
- Team Size
453
- </Label>
454
- <Select
455
- value={designPartnerForm.watch('teamSize') || ''}
456
- onValueChange={(value) =>
457
- designPartnerForm.setValue('teamSize', value)
458
- }
459
- disabled={isPending || submitResult?.success}
460
- >
461
- <SelectTrigger id="waitlist-team-size" className="w-full">
462
- <SelectValue placeholder="Select team size" />
463
- </SelectTrigger>
464
- <SelectContent>
465
- <SelectItem value="solo">Solo</SelectItem>
466
- <SelectItem value="2-5">2-5</SelectItem>
467
- <SelectItem value="6-20">6-20</SelectItem>
468
- <SelectItem value="20+">20+</SelectItem>
469
- </SelectContent>
470
- </Select>
471
- </div>
472
-
473
- <div className="space-y-2">
474
- <Label
475
- htmlFor="waitlist-timeline"
476
- className="text-sm font-medium"
477
- >
478
- Timeline
479
- </Label>
480
- <Select
481
- value={designPartnerForm.watch('timeline') || ''}
482
- onValueChange={(value) =>
483
- designPartnerForm.setValue('timeline', value)
484
- }
485
- disabled={isPending || submitResult?.success}
486
- >
487
- <SelectTrigger id="waitlist-timeline" className="w-full">
488
- <SelectValue placeholder="Select timeline" />
489
- </SelectTrigger>
490
- <SelectContent>
491
- <SelectItem value="now">Now</SelectItem>
492
- <SelectItem value="1-3-months">1-3 months</SelectItem>
493
- <SelectItem value="3-6-months">3-6 months</SelectItem>
494
- <SelectItem value="exploring">Exploring</SelectItem>
495
- </SelectContent>
496
- </Select>
497
- </div>
498
- </div>
499
-
500
- <div className="space-y-3">
501
- <div className="flex items-start gap-3">
502
- <Checkbox
503
- id="waitlist-open-to-sessions"
504
- checked={designPartnerForm.watch('openToSessions')}
505
- onCheckedChange={(checked) =>
506
- designPartnerForm.setValue(
507
- 'openToSessions',
508
- checked === true
509
- )
510
- }
511
- disabled={isPending || submitResult?.success}
512
- />
513
- <Label
514
- htmlFor="waitlist-open-to-sessions"
515
- className="cursor-pointer text-sm leading-relaxed"
516
- >
517
- I'm open to 1:1 product/design sessions
518
- </Label>
519
- </div>
520
-
521
- <div className="flex items-start gap-3">
522
- <Checkbox
523
- id="waitlist-case-studies"
524
- checked={designPartnerForm.watch('okayWithCaseStudies')}
525
- onCheckedChange={(checked) =>
526
- designPartnerForm.setValue(
527
- 'okayWithCaseStudies',
528
- checked === true
529
- )
530
- }
531
- disabled={isPending || submitResult?.success}
532
- />
533
- <Label
534
- htmlFor="waitlist-case-studies"
535
- className="cursor-pointer text-sm leading-relaxed"
536
- >
537
- I'm okay with anonymized case studies about our usage
538
- </Label>
539
- </div>
540
- </div>
541
- </>
542
- ) : (
543
- <div className="space-y-2">
544
- <Label htmlFor="waitlist-email" className="text-sm font-medium">
545
- Email <span className="text-red-400">*</span>
546
- </Label>
547
- <Input
548
- id="waitlist-email"
549
- {...simpleForm.register('email')}
550
- type="email"
551
- placeholder="your@email.com"
552
- disabled={isPending || submitResult?.success}
553
- />
554
- {simpleForm.formState.errors.email && (
555
- <p className="text-xs text-red-400">
556
- {simpleForm.formState.errors.email.message}
557
- </p>
558
- )}
559
- </div>
560
- )}
561
-
562
- {submitResult && !isPending && (
563
- <div
564
- className={`flex items-start gap-2 rounded-lg p-4 text-sm ${
565
- submitResult.success
566
- ? 'border border-green-500/20 bg-green-500/10 text-green-400'
567
- : 'border border-red-500/20 bg-red-500/10 text-red-400'
568
- }`}
569
- >
570
- {submitResult.success ? (
571
- <CheckCircle size={20} className="mt-0.5 shrink-0" />
572
- ) : (
573
- <AlertCircle size={20} className="mt-0.5 shrink-0" />
574
- )}
575
- <div className="flex-1">
576
- {submitResult.success ? (
577
- <>
578
- <p className="mb-1 font-semibold">You're on the list.</p>
579
- <p className="text-sm">{submitResult.text}</p>
580
- </>
581
- ) : (
582
- <p>{submitResult.text}</p>
583
- )}
584
- </div>
585
- </div>
586
- )}
587
-
588
- <Button
589
- type="submit"
590
- disabled={isPending || submitResult?.success}
591
- className="w-full"
592
- >
593
- {isPending
594
- ? 'Submitting...'
595
- : isDesignPartner
596
- ? 'Apply to the waitlist'
597
- : 'Join waitlist'}
598
- </Button>
599
-
600
- <p className="text-muted-foreground text-center text-xs">
601
- No spam. We'll only email you about ContractSpec and your application.
602
- </p>
603
- </form>
604
- </div>
605
- );
606
- }