@kennethsolomon/shipkit 3.1.0 → 3.3.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.
@@ -0,0 +1,136 @@
1
+ # Design System — MVP Aesthetic Guidelines
2
+
3
+ Guidelines for generating visually distinctive MVPs that don't look like generic AI output.
4
+
5
+ ---
6
+
7
+ ## Core Principle
8
+
9
+ Every MVP must look **intentionally designed**, not template-generated. The goal is to make visitors think "this looks legit" — good enough to trust with their email and time.
10
+
11
+ ---
12
+
13
+ ## Typography
14
+
15
+ ### Rules
16
+ - Always use **two fonts**: one display/heading font + one body font.
17
+ - Source from Google Fonts. Never use system fonts, Inter, Roboto, or Arial as the primary choice.
18
+ - Vary choices between projects — do not converge on the same fonts repeatedly.
19
+
20
+ ### Suggested Pairings (rotate, don't default to #1)
21
+ 1. **DM Serif Display** + **DM Sans** — editorial, trustworthy
22
+ 2. **Playfair Display** + **Source Sans 3** — luxury, refined
23
+ 3. **Space Grotesk** + **Inter** — tech, modern (use sparingly, very common)
24
+ 4. **Sora** + **Nunito Sans** — friendly, approachable SaaS
25
+ 5. **Clash Display** + **Satoshi** — bold, contemporary
26
+ 6. **Fraunces** + **Commissioner** — warm, distinctive
27
+ 7. **Cabinet Grotesk** + **General Sans** — clean, startup
28
+ 8. **Bricolage Grotesque** + **Geist** — editorial tech
29
+
30
+ ### Scale
31
+ Use a consistent type scale. Recommended base: `16px`.
32
+
33
+ | Element | Size | Weight | Tracking |
34
+ |---------|------|--------|----------|
35
+ | H1 (hero) | 48-72px | 700-800 | -0.02em |
36
+ | H2 (section) | 32-40px | 600-700 | -0.01em |
37
+ | H3 (card title) | 20-24px | 600 | normal |
38
+ | Body | 16-18px | 400 | normal |
39
+ | Small/caption | 13-14px | 400-500 | 0.01em |
40
+
41
+ ---
42
+
43
+ ## Color
44
+
45
+ ### Rules
46
+ - Never use default Tailwind color names without customization (`blue-500`, `gray-100` raw).
47
+ - Define a custom palette in `tailwind.config` under `extend.colors`.
48
+ - Every palette needs: background, foreground, primary accent, secondary accent, muted, border, success, error.
49
+ - Commit to a mood — don't mix warm and cool randomly.
50
+
51
+ ### Palette Strategies (pick one per project)
52
+ 1. **Dark + neon accent** — dark bg (#0a0a0a), light text (#fafafa), vibrant accent (#6366f1 or #22d3ee)
53
+ 2. **Warm neutral + earth accent** — warm white (#faf9f6), dark text (#1a1a1a), terracotta/amber accent
54
+ 3. **Cool minimal** — pure white (#ffffff), slate text (#334155), single accent color
55
+ 4. **Bold saturated** — deep colored bg (#1e1b4b), contrasting text, bright accent
56
+ 5. **Soft pastel** — light tinted bg (#f0fdf4), dark text, pastel accent palette
57
+
58
+ ### Contrast
59
+ - Text on background must meet WCAG AA (4.5:1 for body text, 3:1 for large text).
60
+ - Test accent colors against both light and dark surfaces.
61
+
62
+ ---
63
+
64
+ ## Spacing
65
+
66
+ Use a **4px base unit** with a consistent scale:
67
+
68
+ | Token | Value | Use for |
69
+ |-------|-------|---------|
70
+ | `xs` | 4px | Icon gaps, tight padding |
71
+ | `sm` | 8px | Inline spacing, compact cards |
72
+ | `md` | 16px | Default padding, form gaps |
73
+ | `lg` | 24px | Card padding, section content |
74
+ | `xl` | 32px | Section gaps |
75
+ | `2xl` | 48px | Between major sections |
76
+ | `3xl` | 64px | Hero padding, page-level spacing |
77
+ | `4xl` | 96px | Landing page section separation |
78
+
79
+ ### Layout Rhythm
80
+ - Sections on the landing page should have `py-20` to `py-28` (80-112px) vertical padding.
81
+ - Cards should have consistent `p-6` to `p-8` padding.
82
+ - The max content width should be `max-w-6xl` or `max-w-7xl`, centered.
83
+
84
+ ---
85
+
86
+ ## Components
87
+
88
+ ### Buttons
89
+ - Primary: filled with accent color, white text, `rounded-lg` or `rounded-xl`, `px-6 py-3`.
90
+ - Secondary: outlined or ghost, accent color border/text.
91
+ - Hover: subtle scale (`hover:scale-105`) or color shift. Add `transition-all duration-200`.
92
+ - Never use default browser button styles.
93
+
94
+ ### Cards
95
+ - Background slightly offset from page bg (e.g., white card on gray bg, or lighter card on dark bg).
96
+ - Consistent `rounded-xl` or `rounded-2xl`.
97
+ - Subtle shadow: `shadow-sm` or `shadow-md`. On dark themes use border instead.
98
+ - Hover state for interactive cards: lift (`hover:-translate-y-1 hover:shadow-lg`).
99
+
100
+ ### Forms / Inputs
101
+ - Inputs: `rounded-lg`, visible border, generous padding (`px-4 py-3`).
102
+ - Focus state: accent-colored ring (`focus:ring-2 focus:ring-accent`).
103
+ - Labels above inputs, not floating.
104
+ - Error states: red border + error message below.
105
+
106
+ ### Navigation
107
+ - Sticky/fixed navbar with blur backdrop (`backdrop-blur-md bg-white/80`).
108
+ - Logo/name on left, links center or right, CTA button far right.
109
+ - Mobile: hamburger menu with slide-in drawer or dropdown.
110
+
111
+ ---
112
+
113
+ ## Anti-Patterns — NEVER Do These
114
+
115
+ 1. **Default Tailwind colors** — `bg-blue-500 text-gray-700` without custom palette
116
+ 2. **System fonts** — `-apple-system, BlinkMacSystemFont` or `font-sans` without override
117
+ 3. **Flat layouts** — sections that all look the same width/padding/structure
118
+ 4. **Missing hover states** — buttons/links that don't respond to hover
119
+ 5. **Placeholder.com images** — use gradient boxes, SVG illustrations, or emoji as placeholders instead
120
+ 6. **Lorem ipsum** — generate realistic fake content based on the product context
121
+ 7. **Inconsistent spacing** — mixing random padding/margin values
122
+ 8. **Tiny click targets** — buttons and links must be minimum 44x44px touch targets
123
+ 9. **No visual hierarchy** — everything same size/weight, no emphasis
124
+ 10. **Generic hero** — "Welcome to [Product]" with no visual interest
125
+
126
+ ---
127
+
128
+ ## Responsive Design
129
+
130
+ - **Mobile-first** approach — design for 375px width, enhance for desktop.
131
+ - Breakpoints: `sm` (640px), `md` (768px), `lg` (1024px), `xl` (1280px).
132
+ - Hero text scales down: 48px desktop → 32px mobile.
133
+ - Feature grids: 3 columns desktop → 1 column mobile.
134
+ - Navigation: full links desktop → hamburger mobile.
135
+ - Cards: horizontal layout desktop → stacked mobile.
136
+ - Always test that nothing overflows horizontally on mobile.
@@ -0,0 +1,236 @@
1
+ # Landing Page — SaaS Section Patterns
2
+
3
+ Structure and patterns for the mandatory MVP landing page.
4
+
5
+ ---
6
+
7
+ ## Required Sections (in order)
8
+
9
+ Every landing page must include ALL of these sections. Do not skip any.
10
+
11
+ ### 1. Navbar
12
+
13
+ ```
14
+ [Logo/Name] [Features] [Pricing] [Waitlist] [Join Waitlist →]
15
+ ```
16
+
17
+ - Sticky at top with backdrop blur.
18
+ - Logo or product name (text logo is fine — use display font).
19
+ - 2-4 nav links that scroll to sections (anchor links).
20
+ - CTA button on the right that scrolls to waitlist section.
21
+ - Mobile: collapse to hamburger.
22
+
23
+ ### 2. Hero Section
24
+
25
+ ```
26
+ ┌─────────────────────────────────────────────────┐
27
+ │ │
28
+ │ [small eyebrow badge or label] │
29
+ │ │
30
+ │ Big Bold Headline That Sells │
31
+ │ the Benefit, Not the Feature │
32
+ │ │
33
+ │ A subheadline that elaborates in 1-2 │
34
+ │ sentences. Specific, not vague. │
35
+ │ │
36
+ │ [Primary CTA Button] [Secondary Link] │
37
+ │ │
38
+ │ ┌─────────────────────────────────┐ │
39
+ │ │ Hero visual / app preview / │ │
40
+ │ │ illustration / gradient box │ │
41
+ │ └─────────────────────────────────┘ │
42
+ │ │
43
+ └─────────────────────────────────────────────────┘
44
+ ```
45
+
46
+ - **Headline**: Benefit-driven ("Ship faster" not "Project management tool"). 5-10 words max.
47
+ - **Subheadline**: Explain what it does and for whom. 1-2 sentences.
48
+ - **CTA**: Action verb + outcome ("Join the waitlist", "Get early access", "Start free").
49
+ - **Visual**: App screenshot mockup, abstract gradient, or SVG illustration. Never leave empty.
50
+ - **Eyebrow**: Optional small badge above headline ("Now in beta", "For developers", "AI-powered").
51
+
52
+ ### 3. Social Proof Bar
53
+
54
+ ```
55
+ Trusted by 500+ early adopters
56
+ [Logo] [Logo] [Logo] [Logo] [Logo]
57
+ ```
58
+
59
+ - Single line below hero.
60
+ - Use placeholder company names/logos (styled as gray text or simple SVG shapes).
61
+ - Alternatively: "Join 500+ people on the waitlist" with a count (fake but plausible).
62
+ - Keep subtle — muted colors, small text.
63
+
64
+ ### 4. Features Grid
65
+
66
+ ```
67
+ ┌──────────┐ ┌──────────┐ ┌──────────┐
68
+ │ 🎯 Icon │ │ ⚡ Icon │ │ 🔒 Icon │
69
+ │ Title │ │ Title │ │ Title │
70
+ │ 2-line │ │ 2-line │ │ 2-line │
71
+ │ desc │ │ desc │ │ desc │
72
+ └──────────┘ └──────────┘ └──────────┘
73
+ ```
74
+
75
+ - 3-6 features in a grid (3 columns desktop, 1 mobile).
76
+ - Each card: icon/emoji + title (3-5 words) + description (1-2 sentences).
77
+ - Icons: use emoji or simple SVG. Heroicons or Lucide if the stack supports it.
78
+ - Feature text must match the key features from Step 1.
79
+
80
+ ### 5. How It Works
81
+
82
+ ```
83
+ Step 1 Step 2 Step 3
84
+ ① ② ③
85
+ Sign up Connect your See results
86
+ and set up data source in minutes
87
+ your account in one click
88
+ ```
89
+
90
+ - 3 steps (rarely 4). Numbered or with icons.
91
+ - Each step: number/icon + title + 1-sentence description.
92
+ - Optional: connecting line or arrow between steps.
93
+ - Explains the user journey from signup to value.
94
+
95
+ ### 6. Pricing
96
+
97
+ ```
98
+ ┌──────────┐ ┌──────────────┐ ┌──────────┐
99
+ │ Free │ │ Pro ⭐ │ │Enterprise│
100
+ │ $0/mo │ │ $29/mo │ │ Custom │
101
+ │ │ │ │ │ │
102
+ │ • 3 feat│ │ • All free │ │ • All │
103
+ │ • Basic │ │ • 5 more │ │ • Custom│
104
+ │ │ │ • Priority │ │ • SLA │
105
+ │ [Start] │ │ [Get Pro] │ │ [Contact]│
106
+ └──────────┘ └──────────────┘ └──────────┘
107
+ ```
108
+
109
+ - 2-3 tiers. Middle tier highlighted (border, scale, badge).
110
+ - Prices should be fake but realistic for the product type.
111
+ - Each tier: name, price, feature list (5-7 items), CTA button.
112
+ - Free tier CTA → waitlist. Paid tier CTAs → waitlist (it's an MVP).
113
+ - All buttons route to the waitlist since nothing is real yet.
114
+
115
+ ### 7. Testimonials
116
+
117
+ ```
118
+ ┌──────────────────────────────────┐
119
+ │ "This changed how I work..." │
120
+ │ │
121
+ │ [Avatar] Jane Smith │
122
+ │ CTO, TechCo │
123
+ └──────────────────────────────────┘
124
+ ```
125
+
126
+ - 2-3 testimonial cards. Carousel or grid.
127
+ - Each: quote (1-3 sentences), name, role/company, avatar placeholder.
128
+ - Generate realistic-sounding quotes that align with the product's value prop.
129
+ - Avatars: use gradient circles with initials, or `ui-avatars.com` service.
130
+ - Mark clearly in code comments that these are placeholder testimonials.
131
+
132
+ ### 8. Waitlist / CTA Section
133
+
134
+ ```
135
+ ┌─────────────────────────────────────────────────┐
136
+ │ │
137
+ │ Ready to try {Product}? │
138
+ │ Join the waitlist for early access. │
139
+ │ │
140
+ │ [email@example.com ] [Join →] │
141
+ │ │
142
+ │ ✓ No spam. We'll only email you when │
143
+ │ we launch. │
144
+ │ │
145
+ └─────────────────────────────────────────────────┘
146
+ ```
147
+
148
+ - Prominent section near bottom (but before footer).
149
+ - Headline: compelling CTA ("Ready to X?", "Be the first to try").
150
+ - Email input + submit button on one line (desktop), stacked (mobile).
151
+ - Trust line below: "No spam" or "Join X others".
152
+ - States:
153
+ - **Default**: input + button enabled.
154
+ - **Loading**: button shows spinner, input disabled.
155
+ - **Success**: replace form with "You're on the list! We'll notify you at {email}."
156
+ - **Error**: show error message below input (invalid email, server error).
157
+
158
+ ### 9. Footer
159
+
160
+ ```
161
+ {Product Name} Features | Pricing | Waitlist
162
+ Built with ♥ © 2026 {Product}. All rights reserved.
163
+ ```
164
+
165
+ - Simple. Product name, nav links (repeat from navbar), copyright.
166
+ - Optional: social links (use # placeholders).
167
+ - Dark or muted background to separate from content.
168
+
169
+ ---
170
+
171
+ ## Waitlist Backend Patterns
172
+
173
+ ### Backend Stacks (Next.js, Nuxt, Laravel)
174
+
175
+ **API Route Pattern:**
176
+
177
+ ```
178
+ POST /api/waitlist
179
+ Body: { "email": "user@example.com" }
180
+
181
+ → Validate email format (regex or built-in validator)
182
+ → Read waitlist.json from disk (create if doesn't exist)
183
+ → Check for duplicate email
184
+ → Append { email, timestamp, source: "landing-page" }
185
+ → Write back to waitlist.json
186
+ → Return { success: true, message: "You're on the list!" }
187
+
188
+ Errors:
189
+ → Invalid email: 400 { success: false, message: "Please enter a valid email." }
190
+ → Duplicate: 200 { success: true, message: "You're already on the list!" }
191
+ → Server error: 500 { success: false, message: "Something went wrong. Try again." }
192
+ ```
193
+
194
+ **waitlist.json format:**
195
+ ```json
196
+ {
197
+ "entries": [
198
+ {
199
+ "email": "user@example.com",
200
+ "timestamp": "2026-03-18T10:30:00Z",
201
+ "source": "landing-page"
202
+ }
203
+ ]
204
+ }
205
+ ```
206
+
207
+ The waitlist.json file should be in a non-public location:
208
+ - Next.js: project root `./waitlist.json` (outside `public/`)
209
+ - Nuxt: project root `./waitlist.json`
210
+ - Laravel: `storage/app/waitlist.json`
211
+
212
+ ### Static Stacks (React + Vite)
213
+
214
+ **Formspree Pattern:**
215
+
216
+ ```html
217
+ <form action="https://formspree.io/f/{your-form-id}" method="POST">
218
+ <input type="email" name="email" required />
219
+ <button type="submit">Join Waitlist</button>
220
+ </form>
221
+ ```
222
+
223
+ - Handle submission via JavaScript fetch for better UX (show loading/success states).
224
+ - Add a code comment: `// Replace {your-form-id} with your Formspree form ID — create one free at formspree.io`
225
+ - Handle Formspree's response format for success/error states.
226
+
227
+ ---
228
+
229
+ ## Copywriting Guidelines
230
+
231
+ - **Headlines**: Lead with the benefit, not the feature. "Save 10 hours a week" > "Task management tool".
232
+ - **Subheadlines**: Be specific about who and what. "For freelancers who juggle too many clients" > "For everyone".
233
+ - **CTAs**: Action verb + outcome. "Get early access" > "Submit". "Join the waitlist" > "Sign up".
234
+ - **Feature descriptions**: Problem → solution format. "Stop losing track of invoices. Auto-track every payment in real time."
235
+ - **Tone**: Match the product. B2B SaaS = professional but warm. Dev tools = casual and direct. Consumer = friendly and energetic.
236
+ - **Never use**: "Revolutionize", "leverage", "synergy", "disrupt", "cutting-edge" (overused startup jargon).
@@ -0,0 +1,321 @@
1
+ # Laravel + Blade + Tailwind — Stack Reference
2
+
3
+ ## Scaffold
4
+
5
+ ```bash
6
+ composer create-project laravel/laravel {project-name}
7
+ cd {project-name}
8
+ npm install
9
+ ```
10
+
11
+ Laravel ships with Tailwind and Vite out of the box (Laravel 11+).
12
+
13
+ ## Directory Structure
14
+
15
+ ```
16
+ {project-name}/
17
+ ├── resources/
18
+ │ ├── views/
19
+ │ │ ├── layouts/
20
+ │ │ │ ├── landing.blade.php ← landing page layout
21
+ │ │ │ └── app.blade.php ← app layout (sidebar)
22
+ │ │ ├── components/
23
+ │ │ │ ├── landing/
24
+ │ │ │ │ ├── navbar.blade.php
25
+ │ │ │ │ ├── hero.blade.php
26
+ │ │ │ │ ├── features.blade.php
27
+ │ │ │ │ ├── how-it-works.blade.php
28
+ │ │ │ │ ├── pricing.blade.php
29
+ │ │ │ │ ├── testimonials.blade.php
30
+ │ │ │ │ ├── waitlist-form.blade.php
31
+ │ │ │ │ └── footer.blade.php
32
+ │ │ │ ├── app/
33
+ │ │ │ │ ├── sidebar.blade.php
34
+ │ │ │ │ └── {feature components}
35
+ │ │ │ └── ui/
36
+ │ │ │ ├── button.blade.php
37
+ │ │ │ ├── input.blade.php
38
+ │ │ │ ├── card.blade.php
39
+ │ │ │ └── modal.blade.php
40
+ │ │ ├── landing.blade.php ← landing page
41
+ │ │ ├── dashboard.blade.php ← dashboard
42
+ │ │ ├── {feature-1}.blade.php
43
+ │ │ ├── {feature-2}.blade.php
44
+ │ │ └── settings.blade.php
45
+ │ ├── css/
46
+ │ │ └── app.css ← Tailwind directives + custom vars
47
+ │ └── js/
48
+ │ └── app.js ← Vite entry + Alpine.js for interactivity
49
+ ├── routes/
50
+ │ └── web.php ← all routes
51
+ ├── app/
52
+ │ └── Http/
53
+ │ └── Controllers/
54
+ │ └── WaitlistController.php
55
+ ├── storage/
56
+ │ └── app/
57
+ │ └── waitlist.json ← email storage (auto-created)
58
+ ├── public/
59
+ │ └── {compiled assets}
60
+ ├── tailwind.config.js
61
+ ├── vite.config.js
62
+ └── package.json
63
+ ```
64
+
65
+ ## Routes
66
+
67
+ `routes/web.php`:
68
+
69
+ ```php
70
+ use App\Http\Controllers\WaitlistController;
71
+
72
+ // Landing page
73
+ Route::view('/', 'landing');
74
+
75
+ // App pages
76
+ Route::view('/dashboard', 'dashboard');
77
+ Route::view('/{feature-1}', '{feature-1}');
78
+ Route::view('/{feature-2}', '{feature-2}');
79
+ Route::view('/settings', 'settings');
80
+
81
+ // Waitlist API
82
+ Route::post('/api/waitlist', [WaitlistController::class, 'store']);
83
+ ```
84
+
85
+ ## Layouts
86
+
87
+ ### Landing Layout (`resources/views/layouts/landing.blade.php`)
88
+
89
+ ```blade
90
+ <!DOCTYPE html>
91
+ <html lang="en">
92
+ <head>
93
+ <meta charset="UTF-8">
94
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
95
+ <title>{{ $title ?? '{Product Name}' }}</title>
96
+ <link rel="preconnect" href="https://fonts.googleapis.com">
97
+ <link href="https://fonts.googleapis.com/css2?family={DisplayFont}:wght@400;600;700;800&family={BodyFont}:wght@400;500;600&display=swap" rel="stylesheet">
98
+ @vite(['resources/css/app.css', 'resources/js/app.js'])
99
+ </head>
100
+ <body class="bg-bg text-fg font-body antialiased">
101
+ {{ $slot }}
102
+ </body>
103
+ </html>
104
+ ```
105
+
106
+ ### App Layout (`resources/views/layouts/app.blade.php`)
107
+
108
+ ```blade
109
+ <!DOCTYPE html>
110
+ <html lang="en">
111
+ <head>
112
+ <meta charset="UTF-8">
113
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
114
+ <title>{{ $title ?? '{Product Name}' }}</title>
115
+ <link rel="preconnect" href="https://fonts.googleapis.com">
116
+ <link href="https://fonts.googleapis.com/css2?family={DisplayFont}:wght@400;600;700;800&family={BodyFont}:wght@400;500;600&display=swap" rel="stylesheet">
117
+ @vite(['resources/css/app.css', 'resources/js/app.js'])
118
+ </head>
119
+ <body class="bg-bg text-fg font-body antialiased">
120
+ <div class="flex min-h-screen">
121
+ <x-app.sidebar />
122
+ <main class="flex-1 p-6 lg:p-8">
123
+ {{ $slot }}
124
+ </main>
125
+ </div>
126
+ </body>
127
+ </html>
128
+ ```
129
+
130
+ ## Tailwind Config
131
+
132
+ `tailwind.config.js`:
133
+
134
+ ```js
135
+ export default {
136
+ content: [
137
+ './resources/**/*.blade.php',
138
+ './resources/**/*.js',
139
+ ],
140
+ theme: {
141
+ extend: {
142
+ colors: {
143
+ bg: 'var(--color-bg)',
144
+ fg: 'var(--color-fg)',
145
+ accent: 'var(--color-accent)',
146
+ muted: 'var(--color-muted)',
147
+ },
148
+ fontFamily: {
149
+ display: ['{DisplayFont}', 'serif'],
150
+ body: ['{BodyFont}', 'sans-serif'],
151
+ },
152
+ },
153
+ },
154
+ }
155
+ ```
156
+
157
+ CSS variables in `resources/css/app.css`:
158
+
159
+ ```css
160
+ @tailwind base;
161
+ @tailwind components;
162
+ @tailwind utilities;
163
+
164
+ :root {
165
+ --color-bg: #xxxxxx;
166
+ --color-fg: #xxxxxx;
167
+ --color-accent: #xxxxxx;
168
+ --color-muted: #xxxxxx;
169
+ }
170
+ ```
171
+
172
+ ## Interactivity with Alpine.js
173
+
174
+ Install Alpine.js for lightweight interactivity (modals, toasts, form states):
175
+
176
+ ```bash
177
+ npm install alpinejs
178
+ ```
179
+
180
+ `resources/js/app.js`:
181
+
182
+ ```js
183
+ import Alpine from 'alpinejs'
184
+ window.Alpine = Alpine
185
+ Alpine.start()
186
+ ```
187
+
188
+ ## Waitlist Controller
189
+
190
+ `app/Http/Controllers/WaitlistController.php`:
191
+
192
+ ```php
193
+ <?php
194
+
195
+ namespace App\Http\Controllers;
196
+
197
+ use Illuminate\Http\Request;
198
+ use Illuminate\Support\Facades\Storage;
199
+
200
+ class WaitlistController extends Controller
201
+ {
202
+ public function store(Request $request)
203
+ {
204
+ $request->validate([
205
+ 'email' => 'required|email',
206
+ ]);
207
+
208
+ $email = $request->input('email');
209
+ $path = 'waitlist.json';
210
+
211
+ // Read or create
212
+ $data = Storage::exists($path)
213
+ ? json_decode(Storage::get($path), true)
214
+ : ['entries' => []];
215
+
216
+ // Check duplicate
217
+ $exists = collect($data['entries'])->contains('email', $email);
218
+ if ($exists) {
219
+ return response()->json(['success' => true, 'message' => "You're already on the list!"]);
220
+ }
221
+
222
+ // Append
223
+ $data['entries'][] = [
224
+ 'email' => $email,
225
+ 'timestamp' => now()->toISOString(),
226
+ 'source' => 'landing-page',
227
+ ];
228
+
229
+ Storage::put($path, json_encode($data, JSON_PRETTY_PRINT));
230
+
231
+ return response()->json(['success' => true, 'message' => "You're on the list!"]);
232
+ }
233
+ }
234
+ ```
235
+
236
+ ## Blade Component Patterns
237
+
238
+ ### Anonymous Components (preferred for UI)
239
+
240
+ `resources/views/components/ui/button.blade.php`:
241
+
242
+ ```blade
243
+ @props(['variant' => 'primary', 'size' => 'md'])
244
+
245
+ @php
246
+ $classes = match($variant) {
247
+ 'primary' => 'bg-accent text-white hover:opacity-90',
248
+ 'secondary' => 'border border-accent text-accent hover:bg-accent/10',
249
+ 'ghost' => 'text-fg hover:bg-muted/20',
250
+ };
251
+ $sizes = match($size) {
252
+ 'sm' => 'px-4 py-2 text-sm',
253
+ 'md' => 'px-6 py-3 text-base',
254
+ 'lg' => 'px-8 py-4 text-lg',
255
+ };
256
+ @endphp
257
+
258
+ <button {{ $attributes->merge(['class' => "$classes $sizes rounded-xl font-medium transition-all duration-200"]) }}>
259
+ {{ $slot }}
260
+ </button>
261
+ ```
262
+
263
+ Usage: `<x-ui.button variant="primary">Join Waitlist</x-ui.button>`
264
+
265
+ ### Landing Page
266
+
267
+ `resources/views/landing.blade.php`:
268
+
269
+ ```blade
270
+ <x-layouts.landing>
271
+ <x-landing.navbar />
272
+ <x-landing.hero />
273
+ <x-landing.features />
274
+ <x-landing.how-it-works />
275
+ <x-landing.pricing />
276
+ <x-landing.testimonials />
277
+ <x-landing.waitlist-form />
278
+ <x-landing.footer />
279
+ </x-layouts.landing>
280
+ ```
281
+
282
+ ### Waitlist Form with Alpine.js
283
+
284
+ ```blade
285
+ <div x-data="{ email: '', status: 'idle', message: '' }">
286
+ <form @submit.prevent="
287
+ status = 'loading';
288
+ fetch('/api/waitlist', {
289
+ method: 'POST',
290
+ headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': '{{ csrf_token() }}' },
291
+ body: JSON.stringify({ email })
292
+ })
293
+ .then(r => r.json())
294
+ .then(d => { status = 'success'; message = d.message; })
295
+ .catch(() => { status = 'error'; message = 'Something went wrong.'; })
296
+ ">
297
+ <template x-if="status !== 'success'">
298
+ <div class="flex gap-3">
299
+ <input x-model="email" type="email" placeholder="you@example.com" required
300
+ class="flex-1 px-4 py-3 rounded-lg border focus:ring-2 focus:ring-accent" />
301
+ <x-ui.button type="submit" x-bind:disabled="status === 'loading'">
302
+ <span x-show="status !== 'loading'">Join Waitlist</span>
303
+ <span x-show="status === 'loading'">Joining...</span>
304
+ </x-ui.button>
305
+ </div>
306
+ </template>
307
+ <p x-show="status === 'success'" x-text="message" class="text-green-600 font-medium"></p>
308
+ <p x-show="status === 'error'" x-text="message" class="text-red-500 text-sm mt-2"></p>
309
+ </form>
310
+ </div>
311
+ ```
312
+
313
+ ## Dev Server
314
+
315
+ ```bash
316
+ # Run both in separate terminals (or use Concurrently)
317
+ php artisan serve # http://localhost:8000
318
+ npm run dev # Vite dev server for assets
319
+ ```
320
+
321
+ Or use Laravel Herd if available (auto-serves at `{project-name}.test`).