rebar-mcp 2.0.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 (248) hide show
  1. package/.claude/agents/template-writer.md +43 -0
  2. package/.claude/agents/test-runner.md +47 -0
  3. package/.claude/mcp.json +9 -0
  4. package/.claude/settings.json +29 -0
  5. package/.claude/skills/ /SKILL.md +21 -0
  6. package/.claude/skills/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/SKILL.md +21 -0
  7. package/.claude/skills/bmmibwetxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  8. package/.claude/skills/bmmibwjgvxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  9. package/.claude/skills/bmmibwsesxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  10. package/.claude/skills/bmmibwxufxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  11. package/.claude/skills/bmmibx3r9xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  12. package/.claude/skills/bmmji0lrkxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  13. package/.claude/skills/bmmjiniphxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  14. package/.claude/skills/bmmjio86zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  15. package/.claude/skills/bmmjiolfbxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  16. package/.claude/skills/bmmjit1lvxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  17. package/.claude/skills/bmmjita1qxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/SKILL.md +21 -0
  18. package/.claude/skills/bnd-mmibweu3/SKILL.md +21 -0
  19. package/.claude/skills/bnd-mmibwjh4/SKILL.md +21 -0
  20. package/.claude/skills/bnd-mmibwsey/SKILL.md +21 -0
  21. package/.claude/skills/bnd-mmibwxup/SKILL.md +21 -0
  22. package/.claude/skills/bnd-mmibx3rg/SKILL.md +21 -0
  23. package/.claude/skills/bnd-mmji0lrp/SKILL.md +21 -0
  24. package/.claude/skills/bnd-mmjinipm/SKILL.md +21 -0
  25. package/.claude/skills/bnd-mmjio875/SKILL.md +21 -0
  26. package/.claude/skills/bnd-mmjiolfg/SKILL.md +21 -0
  27. package/.claude/skills/bnd-mmjit1m3/SKILL.md +21 -0
  28. package/.claude/skills/bnd-mmjita1x/SKILL.md +21 -0
  29. package/.claude/skills/coercion-test/SKILL.md +50 -0
  30. package/.claude/skills/large-skill/SKILL.md +21 -0
  31. package/.claude/skills/long-desc-skill/SKILL.md +21 -0
  32. package/.claude/skills/mcp-dev/SKILL.md +61 -0
  33. package/.claude/skills/nl-mmibweus/SKILL.md +25 -0
  34. package/.claude/skills/nl-mmibwjhf/SKILL.md +25 -0
  35. package/.claude/skills/nl-mmibwsf7/SKILL.md +25 -0
  36. package/.claude/skills/nl-mmibwxvq/SKILL.md +25 -0
  37. package/.claude/skills/nl-mmibx3rt/SKILL.md +25 -0
  38. package/.claude/skills/nl-mmji0lrz/SKILL.md +25 -0
  39. package/.claude/skills/nl-mmjinipx/SKILL.md +25 -0
  40. package/.claude/skills/nl-mmjio87f/SKILL.md +25 -0
  41. package/.claude/skills/nl-mmjiolfs/SKILL.md +25 -0
  42. package/.claude/skills/nl-mmjit1mc/SKILL.md +25 -0
  43. package/.claude/skills/nl-mmjita26/SKILL.md +25 -0
  44. package/.claude/skills/rapid-1/SKILL.md +21 -0
  45. package/.claude/skills/rapid-2/SKILL.md +21 -0
  46. package/.claude/skills/rapid-3/SKILL.md +21 -0
  47. package/.claude/skills/rapid-4/SKILL.md +21 -0
  48. package/.claude/skills/rapid-5/SKILL.md +21 -0
  49. package/.claude/skills/test/", /"malicious/": /"true/SKILL.md" +69 -0
  50. package/.claude/skills/test-emoji-/360/237/230/200-skill/SKILL.md +69 -0
  51. package/.claude/skills/test-skill/SKILL.md +69 -0
  52. package/.claude/skills/test; rm -rf /; skill/SKILL.md +69 -0
  53. package/.claude/skills/test<script>alert(1)</script>skill/SKILL.md +69 -0
  54. package/.claudeignore +5 -0
  55. package/.mcp.json +3 -0
  56. package/CHANGELOG.md +29 -0
  57. package/CLAUDE.md +76 -0
  58. package/LICENSE +21 -0
  59. package/README.md +149 -0
  60. package/ROADMAP.md +526 -0
  61. package/ccboot-PRD-v1.0.docx.md +732 -0
  62. package/ccboot-v1.2.0-enforcement-spec.md +1272 -0
  63. package/dist/cli.d.ts +3 -0
  64. package/dist/cli.d.ts.map +1 -0
  65. package/dist/cli.js +674 -0
  66. package/dist/cli.js.map +1 -0
  67. package/dist/constants.d.ts +25 -0
  68. package/dist/constants.d.ts.map +1 -0
  69. package/dist/constants.js +118 -0
  70. package/dist/constants.js.map +1 -0
  71. package/dist/index.d.ts +3 -0
  72. package/dist/index.d.ts.map +1 -0
  73. package/dist/index.js +47 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/schemas/common.d.ts +62 -0
  76. package/dist/schemas/common.d.ts.map +1 -0
  77. package/dist/schemas/common.js +15 -0
  78. package/dist/schemas/common.js.map +1 -0
  79. package/dist/schemas/scaffolding.d.ts +277 -0
  80. package/dist/schemas/scaffolding.d.ts.map +1 -0
  81. package/dist/schemas/scaffolding.js +133 -0
  82. package/dist/schemas/scaffolding.js.map +1 -0
  83. package/dist/services/claudemd-generator.d.ts +16 -0
  84. package/dist/services/claudemd-generator.d.ts.map +1 -0
  85. package/dist/services/claudemd-generator.js +426 -0
  86. package/dist/services/claudemd-generator.js.map +1 -0
  87. package/dist/services/codex-generator.d.ts +6 -0
  88. package/dist/services/codex-generator.d.ts.map +1 -0
  89. package/dist/services/codex-generator.js +35 -0
  90. package/dist/services/codex-generator.js.map +1 -0
  91. package/dist/services/cursor-generator.d.ts +15 -0
  92. package/dist/services/cursor-generator.d.ts.map +1 -0
  93. package/dist/services/cursor-generator.js +134 -0
  94. package/dist/services/cursor-generator.js.map +1 -0
  95. package/dist/services/file-ops.d.ts +48 -0
  96. package/dist/services/file-ops.d.ts.map +1 -0
  97. package/dist/services/file-ops.js +153 -0
  98. package/dist/services/file-ops.js.map +1 -0
  99. package/dist/services/output-formatter.d.ts +57 -0
  100. package/dist/services/output-formatter.d.ts.map +1 -0
  101. package/dist/services/output-formatter.js +88 -0
  102. package/dist/services/output-formatter.js.map +1 -0
  103. package/dist/services/platform-detect.d.ts +14 -0
  104. package/dist/services/platform-detect.d.ts.map +1 -0
  105. package/dist/services/platform-detect.js +63 -0
  106. package/dist/services/platform-detect.js.map +1 -0
  107. package/dist/services/project-analyzer.d.ts +71 -0
  108. package/dist/services/project-analyzer.d.ts.map +1 -0
  109. package/dist/services/project-analyzer.js +595 -0
  110. package/dist/services/project-analyzer.js.map +1 -0
  111. package/dist/services/rules-engine.d.ts +41 -0
  112. package/dist/services/rules-engine.d.ts.map +1 -0
  113. package/dist/services/rules-engine.js +304 -0
  114. package/dist/services/rules-engine.js.map +1 -0
  115. package/dist/services/strictness.d.ts +37 -0
  116. package/dist/services/strictness.d.ts.map +1 -0
  117. package/dist/services/strictness.js +182 -0
  118. package/dist/services/strictness.js.map +1 -0
  119. package/dist/services/template-engine.d.ts +16 -0
  120. package/dist/services/template-engine.d.ts.map +1 -0
  121. package/dist/services/template-engine.js +85 -0
  122. package/dist/services/template-engine.js.map +1 -0
  123. package/dist/services/validation.d.ts +41 -0
  124. package/dist/services/validation.d.ts.map +1 -0
  125. package/dist/services/validation.js +104 -0
  126. package/dist/services/validation.js.map +1 -0
  127. package/dist/services/windsurf-generator.d.ts +15 -0
  128. package/dist/services/windsurf-generator.d.ts.map +1 -0
  129. package/dist/services/windsurf-generator.js +127 -0
  130. package/dist/services/windsurf-generator.js.map +1 -0
  131. package/dist/tests/enforcement.test.d.ts +2 -0
  132. package/dist/tests/enforcement.test.d.ts.map +1 -0
  133. package/dist/tests/enforcement.test.js +541 -0
  134. package/dist/tests/enforcement.test.js.map +1 -0
  135. package/dist/tests/enterprise.test.d.ts +2 -0
  136. package/dist/tests/enterprise.test.d.ts.map +1 -0
  137. package/dist/tests/enterprise.test.js +353 -0
  138. package/dist/tests/enterprise.test.js.map +1 -0
  139. package/dist/tests/fuzzing.test.d.ts +2 -0
  140. package/dist/tests/fuzzing.test.d.ts.map +1 -0
  141. package/dist/tests/fuzzing.test.js +596 -0
  142. package/dist/tests/fuzzing.test.js.map +1 -0
  143. package/dist/tests/knowledge.test.d.ts +2 -0
  144. package/dist/tests/knowledge.test.d.ts.map +1 -0
  145. package/dist/tests/knowledge.test.js +292 -0
  146. package/dist/tests/knowledge.test.js.map +1 -0
  147. package/dist/tests/management.test.d.ts +2 -0
  148. package/dist/tests/management.test.d.ts.map +1 -0
  149. package/dist/tests/management.test.js +338 -0
  150. package/dist/tests/management.test.js.map +1 -0
  151. package/dist/tests/scaffolding.test.d.ts +2 -0
  152. package/dist/tests/scaffolding.test.d.ts.map +1 -0
  153. package/dist/tests/scaffolding.test.js +419 -0
  154. package/dist/tests/scaffolding.test.js.map +1 -0
  155. package/dist/tests/test-utils.d.ts +76 -0
  156. package/dist/tests/test-utils.d.ts.map +1 -0
  157. package/dist/tests/test-utils.js +171 -0
  158. package/dist/tests/test-utils.js.map +1 -0
  159. package/dist/tests/tool-harness.d.ts +18 -0
  160. package/dist/tests/tool-harness.d.ts.map +1 -0
  161. package/dist/tests/tool-harness.js +51 -0
  162. package/dist/tests/tool-harness.js.map +1 -0
  163. package/dist/tools/enterprise.d.ts +8 -0
  164. package/dist/tools/enterprise.d.ts.map +1 -0
  165. package/dist/tools/enterprise.js +571 -0
  166. package/dist/tools/enterprise.js.map +1 -0
  167. package/dist/tools/knowledge.d.ts +7 -0
  168. package/dist/tools/knowledge.d.ts.map +1 -0
  169. package/dist/tools/knowledge.js +120 -0
  170. package/dist/tools/knowledge.js.map +1 -0
  171. package/dist/tools/management.d.ts +10 -0
  172. package/dist/tools/management.d.ts.map +1 -0
  173. package/dist/tools/management.js +1541 -0
  174. package/dist/tools/management.js.map +1 -0
  175. package/dist/tools/scaffolding.d.ts +8 -0
  176. package/dist/tools/scaffolding.d.ts.map +1 -0
  177. package/dist/tools/scaffolding.js +736 -0
  178. package/dist/tools/scaffolding.js.map +1 -0
  179. package/dist/types.d.ts +54 -0
  180. package/dist/types.d.ts.map +1 -0
  181. package/dist/types.js +5 -0
  182. package/dist/types.js.map +1 -0
  183. package/landing/app/layout.tsx +30 -0
  184. package/landing/app/page.tsx +944 -0
  185. package/landing/next-env.d.ts +6 -0
  186. package/landing/next.config.js +6 -0
  187. package/landing/package-lock.json +896 -0
  188. package/landing/package.json +20 -0
  189. package/landing/tsconfig.json +40 -0
  190. package/package.json +49 -0
  191. package/rebar-v2.0.0-platform-spec.md +1567 -0
  192. package/server.json +20 -0
  193. package/src/cli.ts +735 -0
  194. package/src/constants.ts +131 -0
  195. package/src/index.ts +54 -0
  196. package/src/schemas/common.ts +22 -0
  197. package/src/schemas/scaffolding.ts +161 -0
  198. package/src/services/claudemd-generator.ts +481 -0
  199. package/src/services/codex-generator.ts +44 -0
  200. package/src/services/cursor-generator.ts +153 -0
  201. package/src/services/file-ops.ts +172 -0
  202. package/src/services/platform-detect.ts +80 -0
  203. package/src/services/project-analyzer.ts +690 -0
  204. package/src/services/rules-engine.ts +353 -0
  205. package/src/services/strictness.ts +202 -0
  206. package/src/services/template-engine.ts +119 -0
  207. package/src/services/validation.ts +138 -0
  208. package/src/services/windsurf-generator.ts +145 -0
  209. package/src/tests/enforcement.test.ts +794 -0
  210. package/src/tests/enterprise.test.ts +483 -0
  211. package/src/tests/fuzzing.test.ts +690 -0
  212. package/src/tests/knowledge.test.ts +371 -0
  213. package/src/tests/management.test.ts +451 -0
  214. package/src/tests/scaffolding.test.ts +575 -0
  215. package/src/tests/test-utils.ts +206 -0
  216. package/src/tests/tool-harness.ts +70 -0
  217. package/src/tools/enterprise.ts +666 -0
  218. package/src/tools/knowledge.ts +162 -0
  219. package/src/tools/management.ts +1706 -0
  220. package/src/tools/scaffolding.ts +909 -0
  221. package/src/types.ts +93 -0
  222. package/supabase/.temp/cli-latest +1 -0
  223. package/supabase/.temp/gotrue-version +1 -0
  224. package/supabase/.temp/pooler-url +1 -0
  225. package/supabase/.temp/postgres-version +1 -0
  226. package/supabase/.temp/project-ref +1 -0
  227. package/supabase/.temp/rest-version +1 -0
  228. package/supabase/.temp/storage-migration +1 -0
  229. package/supabase/.temp/storage-version +1 -0
  230. package/templates/agents/explore.md +41 -0
  231. package/templates/agents/plan.md +73 -0
  232. package/templates/agents/security-auditor.md +77 -0
  233. package/templates/agents/test-runner.md +60 -0
  234. package/templates/claudemd/fastapi.md +49 -0
  235. package/templates/claudemd/monorepo.md +48 -0
  236. package/templates/claudemd/nextjs.md +52 -0
  237. package/templates/claudemd/react-spa.md +50 -0
  238. package/templates/claudemd/springboot.md +50 -0
  239. package/templates/hooks/danger-blocker.json +11 -0
  240. package/templates/hooks/format-on-write.json +17 -0
  241. package/templates/hooks/lint-on-write.json +16 -0
  242. package/templates/hooks/secret-detector.json +11 -0
  243. package/templates/skills/code-review.md +68 -0
  244. package/templates/skills/documentation.md +62 -0
  245. package/templates/skills/performance-audit.md +80 -0
  246. package/templates/skills/security-scan.md +66 -0
  247. package/templates/skills/test-writer.md +56 -0
  248. package/tsconfig.json +19 -0
@@ -0,0 +1,944 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState, FormEvent } from "react";
4
+
5
+ const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL!;
6
+ const SUPABASE_ANON_KEY = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;
7
+
8
+ // Simple Supabase fetch helper (no SDK needed)
9
+ async function supabaseInsert(table: string, data: Record<string, unknown>) {
10
+ const response = await fetch(`${SUPABASE_URL}/rest/v1/${table}`, {
11
+ method: 'POST',
12
+ headers: {
13
+ 'Content-Type': 'application/json',
14
+ 'apikey': SUPABASE_ANON_KEY,
15
+ 'Authorization': `Bearer ${SUPABASE_ANON_KEY}`,
16
+ 'Prefer': 'return=minimal'
17
+ },
18
+ body: JSON.stringify(data)
19
+ });
20
+
21
+ if (!response.ok) {
22
+ const error = await response.json().catch(() => ({}));
23
+ throw new Error(error.message || 'Failed to submit');
24
+ }
25
+
26
+ return true;
27
+ }
28
+
29
+ interface LiveStats {
30
+ npmDownloads: number | null;
31
+ githubStars: number | null;
32
+ githubForks: number | null;
33
+ openIssues: number | null;
34
+ loading: boolean;
35
+ }
36
+
37
+ export default function Home() {
38
+ const [stats, setStats] = useState<LiveStats>({
39
+ npmDownloads: null,
40
+ githubStars: null,
41
+ githubForks: null,
42
+ openIssues: null,
43
+ loading: true,
44
+ });
45
+
46
+ useEffect(() => {
47
+ async function fetchStats() {
48
+ try {
49
+ // Fetch npm downloads (last week)
50
+ const npmRes = await fetch(
51
+ "https://api.npmjs.org/downloads/point/last-week/rebar-mcp"
52
+ );
53
+ const npmData = npmRes.ok ? await npmRes.json() : null;
54
+
55
+ // Fetch GitHub stats
56
+ const ghRes = await fetch(
57
+ "https://api.github.com/repos/RCOLKITT/rebar-mcp"
58
+ );
59
+ const ghData = ghRes.ok ? await ghRes.json() : null;
60
+
61
+ setStats({
62
+ npmDownloads: npmData?.downloads ?? null,
63
+ githubStars: ghData?.stargazers_count ?? null,
64
+ githubForks: ghData?.forks_count ?? null,
65
+ openIssues: ghData?.open_issues_count ?? null,
66
+ loading: false,
67
+ });
68
+ } catch {
69
+ setStats((prev) => ({ ...prev, loading: false }));
70
+ }
71
+ }
72
+
73
+ fetchStats();
74
+ }, []);
75
+
76
+ return (
77
+ <div style={{
78
+ minHeight: '100vh',
79
+ background: 'linear-gradient(180deg, #0a0a0a 0%, #141414 50%, #0a0a0a 100%)',
80
+ color: '#fff',
81
+ padding: '40px 20px'
82
+ }}>
83
+ <div style={{ maxWidth: '1000px', margin: '0 auto' }}>
84
+ {/* Hero */}
85
+ <div style={{ textAlign: 'center', marginBottom: '80px', paddingTop: '40px' }}>
86
+ {/* Logo */}
87
+ <div style={{ marginBottom: '32px' }}>
88
+ <RebarLogo />
89
+ </div>
90
+
91
+ <div style={{
92
+ display: 'inline-block',
93
+ background: 'rgba(245, 158, 11, 0.1)',
94
+ border: '1px solid rgba(245, 158, 11, 0.3)',
95
+ borderRadius: '20px',
96
+ padding: '6px 16px',
97
+ fontSize: '0.85rem',
98
+ color: '#f59e0b',
99
+ marginBottom: '24px',
100
+ fontWeight: 500
101
+ }}>
102
+ Works with Claude Code, Cursor, Windsurf & Codex
103
+ </div>
104
+
105
+ <h1 style={{
106
+ fontSize: '3.8rem',
107
+ fontWeight: 800,
108
+ marginBottom: '24px',
109
+ lineHeight: '1.05',
110
+ letterSpacing: '-0.03em'
111
+ }}>
112
+ <span style={{ color: '#fff' }}>Reinforcement for</span><br/>
113
+ <span style={{
114
+ background: 'linear-gradient(90deg, #f59e0b 0%, #ef4444 100%)',
115
+ WebkitBackgroundClip: 'text',
116
+ WebkitTextFillColor: 'transparent'
117
+ }}>AI-generated code</span>
118
+ </h1>
119
+
120
+ <p style={{
121
+ fontSize: '1.35rem',
122
+ color: '#a1a1aa',
123
+ maxWidth: '650px',
124
+ margin: '0 auto 40px',
125
+ lineHeight: '1.6'
126
+ }}>
127
+ Enforcement hooks, quality audits, and opinionated templates that prevent
128
+ AI coding tools from shipping broken code.
129
+ </p>
130
+
131
+ <div style={{
132
+ background: '#18181b',
133
+ borderRadius: '12px',
134
+ padding: '18px 28px',
135
+ fontFamily: '"JetBrains Mono", monospace',
136
+ fontSize: '1rem',
137
+ border: '1px solid #27272a',
138
+ display: 'inline-block',
139
+ marginBottom: '16px'
140
+ }}>
141
+ <span style={{ color: '#71717a' }}>$</span>{' '}
142
+ <span style={{ color: '#f59e0b' }}>claude mcp add</span> rebar --{' '}
143
+ <span style={{ color: '#22c55e' }}>npx rebar-mcp</span>
144
+ </div>
145
+
146
+ <p style={{ color: '#52525b', fontSize: '0.9rem' }}>
147
+ One command. Works immediately.
148
+ </p>
149
+ </div>
150
+
151
+ {/* Live Stats Bar */}
152
+ <div style={{
153
+ display: 'flex',
154
+ justifyContent: 'center',
155
+ gap: '48px',
156
+ marginBottom: '80px',
157
+ padding: '24px 40px',
158
+ background: 'rgba(255,255,255,0.02)',
159
+ borderRadius: '16px',
160
+ border: '1px solid #27272a',
161
+ flexWrap: 'wrap'
162
+ }}>
163
+ <LiveStat
164
+ label="npm downloads"
165
+ value={stats.npmDownloads}
166
+ suffix="/week"
167
+ loading={stats.loading}
168
+ />
169
+ <LiveStat
170
+ label="GitHub stars"
171
+ value={stats.githubStars}
172
+ loading={stats.loading}
173
+ />
174
+ <LiveStat
175
+ label="Forks"
176
+ value={stats.githubForks}
177
+ loading={stats.loading}
178
+ />
179
+ <LiveStat
180
+ label="Open issues"
181
+ value={stats.openIssues}
182
+ loading={stats.loading}
183
+ />
184
+ </div>
185
+
186
+ {/* Problem/Solution */}
187
+ <div style={{
188
+ display: 'grid',
189
+ gridTemplateColumns: '1fr 1fr',
190
+ gap: '24px',
191
+ marginBottom: '80px'
192
+ }}>
193
+ <div style={{
194
+ background: 'rgba(239, 68, 68, 0.05)',
195
+ border: '1px solid rgba(239, 68, 68, 0.2)',
196
+ borderRadius: '16px',
197
+ padding: '30px'
198
+ }}>
199
+ <h3 style={{ color: '#ef4444', marginTop: 0, fontSize: '1.1rem', fontWeight: 600 }}>
200
+ Without Rebar
201
+ </h3>
202
+ <ul style={{ color: '#a1a1aa', lineHeight: '2.2', paddingLeft: '20px', margin: 0 }}>
203
+ <li>&quot;I&apos;ve completed the implementation&quot; <span style={{ color: '#71717a' }}>(no tests run)</span></li>
204
+ <li>Empty catch blocks swallow errors silently</li>
205
+ <li><code style={{ color: '#ef4444', background: 'rgba(239,68,68,0.1)', padding: '2px 6px', borderRadius: '4px' }}>any</code> types everywhere — TypeScript theater</li>
206
+ <li>TODO comments shipped to production</li>
207
+ </ul>
208
+ </div>
209
+ <div style={{
210
+ background: 'rgba(34, 197, 94, 0.05)',
211
+ border: '1px solid rgba(34, 197, 94, 0.2)',
212
+ borderRadius: '16px',
213
+ padding: '30px'
214
+ }}>
215
+ <h3 style={{ color: '#22c55e', marginTop: 0, fontSize: '1.1rem', fontWeight: 600 }}>
216
+ With Rebar
217
+ </h3>
218
+ <ul style={{ color: '#a1a1aa', lineHeight: '2.2', paddingLeft: '20px', margin: 0 }}>
219
+ <li>Tests run after every file change</li>
220
+ <li>Lint errors block until fixed</li>
221
+ <li>Secrets detected and blocked pre-commit</li>
222
+ <li>Destructive commands require approval</li>
223
+ </ul>
224
+ </div>
225
+ </div>
226
+
227
+ {/* Platforms */}
228
+ <div style={{
229
+ textAlign: 'center',
230
+ marginBottom: '80px',
231
+ padding: '50px 40px',
232
+ background: 'linear-gradient(135deg, rgba(245, 158, 11, 0.05) 0%, rgba(239, 68, 68, 0.05) 100%)',
233
+ borderRadius: '20px',
234
+ border: '1px solid rgba(245, 158, 11, 0.15)'
235
+ }}>
236
+ <h2 style={{ marginTop: 0, fontSize: '1.8rem', marginBottom: '12px', fontWeight: 700 }}>
237
+ One tool. Every AI coding platform.
238
+ </h2>
239
+ <p style={{ color: '#71717a', marginBottom: '32px' }}>
240
+ Rebar generates native configuration for each platform
241
+ </p>
242
+ <div style={{
243
+ display: 'grid',
244
+ gridTemplateColumns: 'repeat(4, 1fr)',
245
+ gap: '24px'
246
+ }}>
247
+ <Platform name="Claude Code" files="CLAUDE.md, hooks, skills" />
248
+ <Platform name="Cursor" files=".cursor/rules/" />
249
+ <Platform name="Windsurf" files=".windsurf/rules/" />
250
+ <Platform name="Codex CLI" files="AGENTS.md" />
251
+ </div>
252
+ </div>
253
+
254
+ {/* Strictness Profiles */}
255
+ <div style={{ marginBottom: '80px' }}>
256
+ <h2 style={{ textAlign: 'center', marginBottom: '16px', fontSize: '2rem', fontWeight: 700 }}>
257
+ Three strictness levels
258
+ </h2>
259
+ <p style={{ textAlign: 'center', color: '#71717a', marginBottom: '40px' }}>
260
+ From gentle nudges to iron-fisted enforcement
261
+ </p>
262
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '20px' }}>
263
+ <ProfileCard
264
+ name="Standard"
265
+ emoji="⚡"
266
+ color="#3b82f6"
267
+ description="Notify on issues. Danger + secrets still block."
268
+ features={['Test results shown', 'Lint output displayed', 'Secrets blocked', 'Danger blocked']}
269
+ />
270
+ <ProfileCard
271
+ name="Strict"
272
+ emoji="🛡️"
273
+ color="#f59e0b"
274
+ description="Block on test and lint failures."
275
+ features={['Tests must pass', 'Lint must pass', 'Secrets blocked', 'Danger blocked']}
276
+ recommended
277
+ />
278
+ <ProfileCard
279
+ name="Paranoid"
280
+ emoji="🔒"
281
+ color="#ef4444"
282
+ description="Maximum enforcement. No shortcuts."
283
+ features={['All Strict rules', 'Files < 400 lines', 'No any types', 'No TODOs in commits']}
284
+ />
285
+ </div>
286
+ </div>
287
+
288
+ {/* Quality Checks */}
289
+ <div style={{
290
+ background: 'rgba(255,255,255,0.02)',
291
+ borderRadius: '20px',
292
+ padding: '50px 40px',
293
+ marginBottom: '80px'
294
+ }}>
295
+ <h2 style={{ marginTop: 0, textAlign: 'center', marginBottom: '12px', fontSize: '2rem', fontWeight: 700 }}>
296
+ 8 Quality Checks, Always On
297
+ </h2>
298
+ <p style={{ textAlign: 'center', color: '#71717a', marginBottom: '36px' }}>
299
+ Audit your code with <code style={{ background: '#27272a', padding: '2px 8px', borderRadius: '4px' }}>rebar audit</code>
300
+ </p>
301
+ <div style={{
302
+ display: 'grid',
303
+ gridTemplateColumns: 'repeat(2, 1fr)',
304
+ gap: '16px'
305
+ }}>
306
+ <Check title="Test Coverage" desc="Tests must pass before task completion" />
307
+ <Check title="Error Handling" desc="No empty catch blocks or swallowed errors" />
308
+ <Check title="Type Safety" desc="No any types in TypeScript code" />
309
+ <Check title="Clean Code" desc="No TODO/FIXME in production code" />
310
+ <Check title="File Size" desc="Files under 400 lines, functions under 50" />
311
+ <Check title="Secret Detection" desc="API keys and tokens blocked from commits" />
312
+ <Check title="Dangerous Commands" desc="rm -rf, force push require approval" />
313
+ <Check title="Custom Rules" desc="Add your own rules in .rebar/rules.yaml" />
314
+ </div>
315
+ </div>
316
+
317
+ {/* CLI */}
318
+ <div style={{ marginBottom: '80px' }}>
319
+ <h2 style={{ textAlign: 'center', marginBottom: '16px', fontSize: '2rem', fontWeight: 700 }}>
320
+ Works in CI/CD too
321
+ </h2>
322
+ <p style={{ textAlign: 'center', color: '#71717a', marginBottom: '32px' }}>
323
+ Gate pull requests on quality score
324
+ </p>
325
+ <div style={{
326
+ background: '#18181b',
327
+ borderRadius: '12px',
328
+ padding: '24px',
329
+ fontFamily: '"JetBrains Mono", monospace',
330
+ fontSize: '0.9rem',
331
+ border: '1px solid #27272a',
332
+ overflow: 'auto'
333
+ }}>
334
+ <pre style={{ margin: 0, color: '#a1a1aa' }}>
335
+ {`# .github/workflows/rebar.yml
336
+ name: Rebar Code Quality
337
+ on: [pull_request]
338
+
339
+ jobs:
340
+ quality:
341
+ runs-on: ubuntu-latest
342
+ steps:
343
+ - uses: actions/checkout@v4
344
+ - run: npx rebar-mcp audit --threshold 70`}
345
+ </pre>
346
+ </div>
347
+ </div>
348
+
349
+ {/* Pricing */}
350
+ <div style={{ marginBottom: '80px' }}>
351
+ <h2 style={{ textAlign: 'center', marginBottom: '16px', fontSize: '2rem', fontWeight: 700 }}>
352
+ Pricing
353
+ </h2>
354
+ <p style={{ textAlign: 'center', color: '#71717a', marginBottom: '40px' }}>
355
+ Open source core. Enterprise features coming soon.
356
+ </p>
357
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '20px' }}>
358
+ <PricingCard
359
+ name="Free"
360
+ price="$0"
361
+ period="forever"
362
+ description="Everything you need to enforce AI code quality"
363
+ features={[
364
+ '15 MCP tools',
365
+ '4 compliance packs (HIPAA, SOC2, PCI-DSS, SOX)',
366
+ '5 CLAUDE.md templates',
367
+ '18 lifecycle hook events',
368
+ 'Multi-platform support',
369
+ 'JSON & dry-run modes',
370
+ 'Community support'
371
+ ]}
372
+ cta="Get Started"
373
+ ctaHref="https://github.com/RCOLKITT/rebar-mcp"
374
+ available
375
+ />
376
+ <PricingCard
377
+ name="Pro"
378
+ price="TBD"
379
+ period=""
380
+ description="Advanced compliance and priority support"
381
+ features={[
382
+ 'Everything in Free',
383
+ 'ISO 42001, FedRAMP, GDPR packs',
384
+ 'Priority GitHub support',
385
+ 'Template customization',
386
+ 'Early access to new features'
387
+ ]}
388
+ cta="Join Waitlist"
389
+ ctaHref="#waitlist"
390
+ comingSoon
391
+ />
392
+ <PricingCard
393
+ name="Enterprise"
394
+ price="Custom"
395
+ period=""
396
+ description="Team management and audit capabilities"
397
+ features={[
398
+ 'Everything in Pro',
399
+ 'Team policy dashboard',
400
+ 'Centralized audit logs',
401
+ 'Custom compliance packs',
402
+ 'SSO/SAML integration',
403
+ 'Dedicated support + SLA'
404
+ ]}
405
+ cta="Contact Sales"
406
+ ctaHref="mailto:ryan@vasperacapital.com?subject=Rebar%20Enterprise"
407
+ comingSoon
408
+ />
409
+ </div>
410
+ </div>
411
+
412
+ {/* Compliance Badges */}
413
+ <div style={{
414
+ textAlign: 'center',
415
+ marginBottom: '80px',
416
+ padding: '40px',
417
+ background: 'rgba(255,255,255,0.02)',
418
+ borderRadius: '20px',
419
+ border: '1px solid #27272a'
420
+ }}>
421
+ <h3 style={{ marginTop: 0, marginBottom: '8px', fontSize: '1.3rem', fontWeight: 600 }}>
422
+ Compliance Packs Available Now
423
+ </h3>
424
+ <p style={{ color: '#71717a', marginBottom: '28px', fontSize: '0.9rem' }}>
425
+ Generate hooks and policies for major compliance frameworks
426
+ </p>
427
+ <div style={{ display: 'flex', justifyContent: 'center', gap: '32px', flexWrap: 'wrap' }}>
428
+ <ComplianceBadge name="HIPAA" />
429
+ <ComplianceBadge name="SOC 2" />
430
+ <ComplianceBadge name="PCI-DSS" />
431
+ <ComplianceBadge name="SOX" />
432
+ </div>
433
+ </div>
434
+
435
+ {/* Waitlist */}
436
+ <div id="waitlist" style={{
437
+ textAlign: 'center',
438
+ marginBottom: '80px',
439
+ padding: '50px 40px',
440
+ background: 'linear-gradient(135deg, rgba(245, 158, 11, 0.05) 0%, rgba(239, 68, 68, 0.05) 100%)',
441
+ borderRadius: '20px',
442
+ border: '1px solid rgba(245, 158, 11, 0.15)'
443
+ }}>
444
+ <h2 style={{ marginTop: 0, fontSize: '1.8rem', marginBottom: '12px', fontWeight: 700 }}>
445
+ Interested in Pro or Enterprise?
446
+ </h2>
447
+ <p style={{ color: '#a1a1aa', marginBottom: '28px', maxWidth: '500px', margin: '0 auto 28px' }}>
448
+ We&apos;re building team features and advanced compliance packs. Get notified when they&apos;re ready.
449
+ </p>
450
+ <WaitlistForm />
451
+ </div>
452
+
453
+ {/* CTA */}
454
+ <div style={{
455
+ textAlign: 'center',
456
+ marginBottom: '80px',
457
+ padding: '60px 40px',
458
+ background: 'linear-gradient(135deg, rgba(245, 158, 11, 0.08) 0%, rgba(239, 68, 68, 0.08) 100%)',
459
+ borderRadius: '20px',
460
+ border: '1px solid rgba(245, 158, 11, 0.2)'
461
+ }}>
462
+ <h2 style={{ fontSize: '2.4rem', marginBottom: '16px', fontWeight: 700 }}>
463
+ Stop shipping broken AI code
464
+ </h2>
465
+ <p style={{ color: '#a1a1aa', marginBottom: '32px', fontSize: '1.1rem' }}>
466
+ One command to enforce quality across your entire team
467
+ </p>
468
+ <div style={{
469
+ background: '#18181b',
470
+ borderRadius: '12px',
471
+ padding: '20px 32px',
472
+ fontFamily: '"JetBrains Mono", monospace',
473
+ fontSize: '1.1rem',
474
+ border: '1px solid #27272a',
475
+ display: 'inline-block',
476
+ marginBottom: '24px'
477
+ }}>
478
+ npx rebar-mcp init --strictness strict
479
+ </div>
480
+ <p style={{ color: '#71717a' }}>
481
+ Or as MCP server: <code style={{ background: '#27272a', padding: '4px 10px', borderRadius: '6px' }}>claude mcp add rebar -- npx rebar-mcp</code>
482
+ </p>
483
+ </div>
484
+
485
+ {/* Footer */}
486
+ <div style={{
487
+ textAlign: 'center',
488
+ color: '#52525b',
489
+ paddingTop: '40px',
490
+ borderTop: '1px solid #27272a'
491
+ }}>
492
+ <p>
493
+ Built by{' '}
494
+ <a href="https://vasperacapital.com" style={{ color: '#f59e0b', textDecoration: 'none' }}>
495
+ Vaspera Capital
496
+ </a>
497
+ </p>
498
+ <p style={{ marginTop: '10px' }}>
499
+ <a href="https://github.com/RCOLKITT/rebar-mcp" style={{ color: '#71717a', textDecoration: 'none' }}>
500
+ GitHub
501
+ </a>
502
+ {' · '}
503
+ <a href="https://www.npmjs.com/package/rebar-mcp" style={{ color: '#71717a', textDecoration: 'none' }}>
504
+ npm
505
+ </a>
506
+ {' · '}
507
+ <a href="mailto:ryan@vasperacapital.com" style={{ color: '#71717a', textDecoration: 'none' }}>
508
+ Contact
509
+ </a>
510
+ </p>
511
+ </div>
512
+ </div>
513
+ </div>
514
+ )
515
+ }
516
+
517
+ function LiveStat({ label, value, suffix = "", loading }: { label: string; value: number | null; suffix?: string; loading: boolean }) {
518
+ return (
519
+ <div style={{ textAlign: 'center' }}>
520
+ <div style={{ fontSize: '2rem', fontWeight: 700, color: '#f59e0b', minHeight: '40px' }}>
521
+ {loading ? (
522
+ <span style={{ color: '#52525b' }}>—</span>
523
+ ) : value !== null ? (
524
+ <>
525
+ {value.toLocaleString()}
526
+ {suffix && <span style={{ fontSize: '1rem', color: '#71717a' }}>{suffix}</span>}
527
+ </>
528
+ ) : (
529
+ <span style={{ color: '#52525b' }}>—</span>
530
+ )}
531
+ </div>
532
+ <div style={{ color: '#71717a', fontSize: '0.85rem', marginTop: '4px' }}>{label}</div>
533
+ </div>
534
+ );
535
+ }
536
+
537
+ function RebarLogo() {
538
+ return (
539
+ <svg width="200" height="60" viewBox="0 0 200 60" fill="none" xmlns="http://www.w3.org/2000/svg">
540
+ {/* Crossed rebar pattern */}
541
+ <defs>
542
+ <linearGradient id="rebarGradient" x1="0%" y1="0%" x2="100%" y2="100%">
543
+ <stop offset="0%" stopColor="#f59e0b" />
544
+ <stop offset="100%" stopColor="#ef4444" />
545
+ </linearGradient>
546
+ <linearGradient id="steelGradient" x1="0%" y1="0%" x2="100%" y2="0%">
547
+ <stop offset="0%" stopColor="#71717a" />
548
+ <stop offset="50%" stopColor="#a1a1aa" />
549
+ <stop offset="100%" stopColor="#71717a" />
550
+ </linearGradient>
551
+ </defs>
552
+
553
+ {/* Rebar icon - two crossed bars */}
554
+ <g transform="translate(10, 10)">
555
+ {/* Horizontal bar with ridges */}
556
+ <rect x="0" y="18" width="40" height="8" rx="2" fill="url(#rebarGradient)" />
557
+ <rect x="5" y="16" width="3" height="12" rx="1" fill="url(#rebarGradient)" opacity="0.7" />
558
+ <rect x="12" y="16" width="3" height="12" rx="1" fill="url(#rebarGradient)" opacity="0.7" />
559
+ <rect x="19" y="16" width="3" height="12" rx="1" fill="url(#rebarGradient)" opacity="0.7" />
560
+ <rect x="26" y="16" width="3" height="12" rx="1" fill="url(#rebarGradient)" opacity="0.7" />
561
+ <rect x="33" y="16" width="3" height="12" rx="1" fill="url(#rebarGradient)" opacity="0.7" />
562
+
563
+ {/* Diagonal bar */}
564
+ <rect x="8" y="0" width="8" height="44" rx="2" fill="url(#steelGradient)" transform="rotate(30 20 22)" />
565
+ </g>
566
+
567
+ {/* Text */}
568
+ <text x="60" y="40" fontFamily="Inter, system-ui" fontSize="32" fontWeight="800" fill="white">
569
+ Rebar
570
+ </text>
571
+ </svg>
572
+ )
573
+ }
574
+
575
+ function Platform({ name, files }: { name: string; files: string }) {
576
+ return (
577
+ <div style={{
578
+ background: 'rgba(255,255,255,0.03)',
579
+ borderRadius: '12px',
580
+ padding: '20px 16px',
581
+ border: '1px solid rgba(255,255,255,0.05)'
582
+ }}>
583
+ <div style={{ fontWeight: 600, marginBottom: '8px', fontSize: '1.05rem' }}>{name}</div>
584
+ <div style={{ color: '#71717a', fontSize: '0.85rem', fontFamily: '"JetBrains Mono", monospace' }}>{files}</div>
585
+ </div>
586
+ )
587
+ }
588
+
589
+ function ProfileCard({
590
+ name,
591
+ emoji,
592
+ color,
593
+ description,
594
+ features,
595
+ recommended
596
+ }: {
597
+ name: string;
598
+ emoji: string;
599
+ color: string;
600
+ description: string;
601
+ features: string[];
602
+ recommended?: boolean;
603
+ }) {
604
+ return (
605
+ <div style={{
606
+ background: `rgba(255,255,255,0.02)`,
607
+ borderRadius: '16px',
608
+ padding: '28px 24px',
609
+ border: recommended ? `2px solid ${color}` : '1px solid rgba(255,255,255,0.05)',
610
+ position: 'relative'
611
+ }}>
612
+ {recommended && (
613
+ <div style={{
614
+ position: 'absolute',
615
+ top: '-12px',
616
+ left: '50%',
617
+ transform: 'translateX(-50%)',
618
+ background: color,
619
+ color: '#000',
620
+ padding: '4px 12px',
621
+ borderRadius: '12px',
622
+ fontSize: '0.75rem',
623
+ fontWeight: 600
624
+ }}>
625
+ RECOMMENDED
626
+ </div>
627
+ )}
628
+ <div style={{ fontSize: '2rem', marginBottom: '12px' }}>{emoji}</div>
629
+ <h3 style={{ margin: '0 0 8px', fontSize: '1.3rem', color, fontWeight: 700 }}>{name}</h3>
630
+ <p style={{ color: '#a1a1aa', marginBottom: '20px', fontSize: '0.9rem', lineHeight: '1.5' }}>{description}</p>
631
+ <ul style={{ margin: 0, padding: 0, listStyle: 'none' }}>
632
+ {features.map((f, i) => (
633
+ <li key={i} style={{
634
+ color: '#71717a',
635
+ fontSize: '0.85rem',
636
+ marginBottom: '8px',
637
+ display: 'flex',
638
+ alignItems: 'center',
639
+ gap: '8px'
640
+ }}>
641
+ <span style={{ color }}>&#10003;</span> {f}
642
+ </li>
643
+ ))}
644
+ </ul>
645
+ </div>
646
+ )
647
+ }
648
+
649
+ function Check({ title, desc }: { title: string; desc: string }) {
650
+ return (
651
+ <div style={{
652
+ display: 'flex',
653
+ alignItems: 'flex-start',
654
+ gap: '14px',
655
+ padding: '18px',
656
+ background: 'rgba(255,255,255,0.02)',
657
+ borderRadius: '12px',
658
+ border: '1px solid rgba(255,255,255,0.04)'
659
+ }}>
660
+ <span style={{
661
+ color: '#22c55e',
662
+ fontSize: '1.1rem',
663
+ background: 'rgba(34, 197, 94, 0.1)',
664
+ width: '28px',
665
+ height: '28px',
666
+ display: 'flex',
667
+ alignItems: 'center',
668
+ justifyContent: 'center',
669
+ borderRadius: '6px',
670
+ flexShrink: 0
671
+ }}>&#10003;</span>
672
+ <div>
673
+ <div style={{ fontWeight: 600, marginBottom: '4px' }}>{title}</div>
674
+ <div style={{ color: '#71717a', fontSize: '0.9rem' }}>{desc}</div>
675
+ </div>
676
+ </div>
677
+ )
678
+ }
679
+
680
+ function PricingCard({
681
+ name,
682
+ price,
683
+ period,
684
+ description,
685
+ features,
686
+ cta,
687
+ ctaHref,
688
+ available,
689
+ comingSoon
690
+ }: {
691
+ name: string;
692
+ price: string;
693
+ period: string;
694
+ description: string;
695
+ features: string[];
696
+ cta: string;
697
+ ctaHref: string;
698
+ available?: boolean;
699
+ comingSoon?: boolean;
700
+ }) {
701
+ return (
702
+ <div style={{
703
+ background: available ? 'rgba(34, 197, 94, 0.03)' : 'rgba(255,255,255,0.02)',
704
+ borderRadius: '16px',
705
+ padding: '32px 28px',
706
+ border: available ? '2px solid rgba(34, 197, 94, 0.3)' : '1px solid rgba(255,255,255,0.05)',
707
+ position: 'relative',
708
+ display: 'flex',
709
+ flexDirection: 'column'
710
+ }}>
711
+ {available && (
712
+ <div style={{
713
+ position: 'absolute',
714
+ top: '-12px',
715
+ left: '50%',
716
+ transform: 'translateX(-50%)',
717
+ background: '#22c55e',
718
+ color: '#000',
719
+ padding: '4px 12px',
720
+ borderRadius: '12px',
721
+ fontSize: '0.75rem',
722
+ fontWeight: 600
723
+ }}>
724
+ AVAILABLE NOW
725
+ </div>
726
+ )}
727
+ {comingSoon && (
728
+ <div style={{
729
+ position: 'absolute',
730
+ top: '-12px',
731
+ left: '50%',
732
+ transform: 'translateX(-50%)',
733
+ background: '#52525b',
734
+ color: '#fff',
735
+ padding: '4px 12px',
736
+ borderRadius: '12px',
737
+ fontSize: '0.75rem',
738
+ fontWeight: 600
739
+ }}>
740
+ COMING SOON
741
+ </div>
742
+ )}
743
+ <h3 style={{ margin: '0 0 8px', fontSize: '1.4rem', fontWeight: 700 }}>{name}</h3>
744
+ <div style={{ marginBottom: '12px' }}>
745
+ <span style={{ fontSize: '2.5rem', fontWeight: 800, color: available ? '#22c55e' : '#fff' }}>{price}</span>
746
+ {period && <span style={{ color: '#71717a', fontSize: '0.9rem' }}> {period}</span>}
747
+ </div>
748
+ <p style={{ color: '#a1a1aa', marginBottom: '24px', fontSize: '0.9rem', lineHeight: '1.5' }}>{description}</p>
749
+ <ul style={{ margin: '0 0 28px', padding: 0, listStyle: 'none', flex: 1 }}>
750
+ {features.map((f, i) => (
751
+ <li key={i} style={{
752
+ color: '#a1a1aa',
753
+ fontSize: '0.85rem',
754
+ marginBottom: '10px',
755
+ display: 'flex',
756
+ alignItems: 'flex-start',
757
+ gap: '10px'
758
+ }}>
759
+ <span style={{ color: available ? '#22c55e' : '#71717a', flexShrink: 0 }}>&#10003;</span>
760
+ <span>{f}</span>
761
+ </li>
762
+ ))}
763
+ </ul>
764
+ <a
765
+ href={ctaHref}
766
+ style={{
767
+ display: 'block',
768
+ textAlign: 'center',
769
+ background: available ? '#22c55e' : comingSoon ? 'transparent' : '#f59e0b',
770
+ color: available ? '#000' : comingSoon ? '#a1a1aa' : '#000',
771
+ padding: '12px 20px',
772
+ borderRadius: '8px',
773
+ fontWeight: 600,
774
+ textDecoration: 'none',
775
+ border: comingSoon ? '1px solid #52525b' : 'none'
776
+ }}
777
+ >
778
+ {cta}
779
+ </a>
780
+ </div>
781
+ )
782
+ }
783
+
784
+ function ComplianceBadge({ name }: { name: string }) {
785
+ return (
786
+ <div style={{
787
+ background: 'rgba(245, 158, 11, 0.1)',
788
+ border: '1px solid rgba(245, 158, 11, 0.3)',
789
+ borderRadius: '8px',
790
+ padding: '12px 24px',
791
+ fontWeight: 600,
792
+ fontSize: '0.95rem',
793
+ color: '#f59e0b'
794
+ }}>
795
+ {name}
796
+ </div>
797
+ )
798
+ }
799
+
800
+ function WaitlistForm() {
801
+ const [email, setEmail] = useState('');
802
+ const [interest, setInterest] = useState<'pro' | 'enterprise' | 'both'>('pro');
803
+ const [company, setCompany] = useState('');
804
+ const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
805
+ const [errorMessage, setErrorMessage] = useState('');
806
+
807
+ const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
808
+ e.preventDefault();
809
+ setStatus('loading');
810
+ setErrorMessage('');
811
+
812
+ try {
813
+ await supabaseInsert('waitlist', {
814
+ email,
815
+ interest,
816
+ company: company || null,
817
+ source: 'landing_page'
818
+ });
819
+ setStatus('success');
820
+ setEmail('');
821
+ setCompany('');
822
+ } catch (err) {
823
+ setStatus('error');
824
+ setErrorMessage(err instanceof Error ? err.message : 'Something went wrong');
825
+ }
826
+ };
827
+
828
+ if (status === 'success') {
829
+ return (
830
+ <div style={{
831
+ background: 'rgba(34, 197, 94, 0.1)',
832
+ border: '1px solid rgba(34, 197, 94, 0.3)',
833
+ borderRadius: '12px',
834
+ padding: '24px',
835
+ maxWidth: '400px',
836
+ margin: '0 auto'
837
+ }}>
838
+ <div style={{ color: '#22c55e', fontSize: '1.5rem', marginBottom: '8px' }}>&#10003;</div>
839
+ <div style={{ color: '#22c55e', fontWeight: 600, marginBottom: '4px' }}>You&apos;re on the list!</div>
840
+ <div style={{ color: '#a1a1aa', fontSize: '0.9rem' }}>We&apos;ll notify you when Pro/Enterprise launches.</div>
841
+ </div>
842
+ );
843
+ }
844
+
845
+ return (
846
+ <form onSubmit={handleSubmit} style={{ maxWidth: '400px', margin: '0 auto' }}>
847
+ <div style={{ marginBottom: '16px' }}>
848
+ <input
849
+ type="email"
850
+ required
851
+ placeholder="you@company.com"
852
+ value={email}
853
+ onChange={(e) => setEmail(e.target.value)}
854
+ style={{
855
+ width: '100%',
856
+ padding: '14px 18px',
857
+ borderRadius: '8px',
858
+ border: '1px solid #27272a',
859
+ background: '#18181b',
860
+ color: '#fff',
861
+ fontSize: '1rem',
862
+ outline: 'none'
863
+ }}
864
+ />
865
+ </div>
866
+
867
+ <div style={{ marginBottom: '16px' }}>
868
+ <input
869
+ type="text"
870
+ placeholder="Company (optional)"
871
+ value={company}
872
+ onChange={(e) => setCompany(e.target.value)}
873
+ style={{
874
+ width: '100%',
875
+ padding: '14px 18px',
876
+ borderRadius: '8px',
877
+ border: '1px solid #27272a',
878
+ background: '#18181b',
879
+ color: '#fff',
880
+ fontSize: '1rem',
881
+ outline: 'none'
882
+ }}
883
+ />
884
+ </div>
885
+
886
+ <div style={{ marginBottom: '20px', display: 'flex', gap: '12px', justifyContent: 'center' }}>
887
+ {(['pro', 'enterprise', 'both'] as const).map((opt) => (
888
+ <label
889
+ key={opt}
890
+ style={{
891
+ display: 'flex',
892
+ alignItems: 'center',
893
+ gap: '6px',
894
+ cursor: 'pointer',
895
+ color: interest === opt ? '#f59e0b' : '#71717a',
896
+ fontSize: '0.9rem'
897
+ }}
898
+ >
899
+ <input
900
+ type="radio"
901
+ name="interest"
902
+ value={opt}
903
+ checked={interest === opt}
904
+ onChange={() => setInterest(opt)}
905
+ style={{ accentColor: '#f59e0b' }}
906
+ />
907
+ {opt.charAt(0).toUpperCase() + opt.slice(1)}
908
+ </label>
909
+ ))}
910
+ </div>
911
+
912
+ {status === 'error' && (
913
+ <div style={{
914
+ color: '#ef4444',
915
+ fontSize: '0.85rem',
916
+ marginBottom: '12px',
917
+ padding: '8px 12px',
918
+ background: 'rgba(239, 68, 68, 0.1)',
919
+ borderRadius: '6px'
920
+ }}>
921
+ {errorMessage || 'Failed to join waitlist. Try again.'}
922
+ </div>
923
+ )}
924
+
925
+ <button
926
+ type="submit"
927
+ disabled={status === 'loading'}
928
+ style={{
929
+ width: '100%',
930
+ background: status === 'loading' ? '#52525b' : 'linear-gradient(90deg, #f59e0b 0%, #ef4444 100%)',
931
+ color: '#000',
932
+ padding: '14px 32px',
933
+ borderRadius: '8px',
934
+ fontWeight: 600,
935
+ fontSize: '1rem',
936
+ border: 'none',
937
+ cursor: status === 'loading' ? 'not-allowed' : 'pointer'
938
+ }}
939
+ >
940
+ {status === 'loading' ? 'Joining...' : 'Join the Waitlist'}
941
+ </button>
942
+ </form>
943
+ );
944
+ }