@nordsym/apiclaw 1.4.2 → 1.4.4

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 (71) hide show
  1. package/AGENTS.md +115 -0
  2. package/CHANGELOG.md +86 -0
  3. package/CONTRIBUTING.md +84 -0
  4. package/README.md +196 -436
  5. package/convex/_generated/api.d.ts +4 -0
  6. package/convex/adminActivate.ts +54 -0
  7. package/convex/http.ts +112 -18
  8. package/convex/logs.ts +25 -0
  9. package/convex/mou.ts +91 -0
  10. package/convex/schema.ts +20 -0
  11. package/convex/searchLogs.ts +111 -106
  12. package/convex/workspaces.ts +76 -0
  13. package/dist/cli/commands/doctor.d.ts.map +1 -1
  14. package/dist/cli/commands/doctor.js +45 -32
  15. package/dist/cli/commands/doctor.js.map +1 -1
  16. package/dist/cli/commands/setup.js +2 -2
  17. package/dist/cli/commands/setup.js.map +1 -1
  18. package/dist/stripe.js +2 -2
  19. package/dist/stripe.js.map +1 -1
  20. package/dist/ui/errors.js +17 -17
  21. package/dist/ui/errors.js.map +1 -1
  22. package/dist/ui/prompts.js +1 -1
  23. package/dist/ui/prompts.js.map +1 -1
  24. package/landing/.env.local.stripe +2 -0
  25. package/landing/package-lock.json +916 -2
  26. package/landing/package.json +2 -0
  27. package/landing/public/.well-known/ai-plugin.json +17 -0
  28. package/landing/public/.well-known/openapi.json +295 -0
  29. package/landing/public/llms-full.txt +322 -0
  30. package/landing/public/llms.txt +61 -72
  31. package/landing/public/robots.txt +28 -0
  32. package/landing/public/sitemap.xml +15 -0
  33. package/landing/src/app/admin/page.tsx +1 -1
  34. package/landing/src/app/api/auth/magic-link/route.ts +1 -1
  35. package/landing/src/app/api/auth/session/route.ts +1 -1
  36. package/landing/src/app/api/auth/verify/route.ts +1 -1
  37. package/landing/src/app/api/billing/checkout/route.ts +1 -1
  38. package/landing/src/app/api/billing/payment-method/route.ts +1 -1
  39. package/landing/src/app/api/billing/portal/route.ts +1 -1
  40. package/landing/src/app/api/mou/sign/route.ts +312 -0
  41. package/landing/src/app/api/stripe/webhook/route.ts +1 -1
  42. package/landing/src/app/api/workspace-auth/magic-link/route.ts +1 -1
  43. package/landing/src/app/api/workspace-auth/session/route.ts +1 -1
  44. package/landing/src/app/api/workspace-auth/verify/route.ts +1 -1
  45. package/landing/src/app/auth/verify/page.tsx +1 -1
  46. package/landing/src/app/founding-backer/success/page.tsx +115 -0
  47. package/landing/src/app/layout.tsx +35 -0
  48. package/landing/src/app/mou/[partnerId]/page.tsx +424 -0
  49. package/landing/src/app/mou/coaccept/page.tsx +416 -0
  50. package/landing/src/app/page.tsx +38 -11
  51. package/landing/src/app/providers/dashboard/[apiId]/actions/[actionId]/edit/page.tsx +1 -1
  52. package/landing/src/app/providers/dashboard/[apiId]/actions/new/page.tsx +1 -1
  53. package/landing/src/app/providers/dashboard/[apiId]/actions/page.tsx +1 -1
  54. package/landing/src/app/providers/register/page.tsx +2 -2
  55. package/landing/src/app/upgrade/page.tsx +1 -1
  56. package/landing/src/app/workspace/chains/page.tsx +1 -1
  57. package/landing/src/app/workspace/page.tsx +1 -1
  58. package/landing/src/components/EarnCreditsTab.tsx +1 -1
  59. package/landing/src/components/HeroTabs.tsx +2 -2
  60. package/landing/src/lib/convex-client.ts +1 -1
  61. package/landing/src/lib/pdf.ts +24 -0
  62. package/landing/src/lib/stats.json +3 -2
  63. package/landing/src/middleware.ts +1 -1
  64. package/landing/vercel.json +8 -0
  65. package/package.json +2 -2
  66. package/scripts/activate-hivr-workspace.ts +20 -0
  67. package/src/cli/commands/doctor.ts +49 -36
  68. package/src/cli/commands/setup.ts +2 -2
  69. package/src/stripe.ts +2 -2
  70. package/src/ui/errors.ts +17 -17
  71. package/src/ui/prompts.ts +1 -1
@@ -0,0 +1,115 @@
1
+ import Link from "next/link";
2
+ import { CheckCircle2, ArrowRight, Sparkles, ShieldCheck, Rocket } from "lucide-react";
3
+
4
+ const FOUNDING_BACKER_PAYMENT_LINK = "https://buy.stripe.com/fZu00l5084em8SU6X6cMM0u";
5
+
6
+ export const metadata = {
7
+ title: "Founding Backer Confirmed | APIClaw",
8
+ description: "Your Founding Backer access is active. Welcome to APIClaw.",
9
+ };
10
+
11
+ type SuccessPageProps = {
12
+ searchParams?: {
13
+ session_id?: string;
14
+ };
15
+ };
16
+
17
+ export default function FoundingBackerSuccessPage({ searchParams }: SuccessPageProps) {
18
+ const sessionId = searchParams?.session_id;
19
+
20
+ return (
21
+ <main className="min-h-screen bg-[#0a0a0a] text-zinc-100">
22
+ <div className="pointer-events-none absolute inset-0 overflow-hidden">
23
+ <div className="absolute left-1/2 top-[-240px] h-[520px] w-[520px] -translate-x-1/2 rounded-full bg-red-500/15 blur-3xl" />
24
+ <div className="absolute bottom-[-220px] right-[-120px] h-[420px] w-[420px] rounded-full bg-red-400/10 blur-3xl" />
25
+ </div>
26
+
27
+ <div className="relative mx-auto flex w-full max-w-5xl flex-col gap-10 px-6 py-14 md:px-10 md:py-20">
28
+ <section className="rounded-3xl border border-red-500/30 bg-zinc-950/80 p-8 shadow-[0_0_90px_rgba(239,68,68,0.14)] backdrop-blur md:p-10">
29
+ <div className="mb-6 inline-flex items-center gap-2 rounded-full border border-red-500/40 bg-red-500/10 px-4 py-2 text-sm font-semibold text-red-300">
30
+ <Sparkles className="h-4 w-4" />
31
+ Founding Backer Confirmed
32
+ </div>
33
+
34
+ <h1 className="text-balance text-4xl font-black tracking-tight text-white md:text-5xl">
35
+ You are in.
36
+ </h1>
37
+ <p className="mt-4 max-w-2xl text-pretty text-lg text-zinc-300">
38
+ Thank you for backing APIClaw early. Your Founding Backer access is active, including free API usage through December 31, 2026.
39
+ </p>
40
+
41
+ <div className="mt-8 grid gap-4 sm:grid-cols-3">
42
+ <div className="rounded-2xl border border-zinc-800 bg-zinc-900/70 p-4">
43
+ <div className="mb-2 inline-flex rounded-lg bg-red-500/15 p-2 text-red-300">
44
+ <CheckCircle2 className="h-4 w-4" />
45
+ </div>
46
+ <p className="text-sm font-semibold text-zinc-100">Status</p>
47
+ <p className="mt-1 text-sm text-zinc-400">Payment completed</p>
48
+ </div>
49
+ <div className="rounded-2xl border border-zinc-800 bg-zinc-900/70 p-4">
50
+ <div className="mb-2 inline-flex rounded-lg bg-red-500/15 p-2 text-red-300">
51
+ <ShieldCheck className="h-4 w-4" />
52
+ </div>
53
+ <p className="text-sm font-semibold text-zinc-100">Access window</p>
54
+ <p className="mt-1 text-sm text-zinc-400">Free usage until 2026-12-31</p>
55
+ </div>
56
+ <div className="rounded-2xl border border-zinc-800 bg-zinc-900/70 p-4">
57
+ <div className="mb-2 inline-flex rounded-lg bg-red-500/15 p-2 text-red-300">
58
+ <Rocket className="h-4 w-4" />
59
+ </div>
60
+ <p className="text-sm font-semibold text-zinc-100">Next step</p>
61
+ <p className="mt-1 text-sm text-zinc-400">Open workspace and start building</p>
62
+ </div>
63
+ </div>
64
+
65
+ {sessionId ? (
66
+ <p className="mt-6 font-mono text-xs text-zinc-500">Session: {sessionId}</p>
67
+ ) : null}
68
+
69
+ <div className="mt-8 flex flex-col gap-3 sm:flex-row">
70
+ <Link
71
+ href="/workspace"
72
+ className="inline-flex items-center justify-center gap-2 rounded-xl bg-red-500 px-5 py-3 text-sm font-bold text-white transition hover:bg-red-400"
73
+ >
74
+ Open APIClaw Workspace
75
+ <ArrowRight className="h-4 w-4" />
76
+ </Link>
77
+ <Link
78
+ href="/docs"
79
+ className="inline-flex items-center justify-center gap-2 rounded-xl border border-zinc-700 bg-zinc-900 px-5 py-3 text-sm font-semibold text-zinc-200 transition hover:border-zinc-500 hover:bg-zinc-800"
80
+ >
81
+ Setup Guides
82
+ </Link>
83
+ <a
84
+ href={FOUNDING_BACKER_PAYMENT_LINK}
85
+ target="_blank"
86
+ rel="noreferrer"
87
+ className="inline-flex items-center justify-center gap-2 rounded-xl border border-red-500/50 bg-red-500/10 px-5 py-3 text-sm font-semibold text-red-200 transition hover:bg-red-500/20"
88
+ >
89
+ Founding Backer Link
90
+ </a>
91
+ </div>
92
+ </section>
93
+
94
+ <section className="terminal">
95
+ <div className="terminal-header">
96
+ <span className="terminal-dot terminal-dot-red" />
97
+ <span className="terminal-dot terminal-dot-yellow" />
98
+ <span className="terminal-dot terminal-dot-green" />
99
+ <span className="terminal-title">next-steps.sh</span>
100
+ </div>
101
+ <div className="terminal-body">
102
+ <p className="terminal-command">
103
+ <span className="terminal-prompt">$</span> npx @nordsym/apiclaw mcp-install
104
+ </p>
105
+ <p className="terminal-output mt-2">Auto-configures supported MCP clients</p>
106
+ <p className="terminal-command mt-4">
107
+ <span className="terminal-prompt">$</span> codex mcp add apiclaw -- node /path/to/apiclaw/dist/index.js
108
+ </p>
109
+ <p className="terminal-output mt-2">Recommended for local Codex integration</p>
110
+ </div>
111
+ </section>
112
+ </div>
113
+ </main>
114
+ );
115
+ }
@@ -43,6 +43,37 @@ export const metadata: Metadata = {
43
43
  robots: "index, follow",
44
44
  };
45
45
 
46
+ // Schema.org JSON-LD
47
+ const schemaOrg = {
48
+ "@context": "https://schema.org",
49
+ "@graph": [
50
+ {
51
+ "@type": "WebSite",
52
+ "name": "APIClaw",
53
+ "url": "https://apiclaw.nordsym.com",
54
+ "description": "The API layer for AI agents. Discover and call APIs via MCP with structured data and ranked results."
55
+ },
56
+ {
57
+ "@type": "Organization",
58
+ "name": "NordSym AB",
59
+ "url": "https://nordsym.com"
60
+ },
61
+ {
62
+ "@type": "SoftwareApplication",
63
+ "name": "APIClaw",
64
+ "applicationCategory": "DeveloperApplication",
65
+ "operatingSystem": "Web",
66
+ "description": "API discovery and execution layer for AI agents. 22,000+ APIs indexed. MCP native. Direct Call providers.",
67
+ "offers": {
68
+ "@type": "Offer",
69
+ "price": "0",
70
+ "priceCurrency": "USD",
71
+ "description": "Free tier available"
72
+ }
73
+ }
74
+ ]
75
+ };
76
+
46
77
  export default function RootLayout({
47
78
  children,
48
79
  }: Readonly<{
@@ -57,6 +88,10 @@ export default function RootLayout({
57
88
  <meta name="theme-color" content="#ef4444" />
58
89
  <meta name="apple-mobile-web-app-capable" content="yes" />
59
90
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
91
+ <script
92
+ type="application/ld+json"
93
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaOrg) }}
94
+ />
60
95
  </head>
61
96
  <body className="antialiased bg-background text-text-primary">
62
97
  {children}
@@ -0,0 +1,424 @@
1
+ "use client";
2
+
3
+ import { useEffect, useRef, useState } from "react";
4
+ import { useParams } from "next/navigation";
5
+
6
+ // Type definitions
7
+ type MOUSection = {
8
+ title: string;
9
+ content?: string[];
10
+ phases?: { name: string; desc: string }[];
11
+ };
12
+
13
+ type MOUPartner = {
14
+ partnerName: string;
15
+ partnerRepresentative: string;
16
+ sections: MOUSection[];
17
+ };
18
+
19
+ // MOU content for partners
20
+ const mouContent: Record<string, MOUPartner> = {
21
+ cqtinvest: {
22
+ partnerName: "CQT Invest",
23
+ partnerRepresentative: "Mohammed Alubeid",
24
+ sections: [
25
+ {
26
+ title: "1. Parties",
27
+ content: [
28
+ "<strong>APIClaw</strong> (operated by NordSym AB, org.nr 559535-5768), represented by Gustav Hemmingsson, CEO",
29
+ "<strong>CQT Invest</strong>, represented by Mohammed Alubeid (\"Molle\")"
30
+ ]
31
+ },
32
+ {
33
+ title: "2. Purpose",
34
+ content: [
35
+ "This Memorandum of Understanding establishes a framework for an advisory partnership between APIClaw/NordSym and CQT Invest, with the goal of:",
36
+ "• Leveraging CQT Invest's network and strategic expertise to accelerate APIClaw's growth",
37
+ "• Creating mutual value through introductions, advisory services, and business development",
38
+ "• Building a long-term, trust-based collaboration with aligned incentives"
39
+ ]
40
+ },
41
+ {
42
+ title: "3. CQT Invest Provides",
43
+ content: [
44
+ "• <strong>Strategic Advisory:</strong> Business strategy, market positioning, and growth guidance",
45
+ "• <strong>Network & Introductions:</strong> Access to relevant contacts, potential customers, partners, and investors",
46
+ "• <strong>Business Development Support:</strong> Assistance with deal structuring, negotiations, and market expansion"
47
+ ]
48
+ },
49
+ {
50
+ title: "4. APIClaw / NordSym Provides",
51
+ content: [
52
+ "• <strong>Product Access:</strong> Full access to APIClaw platform and services",
53
+ "• <strong>Revenue Share:</strong> Success fee on deals and customers referred by CQT Invest (terms to be agreed per deal)",
54
+ "• <strong>Collaboration:</strong> Open communication and joint exploration of opportunities"
55
+ ]
56
+ },
57
+ {
58
+ title: "5. Terms & Conditions",
59
+ content: [
60
+ "• <strong>Non-Exclusive:</strong> This partnership does not restrict either party from engaging with other partners or advisors",
61
+ "• <strong>Good Faith:</strong> Both parties commit to acting in good faith and maintaining open, honest communication",
62
+ "• <strong>Confidentiality:</strong> Business information shared between parties shall be treated as confidential",
63
+ "• <strong>Flexibility:</strong> Specific terms for individual deals or projects will be agreed upon as opportunities arise"
64
+ ]
65
+ },
66
+ {
67
+ title: "6. Duration",
68
+ content: [
69
+ "This MOU is effective from the date of signing and remains in effect until terminated by either party with 30 days written notice. Existing commitments and revenue share agreements shall survive termination."
70
+ ]
71
+ },
72
+ {
73
+ title: "7. Non-Binding Intent",
74
+ content: ["This MOU represents a statement of intent and mutual commitment. While it establishes the framework for collaboration, specific commercial terms for individual deals will be documented separately as they arise."]
75
+ }
76
+ ]
77
+ },
78
+ apilayer: {
79
+ partnerName: "APILayer",
80
+ partnerRepresentative: "Pratham Kumar",
81
+ sections: [
82
+ {
83
+ title: "1. Parties",
84
+ content: [
85
+ "<strong>APIClaw</strong> (operated by NordSym AB, org.nr 559535-5768), represented by Gustav Hemmingsson, CEO",
86
+ "<strong>APILayer</strong> (apilayer Data Products GmbH), represented by Pratham Kumar"
87
+ ]
88
+ },
89
+ {
90
+ title: "2. Purpose",
91
+ content: [
92
+ "This MOU establishes a framework for exploring a mutually beneficial partnership between APIClaw and APILayer, with the goal of:",
93
+ "• Putting APILayer's APIs in front of AI Agents",
94
+ "• Providing APILayer with featured provider status and attribution within APIClaw",
95
+ "• Exploring co-marketing opportunities that leverage both parties' strengths",
96
+ "• Enabling AI agents to discover and use APILayer APIs"
97
+ ]
98
+ },
99
+ {
100
+ title: "3. Proposed Collaboration",
101
+ phases: [
102
+ { name: "Phase 1: Discovery Integration", desc: "APIClaw indexes APILayer's catalog with AI-optimized metadata. APILayer receives featured provider status and appropriate attribution as mutually agreed." },
103
+ { name: "Phase 2: Direct Call Pilot", desc: "Pilot integration enabling AI agents to access select APILayer APIs directly through APIClaw. Both parties evaluate performance and user adoption." },
104
+ { name: "Phase 3: Scale & Co-Marketing", desc: "Based on pilot learnings, parties discuss expanded integration, co-marketing initiatives, and commercial terms that reflect the value created." }
105
+ ]
106
+ },
107
+ {
108
+ title: "4. Non-Binding Intent",
109
+ content: ["This MOU represents a statement of intent and is <strong>not legally binding</strong>. It serves as a foundation for further discussions and the potential development of a formal partnership agreement."]
110
+ },
111
+ {
112
+ title: "5. Confidentiality",
113
+ content: ["Both parties agree to treat any shared business information, technical details, and strategic discussions as confidential."]
114
+ },
115
+ {
116
+ title: "6. Next Steps",
117
+ content: [
118
+ "• Set up Telegram group for technical coordination",
119
+ "• Agree on pilot APIs and integration approach",
120
+ "• Launch pilot, iterate based on learnings, and refine approach together"
121
+ ]
122
+ }
123
+ ]
124
+ }
125
+ };
126
+
127
+ export default function MOUPage() {
128
+ const params = useParams();
129
+ const partnerId = params.partnerId as string;
130
+ const canvasRef = useRef<HTMLCanvasElement>(null);
131
+ const [isDrawing, setIsDrawing] = useState(false);
132
+ const [hasSignature, setHasSignature] = useState(false);
133
+ // Default values per partner
134
+ const partnerDefaults: Record<string, { name: string; title: string }> = {
135
+ cqtinvest: { name: "Mohammed Alubeid", title: "Partner" },
136
+ apilayer: { name: "", title: "" },
137
+ };
138
+ const defaults = partnerDefaults[partnerId] || { name: "", title: "" };
139
+
140
+ const [signerName, setSignerName] = useState(defaults.name);
141
+ const [signerTitle, setSignerTitle] = useState(defaults.title);
142
+ const [isSubmitting, setIsSubmitting] = useState(false);
143
+ const [isSubmitted, setIsSubmitted] = useState(false);
144
+ const [error, setError] = useState("");
145
+
146
+ const mou = mouContent[partnerId as keyof typeof mouContent];
147
+
148
+ useEffect(() => {
149
+ const canvas = canvasRef.current;
150
+ if (!canvas) return;
151
+ const ctx = canvas.getContext("2d");
152
+ if (!ctx) return;
153
+ ctx.fillStyle = "#ffffff";
154
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
155
+ ctx.strokeStyle = "#1a1a1a";
156
+ ctx.lineWidth = 2;
157
+ ctx.lineCap = "round";
158
+ ctx.lineJoin = "round";
159
+ }, []);
160
+
161
+ const startDrawing = (e: React.MouseEvent | React.TouchEvent) => {
162
+ setIsDrawing(true);
163
+ const canvas = canvasRef.current;
164
+ if (!canvas) return;
165
+ const ctx = canvas.getContext("2d");
166
+ if (!ctx) return;
167
+
168
+ const rect = canvas.getBoundingClientRect();
169
+ const x = "touches" in e ? e.touches[0].clientX - rect.left : e.clientX - rect.left;
170
+ const y = "touches" in e ? e.touches[0].clientY - rect.top : e.clientY - rect.top;
171
+
172
+ ctx.beginPath();
173
+ ctx.moveTo(x, y);
174
+ };
175
+
176
+ const draw = (e: React.MouseEvent | React.TouchEvent) => {
177
+ if (!isDrawing) return;
178
+ const canvas = canvasRef.current;
179
+ if (!canvas) return;
180
+ const ctx = canvas.getContext("2d");
181
+ if (!ctx) return;
182
+
183
+ const rect = canvas.getBoundingClientRect();
184
+ const x = "touches" in e ? e.touches[0].clientX - rect.left : e.clientX - rect.left;
185
+ const y = "touches" in e ? e.touches[0].clientY - rect.top : e.clientY - rect.top;
186
+
187
+ ctx.lineTo(x, y);
188
+ ctx.stroke();
189
+ setHasSignature(true);
190
+ };
191
+
192
+ const stopDrawing = () => {
193
+ setIsDrawing(false);
194
+ };
195
+
196
+ const clearSignature = () => {
197
+ const canvas = canvasRef.current;
198
+ if (!canvas) return;
199
+ const ctx = canvas.getContext("2d");
200
+ if (!ctx) return;
201
+ ctx.fillStyle = "#ffffff";
202
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
203
+ setHasSignature(false);
204
+ };
205
+
206
+ const handleSubmit = async () => {
207
+ if (!hasSignature || !signerName || !signerTitle) {
208
+ setError("Please provide your signature, name, and title.");
209
+ return;
210
+ }
211
+
212
+ setIsSubmitting(true);
213
+ setError("");
214
+
215
+ try {
216
+ const canvas = canvasRef.current;
217
+ if (!canvas) throw new Error("Canvas not found");
218
+
219
+ const signatureDataUrl = canvas.toDataURL("image/png");
220
+
221
+ // Save to Convex via API route
222
+ const response = await fetch("/api/mou/sign", {
223
+ method: "POST",
224
+ headers: { "Content-Type": "application/json" },
225
+ body: JSON.stringify({
226
+ partnerId,
227
+ signatureDataUrl,
228
+ signerName,
229
+ signerTitle,
230
+ }),
231
+ });
232
+
233
+ if (!response.ok) {
234
+ throw new Error("Failed to submit signature");
235
+ }
236
+
237
+ setIsSubmitted(true);
238
+ } catch (err) {
239
+ setError(err instanceof Error ? err.message : "Something went wrong");
240
+ } finally {
241
+ setIsSubmitting(false);
242
+ }
243
+ };
244
+
245
+ if (!mou) {
246
+ return (
247
+ <div className="min-h-screen bg-[#fafafa] flex items-center justify-center">
248
+ <div className="text-center">
249
+ <h1 className="text-2xl font-bold text-neutral-900">MOU Not Found</h1>
250
+ <p className="text-neutral-600 mt-2">Invalid partner ID</p>
251
+ </div>
252
+ </div>
253
+ );
254
+ }
255
+
256
+ if (isSubmitted) {
257
+ return (
258
+ <div className="min-h-screen bg-[#fafafa] flex items-center justify-center p-4">
259
+ <div className="bg-white rounded-2xl shadow-lg p-8 max-w-md text-center">
260
+ <div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
261
+ <svg className="w-8 h-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
262
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
263
+ </svg>
264
+ </div>
265
+ <h1 className="text-2xl font-bold text-neutral-900 mb-2">MOU Signed!</h1>
266
+ <p className="text-neutral-600 mb-4">Thank you, {signerName}. Your signature has been recorded.</p>
267
+ <p className="text-sm text-neutral-500">Gustav will be in touch shortly to schedule the next steps.</p>
268
+ </div>
269
+ </div>
270
+ );
271
+ }
272
+
273
+ return (
274
+ <div className="min-h-screen bg-[#fafafa]">
275
+ {/* Header */}
276
+ <header className="bg-white border-b border-neutral-200 py-8 px-4">
277
+ <div className="max-w-3xl mx-auto text-center">
278
+ <div className="flex items-center justify-center gap-3 mb-4">
279
+ <span className="text-4xl">🦞</span>
280
+ <div className="text-left">
281
+ <span className="text-2xl font-bold text-neutral-900">APIClaw × {mou.partnerName}</span>
282
+ <p className="text-sm text-neutral-500">The API Layer for AI Agents</p>
283
+ </div>
284
+ </div>
285
+ <div className="h-1 w-24 bg-gradient-to-r from-red-500 to-red-600 mx-auto mb-4 rounded-full"></div>
286
+ <h1 className="text-2xl font-bold text-neutral-800">Memorandum of Understanding</h1>
287
+ <p className="text-neutral-500 mt-1">Partnership Framework Agreement</p>
288
+ </div>
289
+ </header>
290
+
291
+ {/* Content */}
292
+ <main className="max-w-3xl mx-auto py-8 px-4">
293
+ <div className="bg-white rounded-2xl shadow-lg overflow-hidden">
294
+ <div className="p-8 space-y-8">
295
+ {mou.sections.map((section, idx) => (
296
+ <div key={idx}>
297
+ <h2 className="text-lg font-semibold text-red-600 border-b-2 border-red-100 pb-2 mb-4">
298
+ {section.title}
299
+ </h2>
300
+ {section.content && (
301
+ <div className="space-y-3 text-neutral-600">
302
+ {section.content.map((text, i) => (
303
+ <p key={i} dangerouslySetInnerHTML={{ __html: text }} />
304
+ ))}
305
+ </div>
306
+ )}
307
+ {section.phases && (
308
+ <div className="space-y-4">
309
+ {section.phases.map((phase, i) => (
310
+ <div key={i} className="bg-red-50 border-l-4 border-red-600 p-4 rounded-r-lg">
311
+ <h3 className="font-semibold text-red-800">{phase.name}</h3>
312
+ <p className="text-neutral-600 mt-1">{phase.desc}</p>
313
+ </div>
314
+ ))}
315
+ </div>
316
+ )}
317
+ </div>
318
+ ))}
319
+
320
+ {/* Signatures */}
321
+ <div className="border-t-2 border-neutral-200 pt-8 mt-8">
322
+ <h2 className="text-lg font-semibold text-red-600 mb-6">Signatures</h2>
323
+
324
+ <div className="grid md:grid-cols-2 gap-8">
325
+ {/* APIClaw signature (pre-signed) */}
326
+ <div>
327
+ <h3 className="text-xs uppercase tracking-wide text-neutral-500 mb-4">APIClaw / NordSym AB</h3>
328
+ <div className="border-b border-neutral-300 pb-2 mb-2 h-16 flex items-end">
329
+ <span className="font-['Brush_Script_MT',cursive] text-2xl text-neutral-800">Gustav Hemmingsson</span>
330
+ </div>
331
+ <div className="text-sm text-neutral-600">
332
+ <strong className="text-neutral-900 block">Gustav Hemmingsson</strong>
333
+ CEO, NordSym AB<br />
334
+ Date: March 5, 2026
335
+ </div>
336
+ </div>
337
+
338
+ {/* Partner signature (to be signed) */}
339
+ <div>
340
+ <h3 className="text-xs uppercase tracking-wide text-neutral-500 mb-4">{mou.partnerName}</h3>
341
+
342
+ <div className="space-y-4">
343
+ <div>
344
+ <label className="block text-sm text-neutral-600 mb-1">Draw your signature:</label>
345
+ <canvas
346
+ ref={canvasRef}
347
+ width={300}
348
+ height={100}
349
+ className="border border-neutral-300 rounded-lg cursor-crosshair touch-none bg-white"
350
+ onMouseDown={startDrawing}
351
+ onMouseMove={draw}
352
+ onMouseUp={stopDrawing}
353
+ onMouseLeave={stopDrawing}
354
+ onTouchStart={startDrawing}
355
+ onTouchMove={draw}
356
+ onTouchEnd={stopDrawing}
357
+ />
358
+ <button
359
+ onClick={clearSignature}
360
+ className="text-sm text-red-600 hover:text-red-700 mt-1"
361
+ >
362
+ Clear signature
363
+ </button>
364
+ </div>
365
+
366
+ <div>
367
+ <label className="block text-sm text-neutral-600 mb-1">Full Name</label>
368
+ <input
369
+ type="text"
370
+ value={signerName}
371
+ onChange={(e) => setSignerName(e.target.value)}
372
+ placeholder={mou.partnerRepresentative}
373
+ className="w-full border border-neutral-300 rounded-lg px-3 py-2 text-neutral-900 focus:border-red-500 focus:ring-1 focus:ring-red-500 outline-none"
374
+ />
375
+ </div>
376
+
377
+ <div>
378
+ <label className="block text-sm text-neutral-600 mb-1">Title</label>
379
+ <input
380
+ type="text"
381
+ value={signerTitle}
382
+ onChange={(e) => setSignerTitle(e.target.value)}
383
+ placeholder="e.g., Head of Developer Relations"
384
+ className="w-full border border-neutral-300 rounded-lg px-3 py-2 text-neutral-900 focus:border-red-500 focus:ring-1 focus:ring-red-500 outline-none"
385
+ />
386
+ </div>
387
+
388
+ <p className="text-xs text-neutral-500">
389
+ Date: {new Date().toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' })}
390
+ </p>
391
+ </div>
392
+ </div>
393
+ </div>
394
+
395
+ {error && (
396
+ <div className="mt-4 p-3 bg-red-50 border border-red-200 rounded-lg text-red-700 text-sm">
397
+ {error}
398
+ </div>
399
+ )}
400
+
401
+ <div className="mt-8 flex justify-center">
402
+ <button
403
+ onClick={handleSubmit}
404
+ disabled={isSubmitting || !hasSignature || !signerName || !signerTitle}
405
+ className="bg-gradient-to-r from-red-600 to-red-700 text-white px-8 py-3 rounded-lg font-semibold
406
+ hover:from-red-700 hover:to-red-800 disabled:opacity-50 disabled:cursor-not-allowed
407
+ transition-all shadow-lg hover:shadow-xl"
408
+ >
409
+ {isSubmitting ? "Submitting..." : "Sign MOU"}
410
+ </button>
411
+ </div>
412
+ </div>
413
+ </div>
414
+
415
+ {/* Footer */}
416
+ <div className="bg-neutral-50 border-t border-neutral-200 px-8 py-4 text-center text-sm text-neutral-500">
417
+ <p>🦞 APIClaw × {mou.partnerName} Partnership MOU • March 2026</p>
418
+ <p>Questions? Contact <a href="mailto:gustav@nordsym.com" className="text-red-600 hover:underline">gustav@nordsym.com</a></p>
419
+ </div>
420
+ </div>
421
+ </main>
422
+ </div>
423
+ );
424
+ }