Commitadf5e18unknown_key
Add developer program page + GitHub migration as primary CTA
Add developer program page + GitHub migration as primary CTA
- New route GET/POST /developer-program: hero ("Build on Gluecron. Earn
revenue."), three value-prop cards (Publish an agent / Revenue share /
Partner badge), 70% revenue-split highlight strip, partner application
form (logs to stdout; no new DB dependency), and docs link to /help#agents
- Register route in src/app.tsx; add "Developer Program" link in site footer
- Landing hero: add "Migrate from GitHub →" as a secondary CTA button
alongside Sign-up and Claude Desktop, with its own .landing-cta-migrate
style that avoids competing with the primary button
- Layout nav: add "Migrate from GitHub" pill link in the logged-out nav
(hidden on narrow viewports via media query)
- Explore page: add "Coming from GitHub?" callout card at the bottom,
linking to /import with compelling copy and a gradient hairline accent
- import-bulk hero: update headline to "Migrate your GitHub org in 60
seconds. All repos, issues, and history."
https://claude.ai/code/session_01DzJMTFASjMHt2f5ze4cNLR6 files changed+827−7adf5e18f30ccd811a9f2e542f5b4cac520574d0d
6 changed files+827−7
Modifiedsrc/app.tsx+4−0View fileUnifiedSplit
@@ -169,6 +169,7 @@ import { staleBranchRoutes } from "./routes/stale-branches";
169169import pulseRoutes from "./routes/pulse";
170170import healthScoreRoutes from "./routes/health-score";
171171import hotFilesRoutes from "./routes/hot-files";
172import developerProgramRoutes from "./routes/developer-program";
172173import { authRateLimit, gitRateLimit, searchRateLimit } from "./middleware/rate-limit";
173174import { csrfToken, csrfProtect } from "./middleware/csrf";
174175import { noCache } from "./middleware/no-cache";
@@ -531,6 +532,9 @@ app.route("/", changelogRoutes);
531532// /pricing, /features, /about — marketing surface
532533app.route("/", marketingRoutes);
533534
535// /developer-program — partner + marketplace revenue-share page
536app.route("/", developerProgramRoutes);
537
534538// SEO: robots.txt + sitemap.xml
535539app.route("/", seoRoutes);
536540
Addedsrc/routes/developer-program.tsx+688−0View fileUnifiedSplit
@@ -0,0 +1,688 @@
1/**
2 * Developer Program page — /developer-program
3 *
4 * "Build on Gluecron. Earn revenue."
5 * Three sections: Publish an agent · Revenue share · Partner badge.
6 * CTA: partner application form (logs for now; no DB write required).
7 *
8 * Pure server-rendered. Dark theme. No new dependencies.
9 * All CSS scoped under .devprog-* to avoid leaking into app views.
10 */
11
12import { Hono } from "hono";
13import { Layout } from "../views/layout";
14import { softAuth } from "../middleware/auth";
15import type { AuthEnv } from "../middleware/auth";
16
17const developerProgram = new Hono<AuthEnv>();
18
19developerProgram.use("*", softAuth);
20
21// ─── Page-scoped styles ───────────────────────────────────────────────────────
22const devprogCss = `
23 .devprog-wrap { max-width: 1060px; margin: 0 auto; padding: 0 24px 80px; }
24
25 /* ─── Hero ─── */
26 .devprog-hero {
27 position: relative;
28 margin: 4px 0 48px;
29 padding: 64px 48px 56px;
30 background: var(--bg-elevated);
31 border: 1px solid var(--border);
32 border-radius: 20px;
33 overflow: hidden;
34 text-align: center;
35 }
36 .devprog-hero::before {
37 content: '';
38 position: absolute;
39 top: 0; left: 0; right: 0;
40 height: 2px;
41 background: linear-gradient(90deg, transparent 0%, #8c6dff 30%, #36c5d6 70%, transparent 100%);
42 opacity: 0.85;
43 pointer-events: none;
44 }
45 .devprog-hero-bg {
46 position: absolute;
47 inset: -30% -10% auto auto;
48 width: 500px; height: 500px;
49 pointer-events: none;
50 z-index: 0;
51 }
52 .devprog-hero-orb {
53 position: absolute;
54 inset: 0;
55 background: radial-gradient(circle, rgba(140,109,255,0.24), rgba(54,197,214,0.10) 45%, transparent 70%);
56 filter: blur(90px);
57 opacity: 0.7;
58 animation: devprogOrbDrift 14s ease-in-out infinite;
59 }
60 .devprog-hero-orb-2 {
61 position: absolute;
62 inset: auto auto -20% -12%;
63 width: 340px; height: 340px;
64 background: radial-gradient(circle, rgba(54,197,214,0.18), rgba(140,109,255,0.06) 50%, transparent 75%);
65 filter: blur(70px);
66 opacity: 0.5;
67 pointer-events: none;
68 z-index: 0;
69 animation: devprogOrbDrift2 18s ease-in-out infinite;
70 }
71 @keyframes devprogOrbDrift {
72 0%, 100% { transform: scale(1) translate(0, 0); opacity: 0.6; }
73 50% { transform: scale(1.10) translate(-12px, 10px); opacity: 0.88; }
74 }
75 @keyframes devprogOrbDrift2 {
76 0%, 100% { transform: scale(1) translate(0, 0); opacity: 0.40; }
77 50% { transform: scale(1.08) translate(14px, -8px); opacity: 0.65; }
78 }
79 @media (prefers-reduced-motion: reduce) {
80 .devprog-hero-orb, .devprog-hero-orb-2 { animation: none; }
81 }
82 .devprog-hero-inner {
83 position: relative;
84 z-index: 1;
85 display: flex;
86 flex-direction: column;
87 align-items: center;
88 gap: 20px;
89 }
90 .devprog-hero-eyebrow {
91 display: inline-flex;
92 align-items: center;
93 gap: 8px;
94 font-family: var(--font-mono);
95 font-size: 11px;
96 font-weight: 600;
97 letter-spacing: 0.13em;
98 text-transform: uppercase;
99 color: var(--accent);
100 }
101 .devprog-hero-eyebrow-dot {
102 display: inline-block;
103 width: 6px; height: 6px;
104 border-radius: 50%;
105 background: var(--accent);
106 box-shadow: 0 0 8px rgba(140,109,255,0.7);
107 }
108 .devprog-hero-title {
109 font-family: var(--font-display);
110 font-size: clamp(36px, 5.5vw, 68px);
111 font-weight: 800;
112 letter-spacing: -0.036em;
113 line-height: 0.96;
114 color: var(--text-strong);
115 max-width: 780px;
116 margin: 0;
117 }
118 .devprog-hero-title .gradient-text {
119 background-image: linear-gradient(135deg, #a48bff 0%, #8c6dff 50%, #36c5d6 100%);
120 -webkit-background-clip: text;
121 background-clip: text;
122 -webkit-text-fill-color: transparent;
123 color: transparent;
124 }
125 .devprog-hero-sub {
126 font-size: 17px;
127 color: var(--text-muted);
128 line-height: 1.6;
129 max-width: 560px;
130 margin: 0;
131 }
132 .devprog-hero-ctas {
133 display: flex;
134 gap: 12px;
135 flex-wrap: wrap;
136 justify-content: center;
137 margin-top: 8px;
138 }
139
140 /* ─── Section cards (the three value props) ─── */
141 .devprog-sections {
142 display: grid;
143 grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
144 gap: 20px;
145 margin-bottom: 48px;
146 }
147 .devprog-card {
148 position: relative;
149 background: var(--bg-elevated);
150 border: 1px solid var(--border);
151 border-radius: 16px;
152 padding: 32px 28px;
153 display: flex;
154 flex-direction: column;
155 gap: 14px;
156 overflow: hidden;
157 transition: border-color 160ms ease, box-shadow 160ms ease, transform 160ms ease;
158 }
159 .devprog-card::before {
160 content: '';
161 position: absolute;
162 top: 0; left: 0; right: 0;
163 height: 2px;
164 background: linear-gradient(90deg, transparent 0%, #8c6dff 30%, #36c5d6 70%, transparent 100%);
165 opacity: 0;
166 transition: opacity 180ms ease;
167 pointer-events: none;
168 }
169 .devprog-card:hover {
170 border-color: rgba(140,109,255,0.40);
171 box-shadow: 0 14px 32px -16px rgba(0,0,0,0.55), 0 0 24px -8px rgba(140,109,255,0.18);
172 transform: translateY(-2px);
173 }
174 .devprog-card:hover::before { opacity: 0.85; }
175 .devprog-card-icon {
176 width: 44px; height: 44px;
177 border-radius: 12px;
178 background: var(--accent-gradient-soft, rgba(140,109,255,0.12));
179 border: 1px solid rgba(140,109,255,0.22);
180 display: flex;
181 align-items: center;
182 justify-content: center;
183 color: var(--accent);
184 flex-shrink: 0;
185 }
186 .devprog-card-eyebrow {
187 font-family: var(--font-mono);
188 font-size: 10.5px;
189 font-weight: 600;
190 letter-spacing: 0.11em;
191 text-transform: uppercase;
192 color: var(--accent);
193 }
194 .devprog-card-title {
195 font-family: var(--font-display);
196 font-size: 20px;
197 font-weight: 700;
198 letter-spacing: -0.018em;
199 color: var(--text-strong);
200 margin: 0;
201 line-height: 1.2;
202 }
203 .devprog-card-body {
204 font-size: 14.5px;
205 color: var(--text-muted);
206 line-height: 1.6;
207 margin: 0;
208 flex: 1;
209 }
210 .devprog-card-link {
211 display: inline-flex;
212 align-items: center;
213 gap: 5px;
214 font-size: 13.5px;
215 font-weight: 600;
216 color: var(--text-link);
217 text-decoration: none;
218 transition: color 120ms ease, gap 120ms ease;
219 margin-top: 4px;
220 }
221 .devprog-card-link:hover {
222 color: var(--accent-hover);
223 gap: 8px;
224 text-decoration: none;
225 }
226
227 /* ─── Revenue highlight strip ─── */
228 .devprog-revenue-strip {
229 display: flex;
230 gap: 0;
231 border: 1px solid var(--border);
232 border-radius: 14px;
233 overflow: hidden;
234 margin-bottom: 48px;
235 background: var(--bg-elevated);
236 }
237 .devprog-revenue-pct {
238 display: flex;
239 flex-direction: column;
240 align-items: center;
241 justify-content: center;
242 gap: 4px;
243 padding: 28px 36px;
244 flex: 0 0 auto;
245 border-right: 1px solid var(--border);
246 }
247 .devprog-revenue-num {
248 font-family: var(--font-display);
249 font-size: 52px;
250 font-weight: 800;
251 letter-spacing: -0.04em;
252 line-height: 1;
253 background-image: linear-gradient(135deg, #a48bff 0%, #36c5d6 100%);
254 -webkit-background-clip: text;
255 background-clip: text;
256 -webkit-text-fill-color: transparent;
257 color: transparent;
258 }
259 .devprog-revenue-label {
260 font-size: 12px;
261 color: var(--text-muted);
262 font-weight: 500;
263 text-align: center;
264 }
265 .devprog-revenue-body {
266 display: flex;
267 flex-direction: column;
268 justify-content: center;
269 padding: 28px 32px;
270 gap: 8px;
271 }
272 .devprog-revenue-title {
273 font-family: var(--font-display);
274 font-size: 18px;
275 font-weight: 700;
276 letter-spacing: -0.018em;
277 color: var(--text-strong);
278 margin: 0;
279 }
280 .devprog-revenue-desc {
281 font-size: 14.5px;
282 color: var(--text-muted);
283 line-height: 1.55;
284 margin: 0;
285 max-width: 520px;
286 }
287
288 /* ─── Application form card ─── */
289 .devprog-apply {
290 background: var(--bg-elevated);
291 border: 1px solid var(--border);
292 border-radius: 18px;
293 padding: 44px 48px;
294 position: relative;
295 overflow: hidden;
296 }
297 .devprog-apply::before {
298 content: '';
299 position: absolute;
300 top: 0; left: 0; right: 0;
301 height: 2px;
302 background: linear-gradient(90deg, transparent 0%, #8c6dff 30%, #36c5d6 70%, transparent 100%);
303 opacity: 0.75;
304 pointer-events: none;
305 }
306 .devprog-apply-inner { max-width: 640px; }
307 .devprog-apply-eyebrow {
308 font-family: var(--font-mono);
309 font-size: 11px;
310 font-weight: 600;
311 letter-spacing: 0.12em;
312 text-transform: uppercase;
313 color: var(--accent);
314 margin-bottom: 12px;
315 }
316 .devprog-apply-title {
317 font-family: var(--font-display);
318 font-size: clamp(24px, 3.5vw, 36px);
319 font-weight: 800;
320 letter-spacing: -0.028em;
321 line-height: 1.1;
322 color: var(--text-strong);
323 margin: 0 0 10px;
324 }
325 .devprog-apply-sub {
326 font-size: 15px;
327 color: var(--text-muted);
328 line-height: 1.6;
329 margin: 0 0 28px;
330 }
331 .devprog-form { display: flex; flex-direction: column; gap: 16px; }
332 .devprog-field { display: flex; flex-direction: column; gap: 6px; }
333 .devprog-label {
334 font-size: 13px;
335 font-weight: 600;
336 color: var(--text-strong);
337 letter-spacing: -0.008em;
338 }
339 .devprog-input,
340 .devprog-textarea {
341 background: var(--bg-secondary);
342 border: 1px solid var(--border);
343 border-radius: var(--r);
344 padding: 9px 12px;
345 font-family: var(--font-sans);
346 font-size: 14px;
347 color: var(--text);
348 transition: border-color 120ms ease, box-shadow 120ms ease;
349 width: 100%;
350 }
351 .devprog-input::placeholder,
352 .devprog-textarea::placeholder { color: var(--text-faint); }
353 .devprog-input:focus,
354 .devprog-textarea:focus {
355 outline: none;
356 border-color: var(--border-focus);
357 box-shadow: var(--ring);
358 }
359 .devprog-textarea { resize: vertical; min-height: 90px; }
360 .devprog-form-actions {
361 display: flex;
362 align-items: center;
363 gap: 16px;
364 margin-top: 8px;
365 flex-wrap: wrap;
366 }
367 .devprog-form-note {
368 font-size: 12.5px;
369 color: var(--text-faint);
370 line-height: 1.5;
371 }
372 .devprog-success {
373 display: flex;
374 align-items: flex-start;
375 gap: 14px;
376 background: rgba(52,211,153,0.06);
377 border: 1px solid rgba(52,211,153,0.28);
378 border-radius: 12px;
379 padding: 18px 20px;
380 font-size: 14.5px;
381 color: var(--text-strong);
382 line-height: 1.55;
383 }
384 .devprog-success-icon {
385 width: 24px; height: 24px;
386 border-radius: 50%;
387 background: rgba(52,211,153,0.15);
388 border: 1px solid rgba(52,211,153,0.35);
389 display: flex; align-items: center; justify-content: center;
390 font-size: 14px;
391 flex-shrink: 0;
392 color: #34d399;
393 }
394
395 /* ─── Docs link strip ─── */
396 .devprog-docs-strip {
397 display: flex;
398 align-items: center;
399 gap: 12px;
400 padding: 18px 24px;
401 background: var(--bg-elevated);
402 border: 1px solid var(--border);
403 border-radius: 12px;
404 margin-top: 28px;
405 font-size: 14px;
406 }
407 .devprog-docs-strip-icon { color: var(--accent); flex-shrink: 0; }
408 .devprog-docs-strip-text { flex: 1; color: var(--text-muted); }
409 .devprog-docs-strip-link {
410 font-weight: 600;
411 color: var(--text-link);
412 white-space: nowrap;
413 }
414
415 @media (max-width: 640px) {
416 .devprog-hero { padding: 40px 24px 36px; }
417 .devprog-apply { padding: 32px 24px; }
418 .devprog-revenue-strip { flex-direction: column; }
419 .devprog-revenue-pct { border-right: none; border-bottom: 1px solid var(--border); }
420 }
421`;
422
423// ─── Route: GET /developer-program ───────────────────────────────────────────
424developerProgram.get("/developer-program", (c) => {
425 const user = c.get("user");
426 const applied = c.req.query("applied") === "1";
427
428 return c.html(
429 <Layout
430 title="Developer Program"
431 user={user}
432 description="Build AI agents on Gluecron, list them in the marketplace, and keep 70% of every sale. Apply for the partner program today."
433 >
434 <style dangerouslySetInnerHTML={{ __html: devprogCss }} />
435 <div class="devprog-wrap">
436
437 {/* ── Hero ── */}
438 <section class="devprog-hero" aria-labelledby="devprog-hero-h">
439 <div class="devprog-hero-bg" aria-hidden="true">
440 <div class="devprog-hero-orb" />
441 <div class="devprog-hero-orb-2" />
442 </div>
443 <div class="devprog-hero-inner">
444 <div class="devprog-hero-eyebrow">
445 <span class="devprog-hero-eyebrow-dot" aria-hidden="true" />
446 Gluecron Developer Program
447 </div>
448 <h1 id="devprog-hero-h" class="devprog-hero-title">
449 Build on Gluecron.{" "}
450 <span class="gradient-text">Earn revenue.</span>
451 </h1>
452 <p class="devprog-hero-sub">
453 List your AI agent in the Gluecron marketplace. Set your price. Ship
454 to thousands of developer teams already using Gluecron for their daily
455 workflow.
456 </p>
457 <div class="devprog-hero-ctas">
458 <a href="#apply" class="btn btn-primary btn-xl">
459 Apply for partner status
460 <span aria-hidden="true">{" →"}</span>
461 </a>
462 <a href="/help#agents" class="btn btn-secondary btn-xl">
463 Read the docs
464 </a>
465 </div>
466 </div>
467 </section>
468
469 {/* ── Three sections ── */}
470 <div class="devprog-sections">
471 {/* 1 — Publish an agent */}
472 <div class="devprog-card">
473 <div class="devprog-card-icon" aria-hidden="true">
474 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
475 <path d="M12 2l2.39 5.95L20 9l-4.5 3.9L17 19l-5-3.2L7 19l1.5-6.1L4 9l5.61-1.05L12 2z" />
476 </svg>
477 </div>
478 <div class="devprog-card-eyebrow">Step 1</div>
479 <h2 class="devprog-card-title">Publish an agent</h2>
480 <p class="devprog-card-body">
481 List your AI agent in the Gluecron marketplace. Set your price.
482 Gluecron takes 30% — you keep 70%. Your agent is discoverable
483 by every developer on the platform the moment it's approved.
484 </p>
485 <a href="/marketplace/agents/new" class="devprog-card-link">
486 Publish your first agent
487 <span aria-hidden="true">→</span>
488 </a>
489 </div>
490
491 {/* 2 — Revenue share */}
492 <div class="devprog-card">
493 <div class="devprog-card-icon" aria-hidden="true">
494 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
495 <line x1="12" y1="1" x2="12" y2="23" />
496 <path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" />
497 </svg>
498 </div>
499 <div class="devprog-card-eyebrow">How you earn</div>
500 <h2 class="devprog-card-title">Revenue share</h2>
501 <p class="devprog-card-body">
502 The 30% platform cut is already in the schema. When your agent is
503 installed by a team, you earn automatically — no invoicing, no
504 chasing payments. Payouts go out monthly to your connected account.
505 </p>
506 <a href="/help#agents" class="devprog-card-link">
507 See payout docs
508 <span aria-hidden="true">→</span>
509 </a>
510 </div>
511
512 {/* 3 — Partner badge */}
513 <div class="devprog-card">
514 <div class="devprog-card-icon" aria-hidden="true">
515 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
516 <path d="M12 2.5l8 3.5v6c0 5-3.5 8.5-8 9.5-4.5-1-8-4.5-8-9.5v-6z" />
517 <path d="M9 12l2 2 4-4" />
518 </svg>
519 </div>
520 <div class="devprog-card-eyebrow">Verified status</div>
521 <h2 class="devprog-card-title">gluecron-partner badge</h2>
522 <p class="devprog-card-body">
523 Verified partners get a badge on their profile and priority
524 placement in the marketplace. The badge signals to buyers that
525 your agent has been reviewed by the Gluecron team and meets our
526 quality and security bar.
527 </p>
528 <a href="#apply" class="devprog-card-link">
529 Apply now
530 <span aria-hidden="true">→</span>
531 </a>
532 </div>
533 </div>
534
535 {/* ── Revenue highlight strip ── */}
536 <div class="devprog-revenue-strip" aria-label="Revenue split: you keep 70%">
537 <div class="devprog-revenue-pct">
538 <div class="devprog-revenue-num">70%</div>
539 <div class="devprog-revenue-label">you keep</div>
540 </div>
541 <div class="devprog-revenue-body">
542 <h2 class="devprog-revenue-title">The math is simple.</h2>
543 <p class="devprog-revenue-desc">
544 Gluecron takes 30% to cover infrastructure, payments, and
545 marketplace distribution. You keep 70% of every subscription or
546 one-time purchase — no hidden fees, no surprise deductions. The
547 split is encoded in the platform schema and applied automatically
548 at payout time.
549 </p>
550 </div>
551 </div>
552
553 {/* ── Application form ── */}
554 <section id="apply" class="devprog-apply" aria-labelledby="devprog-apply-h">
555 <div class="devprog-apply-inner">
556 <div class="devprog-apply-eyebrow">Partner application</div>
557 <h2 id="devprog-apply-h" class="devprog-apply-title">
558 Apply for partner status
559 </h2>
560 <p class="devprog-apply-sub">
561 Tell us about the agent you're building. We review every
562 application within 5 business days and reply to the email on your
563 Gluecron account.
564 </p>
565
566 {applied ? (
567 <div class="devprog-success" role="alert">
568 <div class="devprog-success-icon" aria-hidden="true">✓</div>
569 <div>
570 <strong>Application received.</strong> We'll review your
571 submission and reply within 5 business days. Keep building!
572 </div>
573 </div>
574 ) : (
575 <form
576 method="post"
577 action="/developer-program"
578 class="devprog-form"
579 >
580 <div class="devprog-field">
581 <label class="devprog-label" for="dp-agent-name">
582 Agent name
583 </label>
584 <input
585 id="dp-agent-name"
586 name="agent_name"
587 type="text"
588 class="devprog-input"
589 placeholder="e.g. PR Summarizer Pro"
590 required
591 maxlength={120}
592 />
593 </div>
594 <div class="devprog-field">
595 <label class="devprog-label" for="dp-agent-desc">
596 What does it do?
597 </label>
598 <textarea
599 id="dp-agent-desc"
600 name="agent_description"
601 class="devprog-textarea"
602 placeholder="Describe your agent in 2–4 sentences. What problem does it solve? Who is it for?"
603 required
604 maxlength={800}
605 />
606 </div>
607 <div class="devprog-field">
608 <label class="devprog-label" for="dp-pricing">
609 Pricing model
610 </label>
611 <input
612 id="dp-pricing"
613 name="pricing_model"
614 type="text"
615 class="devprog-input"
616 placeholder='e.g. "$9/mo per seat" or "one-time $49"'
617 maxlength={120}
618 />
619 </div>
620 <div class="devprog-field">
621 <label class="devprog-label" for="dp-repo">
622 Gluecron repo or demo URL
623 </label>
624 <input
625 id="dp-repo"
626 name="repo_url"
627 type="url"
628 class="devprog-input"
629 placeholder="https://gluecron.com/you/your-agent"
630 />
631 </div>
632 <div class="devprog-form-actions">
633 <button type="submit" class="btn btn-primary">
634 Submit application
635 </button>
636 <p class="devprog-form-note">
637 We'll reply to the email on your Gluecron account.
638 Applications are reviewed manually — no bots.
639 </p>
640 </div>
641 </form>
642 )}
643
644 {/* Docs link */}
645 <div class="devprog-docs-strip" role="complementary">
646 <svg class="devprog-docs-strip-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
647 <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
648 <polyline points="14 2 14 8 20 8" />
649 <line x1="16" y1="13" x2="8" y2="13" />
650 <line x1="16" y1="17" x2="8" y2="17" />
651 <polyline points="10 9 9 9 8 9" />
652 </svg>
653 <span class="devprog-docs-strip-text">
654 Want to start building before applying?
655 </span>
656 <a href="/help#agents" class="devprog-docs-strip-link">
657 Read the agent publishing docs →
658 </a>
659 </div>
660 </div>
661 </section>
662
663 </div>
664 </Layout>
665 );
666});
667
668// ─── Route: POST /developer-program ──────────────────────────────────────────
669// Logs the application for now. Future: insert into partner_applications table.
670developerProgram.post("/developer-program", async (c) => {
671 const user = c.get("user");
672 const body = await c.req.parseBody();
673
674 // Log the application so it's visible in server output / observability
675 console.log("[developer-program] partner application received", {
676 userId: user?.id ?? null,
677 username: user?.username ?? null,
678 agentName: body["agent_name"],
679 pricingModel: body["pricing_model"],
680 repoUrl: body["repo_url"],
681 // description intentionally omitted from log to keep it brief
682 at: new Date().toISOString(),
683 });
684
685 return c.redirect("/developer-program?applied=1");
686});
687
688export default developerProgram;
Modifiedsrc/routes/explore.tsx+70−0View fileUnifiedSplit
@@ -605,7 +605,56 @@ const ExploreStyle = () => (
605605 .explore-foot { flex-direction: column; align-items: stretch; padding: 16px 18px; }
606606 .explore-foot-actions { width: 100%; }
607607 .explore-foot-actions .btn { flex: 1; min-width: 0; }
608 .explore-gh-callout { flex-direction: column; gap: 12px; }
609 .explore-gh-callout-cta { width: 100%; justify-content: center; }
608610 }
611
612 /* "Coming from GitHub?" callout — bottom-of-page migration prompt */
613 .explore-gh-callout {
614 display: flex;
615 align-items: center;
616 gap: 16px;
617 margin-top: 32px;
618 padding: 20px 24px;
619 background: var(--bg-elevated);
620 border: 1px solid var(--border);
621 border-radius: 14px;
622 position: relative;
623 overflow: hidden;
624 }
625 .explore-gh-callout::before {
626 content: '';
627 position: absolute;
628 top: 0; left: 0; right: 0;
629 height: 2px;
630 background: linear-gradient(90deg, transparent 0%, #8c6dff 30%, #36c5d6 70%, transparent 100%);
631 opacity: 0.7;
632 pointer-events: none;
633 }
634 .explore-gh-callout-icon {
635 width: 40px; height: 40px;
636 border-radius: 10px;
637 background: rgba(140,109,255,0.10);
638 border: 1px solid rgba(140,109,255,0.20);
639 display: flex; align-items: center; justify-content: center;
640 color: var(--accent);
641 flex-shrink: 0;
642 }
643 .explore-gh-callout-body { flex: 1; min-width: 0; }
644 .explore-gh-callout-title {
645 font-size: 15px;
646 font-weight: 700;
647 color: var(--text-strong);
648 margin: 0 0 3px;
649 letter-spacing: -0.012em;
650 }
651 .explore-gh-callout-sub {
652 font-size: 13.5px;
653 color: var(--text-muted);
654 margin: 0;
655 line-height: 1.5;
656 }
657 .explore-gh-callout-cta { flex-shrink: 0; white-space: nowrap; }
609658 `,
610659 }}
611660 />
@@ -1170,6 +1219,27 @@ explore.get("/explore", async (c) => {
11701219 )}
11711220 </>
11721221 )}
1222 {/* "Coming from GitHub?" callout — shown to all visitors since
1223 Explore is the discovery surface most likely visited by evaluators
1224 comparing Gluecron to GitHub. Non-intrusive card at the bottom. */}
1225 <div class="explore-gh-callout" aria-label="Coming from GitHub?">
1226 <div class="explore-gh-callout-icon" aria-hidden="true">
1227 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
1228 <polyline points="4 17 10 11 4 5" />
1229 <line x1="12" y1="19" x2="20" y2="19" />
1230 </svg>
1231 </div>
1232 <div class="explore-gh-callout-body">
1233 <p class="explore-gh-callout-title">Coming from GitHub?</p>
1234 <p class="explore-gh-callout-sub">
1235 Migrate your repos, issues, and full history in 60 seconds — no
1236 manual setup, no SaaS migration project required.
1237 </p>
1238 </div>
1239 <a href="/import" class="btn btn-primary explore-gh-callout-cta">
1240 Migrate from GitHub →
1241 </a>
1242 </div>
11731243 </div>
11741244 </Layout>
11751245 );
Modifiedsrc/routes/import-bulk.tsx+7−6View fileUnifiedSplit
@@ -567,16 +567,17 @@ importBulkRoutes.get("/import/bulk", requireAuth, async (c) => {
567567 <div class="import-bulk-hero-inner">
568568 <div class="import-bulk-hero-eyebrow">
569569 <span class="import-bulk-hero-eyebrow-dot" aria-hidden="true" />
570 Bulk migration
570 GitHub migration
571571 </div>
572572 <h1 class="import-bulk-hero-title">
573 Import{" "}
574 <span class="gradient-text">many at once</span>.
573 Migrate your GitHub org{" "}
574 <span class="gradient-text">in 60 seconds</span>.
575575 </h1>
576576 <p class="import-bulk-hero-sub">
577 Paste a GitHub org + personal access token. Gluecron clones every
578 repo into your namespace as a mirror — sequentially, with per-repo
579 status so one failure can't abort the batch.
577 All repos, issues, and history — paste a GitHub org + personal access
578 token and Gluecron imports everything into your namespace
579 sequentially, with per-repo status so one failure can't abort the
580 batch.
580581 </p>
581582 </div>
582583 </div>
Modifiedsrc/views/landing.tsx+39−1View fileUnifiedSplit
@@ -154,12 +154,23 @@ export const LandingHero: FC<LandingPageProps> = ({
154154 Spec to PR in 90 seconds. Push to live in 25. AI review, auto-merge, deploy — automatic.
155155 </p>
156156
157 {/* U1 — primary CTA row, demoted to 2 buttons. */}
157 {/* U1 — primary CTA row. "Migrate from GitHub" added as a
158 secondary CTA alongside sign-up to capture visitors who
159 already have GitHub repos and want a one-click move. */}
158160 <div class="landing-hero-ctas" data-testid="hero-primary-ctas">
159161 <a href="/register" class="btn btn-primary btn-xl landing-cta-primary">
160162 Sign up free
161163 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
162164 </a>
165 {/* Migrate from GitHub — prominent secondary CTA */}
166 <a
167 href="/import"
168 class="btn btn-xl landing-cta-migrate"
169 data-testid="cta-migrate"
170 >
171 Migrate from GitHub
172 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
173 </a>
163174 {/* BLOCK Q1 — one-click Claude Desktop install. */}
164175 <a
165176 href="/gluecron.dxt"
@@ -2155,6 +2166,33 @@ const landingCss = `
21552166 }
21562167 }
21572168
2169 /* "Migrate from GitHub" CTA — secondary, but strong enough to stand
2170 alongside the primary. Uses a subtle amber/violet mix so it reads as
2171 action-oriented without competing with the green primary CTA. */
2172 .landing-cta-migrate {
2173 position: relative;
2174 background: var(--bg-elevated);
2175 color: var(--text-strong);
2176 border: 1px solid var(--border-strong);
2177 transition: border-color var(--t-base, 180ms) var(--ease, ease),
2178 transform var(--t-base, 180ms) var(--ease-spring, ease),
2179 box-shadow var(--t-base, 180ms) var(--ease, ease);
2180 }
2181 .landing-cta-migrate:hover {
2182 border-color: rgba(140,109,255,0.55);
2183 transform: translateY(-2px);
2184 box-shadow: 0 8px 22px -8px rgba(140,109,255,0.30);
2185 text-decoration: none;
2186 color: var(--text-strong);
2187 }
2188 (prefers-reduced-motion: reduce) {
2189 .landing-cta-migrate,
2190 .landing-cta-migrate:hover {
2191 transform: none;
2192 transition: none;
2193 }
2194 }
2195
21582196 /* L8 — free-tier reassurance link beneath the CTA row.
21592197 U1 — rhythm snapped to var(--space-6). */
21602198 .landing-hero-freenote {
Modifiedsrc/views/layout.tsx+19−0View fileUnifiedSplit
@@ -298,6 +298,9 @@ export const Layout: FC<
298298 <span class="theme-icon-dark">{"☾"}</span>
299299 <span class="theme-icon-light">{"☀"}</span>
300300 </a>
301 <a href="/import" class="nav-link nav-migrate" title="Migrate your GitHub repos to Gluecron">
302 Migrate from GitHub
303 </a>
301304 <a href="/login" class="nav-link">Sign in</a>
302305 <a href="/register" class="btn btn-sm btn-primary">Register</a>
303306 </>
@@ -332,6 +335,7 @@ export const Layout: FC<
332335 <a href="/changelog">Changelog</a>
333336 <a href="/explore">Explore</a>
334337 <a href="/marketplace">Marketplace</a>
338 <a href="/developer-program">Developer Program</a>
335339 </div>
336340 <div class="footer-col">
337341 <div class="footer-col-title">Platform</div>
@@ -1620,6 +1624,21 @@ const css = `
16201624 background: var(--accent-gradient);
16211625 border-radius: 2px;
16221626 }
1627 /* "Migrate from GitHub" nav link — logged-out only, slightly accented
1628 so it reads as an action affordance rather than a passive link. */
1629 .nav-migrate {
1630 color: var(--accent);
1631 font-weight: 600;
1632 border: 1px solid rgba(140,109,255,0.22);
1633 background: rgba(140,109,255,0.07);
1634 }
1635 .nav-migrate:hover {
1636 color: var(--accent-hover);
1637 background: rgba(140,109,255,0.13);
1638 border-color: rgba(140,109,255,0.40);
1639 }
1640 @media (max-width: 780px) { .nav-migrate { display: none; } }
1641
16231642 .nav-user {
16241643 color: var(--text-strong);
16251644 font-weight: 600;
16261645