Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesPull RequestsActionsSecurityInsightsSettings
✨ AI
More
Blame · Line-by-line history

landing.tsx

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

landing.tsxBlame479 lines · 1 contributor
2b821b7Claude1/**
2 * Marketing landing page for logged-out visitors.
3 *
4 * Pure presentational component — no props required. Drops into an existing
5 * <Layout user={null}> as a single fragment. All styles are scoped under the
6 * `landing-` class prefix so they don't leak into the rest of the app.
7 *
8 * Tone: confident, technical, specific. No "revolutionary", no "game-changing".
9 */
10
11import type { FC } from "hono/jsx";
12
13export interface LandingPageProps {
14 stats?: {
15 publicRepos?: number;
16 users?: number;
17 };
18}
19
8e9f1d9Claude20export const LandingPage: FC<LandingPageProps> = ({ stats } = {}) => {
21 const hasStats =
22 stats &&
23 ((stats.publicRepos !== undefined && stats.publicRepos > 0) ||
24 (stats.users !== undefined && stats.users > 0));
2b821b7Claude25 return (
26 <>
27 <style>{landingCss}</style>
28
29 {/* ---------- Hero ---------- */}
30 <section class="landing-hero">
31 <h1 class="landing-hero-title">
32 GitHub, but the AI actually ships the code.
33 </h1>
34 <p class="landing-hero-sub">
35 gluecron is a self-hostable code platform with AI review, dependency
36 updates, semantic search, and a workflow runner built in. No plugins.
37 No bolt-ons. One binary.
38 </p>
39 <div class="landing-hero-ctas">
40 <a href="/register" class="btn btn-primary landing-cta-primary">
41 Start free
42 </a>
43 <a href="/explore" class="btn landing-cta-secondary">
44 Explore public repos
45 </a>
46 </div>
47 <p class="landing-trust">
48 Self-hostable &middot; AI built in &middot; Open source mindset
49 </p>
8e9f1d9Claude50 {hasStats && (
51 <p class="landing-stats">
52 {stats!.publicRepos !== undefined && stats!.publicRepos > 0 && (
53 <span>
54 <strong>{stats!.publicRepos.toLocaleString()}</strong> public
55 {stats!.publicRepos === 1 ? " repo" : " repos"}
56 </span>
57 )}
58 {stats!.publicRepos !== undefined &&
59 stats!.publicRepos > 0 &&
60 stats!.users !== undefined &&
61 stats!.users > 0 && <span class="landing-stats-sep"> &middot; </span>}
62 {stats!.users !== undefined && stats!.users > 0 && (
63 <span>
64 <strong>{stats!.users.toLocaleString()}</strong>
65 {stats!.users === 1 ? " developer" : " developers"}
66 </span>
67 )}
68 </p>
69 )}
2b821b7Claude70 </section>
71
72 {/* ---------- Features grid ---------- */}
73 <section class="landing-section">
74 <h2 class="landing-section-title">Everything in one binary</h2>
75 <p class="landing-section-sub">
76 The features GitHub sells as separate products (Dependabot, Copilot,
77 Actions, Advanced Security) ship with gluecron by default.
78 </p>
79 <div class="landing-grid">
80 <FeatureCard
81 icon="\u2728"
82 title="AI code review"
83 desc="Claude Sonnet reviews every PR with inline comments. Can block merges when configured in branch protection."
84 />
85 <FeatureCard
86 icon="\u21BB"
87 title="AI dependency updates"
88 desc="Scans package.json, opens PRs with bump tables, writes the branch via git plumbing. Dependabot without the setup."
89 />
90 <FeatureCard
91 icon="\u2315"
92 title="Semantic code search"
93 desc="voyage-code-3 embeddings over every chunk, with lexical fallback. Finds code by intent, not just text."
94 />
95 <FeatureCard
96 icon="\u{1F4D6}"
97 title="Explain this codebase"
98 desc="One click gets you a per-commit cached Markdown tour of the repo. Onboarding that writes itself."
99 />
100 <FeatureCard
101 icon="\u2713"
102 title="Signed commit verification"
103 desc="GPG and SSH signatures, Issuer Fingerprint extraction, a green Verified badge on the commit list."
104 />
105 <FeatureCard
106 icon="\u21C4"
107 title="Merge queues + required checks"
108 desc="Serialised merges with re-test against the latest base. Per-branch required check matrix, enforced at merge."
109 />
110 <FeatureCard
111 icon="\u2699"
112 title="Self-hosted workflow runner"
113 desc="Actions-equivalent runner reads .gluecron/workflows/*.yml. Bun subprocesses, size-capped logs, per-step timeouts."
114 />
115 <FeatureCard
116 icon="\u{1F4E6}"
117 title="npm-protocol package registry"
118 desc="Publish, install, yank over the real npm protocol. PAT-auth via .npmrc. No separate service to run."
119 />
120 <FeatureCard
121 icon="\u{1F510}"
122 title="Enterprise SSO (OIDC)"
123 desc="Okta, Azure AD, Auth0, Google Workspace. Auth-code flow, state+nonce, optional email-domain allow-list."
124 />
125 </div>
126 </section>
127
128 {/* ---------- vs GitHub ---------- */}
129 <section class="landing-section landing-compare">
130 <h2 class="landing-section-title">gluecron vs GitHub</h2>
131 <p class="landing-section-sub">
132 Honest comparison. GitHub is excellent at what it does. gluecron is
133 built for teams that want AI and CI in one place, on infrastructure
134 they control.
135 </p>
136 <div class="landing-compare-grid">
137 <div class="landing-compare-col landing-compare-us">
138 <h3>gluecron</h3>
139 <ul>
140 <li><span class="landing-check">{"\u2713"}</span> Self-host on your own box</li>
141 <li><span class="landing-check">{"\u2713"}</span> AI review and completion included</li>
142 <li><span class="landing-check">{"\u2713"}</span> Dependency updater included</li>
143 <li><span class="landing-check">{"\u2713"}</span> Workflow runner included</li>
144 <li><span class="landing-check">{"\u2713"}</span> Semantic search included</li>
145 <li><span class="landing-check">{"\u2713"}</span> Green-by-default (gates, protection, codeowners)</li>
146 <li><span class="landing-check">{"\u2713"}</span> One binary. One database. One deploy.</li>
147 </ul>
148 </div>
149 <div class="landing-compare-col landing-compare-them">
150 <h3>GitHub</h3>
151 <ul>
152 <li><span class="landing-dash">{"\u2013"}</span> SaaS-first (Enterprise Server is separate)</li>
153 <li><span class="landing-dash">{"\u2013"}</span> Copilot billed per seat</li>
154 <li><span class="landing-dash">{"\u2013"}</span> Dependabot configured per repo</li>
155 <li><span class="landing-dash">{"\u2013"}</span> Actions minutes metered</li>
156 <li><span class="landing-dash">{"\u2013"}</span> Code search is lexical by default</li>
157 <li><span class="landing-dash">{"\u2013"}</span> Advanced Security is an add-on</li>
158 <li><span class="landing-dash">{"\u2013"}</span> Many moving pieces to wire together</li>
159 </ul>
160 </div>
161 </div>
162 </section>
163
164 {/* ---------- How it works ---------- */}
165 <section class="landing-section">
166 <h2 class="landing-section-title">How it works</h2>
167 <div class="landing-steps">
168 <div class="landing-step">
169 <div class="landing-step-num">1</div>
170 <h3>Push code</h3>
171 <p>
172 <code>git push</code> to gluecron. Standard Smart HTTP. No agent
173 to install.
174 </p>
175 </div>
176 <div class="landing-step">
177 <div class="landing-step-num">2</div>
178 <h3>Gates + AI review run</h3>
179 <p>
180 Secret scanner, security gate, AI reviewer, and your workflows
181 fire automatically. Rulesets enforce push policy.
182 </p>
183 </div>
184 <div class="landing-step">
185 <div class="landing-step-num">3</div>
186 <h3>Green pushes auto-deploy</h3>
187 <p>
188 Default-branch commits that pass all gates deploy through
189 Crontech. Failed deploys open an AI-authored incident issue.
190 </p>
191 </div>
192 </div>
193 </section>
194
195 {/* ---------- Final CTA band ---------- */}
196 <section class="landing-cta-band">
197 <h2>Ready to push?</h2>
198 <p>
199 Create an account, push a repo, watch the gates run. No credit card,
200 no trial clock.
201 </p>
202 <div class="landing-hero-ctas">
203 <a href="/register" class="btn btn-primary landing-cta-primary">
204 Start free
205 </a>
206 <a href="/explore" class="btn landing-cta-secondary">
207 Browse public repos
208 </a>
209 </div>
210 </section>
211
212 {/* ---------- Footer row ---------- */}
213 <section class="landing-foot">
214 <a href="/explore">Explore</a>
215 <span class="landing-foot-sep">&middot;</span>
216 <a href="/marketplace">Marketplace</a>
217 <span class="landing-foot-sep">&middot;</span>
218 <a href="/api/graphql">GraphQL API</a>
219 <span class="landing-foot-sep">&middot;</span>
220 <a href="/shortcuts">Keyboard shortcuts</a>
221 <span class="landing-foot-sep">&middot;</span>
222 <a href="/terms">Terms</a>
223 </section>
224 </>
225 );
226};
227
228const FeatureCard: FC<{ icon: string; title: string; desc: string }> = ({
229 icon,
230 title,
231 desc,
232}) => (
233 <div class="landing-feature">
234 <div class="landing-feature-icon" aria-hidden="true">
235 {icon}
236 </div>
237 <h3 class="landing-feature-title">{title}</h3>
238 <p class="landing-feature-desc">{desc}</p>
239 </div>
240);
241
242const landingCss = `
243 /* ---------- Hero ---------- */
244 .landing-hero {
245 padding: 72px 16px 56px;
246 text-align: center;
247 max-width: 860px;
248 margin: 0 auto;
249 border-bottom: 1px solid var(--border);
250 }
251 .landing-hero-title {
252 font-size: 44px;
253 line-height: 1.1;
254 letter-spacing: -0.02em;
255 margin: 0 0 20px;
256 color: var(--text);
257 font-weight: 700;
258 }
259 .landing-hero-sub {
260 font-size: 18px;
261 color: var(--text-muted);
262 margin: 0 auto 32px;
263 max-width: 640px;
264 line-height: 1.5;
265 }
266 .landing-hero-ctas {
267 display: flex;
268 gap: 12px;
269 justify-content: center;
270 flex-wrap: wrap;
271 margin-bottom: 16px;
272 }
273 .landing-cta-primary,
274 .landing-cta-secondary {
275 padding: 10px 20px;
276 font-size: 15px;
277 font-weight: 600;
278 }
279 .landing-trust {
280 font-size: 13px;
281 color: var(--text-muted);
282 margin-top: 12px;
283 letter-spacing: 0.02em;
284 }
8e9f1d9Claude285 .landing-stats {
286 margin-top: 20px;
287 font-size: 14px;
288 color: var(--text-muted);
289 }
290 .landing-stats strong {
291 color: var(--fg);
292 font-weight: 600;
293 }
294 .landing-stats-sep {
295 opacity: 0.6;
296 }
2b821b7Claude297
298 /* ---------- Section scaffolding ---------- */
299 .landing-section {
300 padding: 64px 16px;
301 max-width: 1080px;
302 margin: 0 auto;
303 border-bottom: 1px solid var(--border);
304 }
305 .landing-section-title {
306 font-size: 28px;
307 font-weight: 700;
308 margin: 0 0 12px;
309 color: var(--text);
310 letter-spacing: -0.01em;
311 }
312 .landing-section-sub {
313 font-size: 15px;
314 color: var(--text-muted);
315 margin: 0 0 36px;
316 max-width: 680px;
317 line-height: 1.6;
318 }
319
320 /* ---------- Features grid ---------- */
321 .landing-grid {
322 display: grid;
323 grid-template-columns: repeat(3, 1fr);
324 gap: 16px;
325 }
326 .landing-feature {
327 border: 1px solid var(--border);
328 border-radius: var(--radius);
329 padding: 20px;
330 background: var(--bg-secondary);
331 transition: border-color 0.15s;
332 }
333 .landing-feature:hover { border-color: var(--text-muted); }
334 .landing-feature-icon {
335 font-size: 22px;
336 margin-bottom: 10px;
337 line-height: 1;
338 }
339 .landing-feature-title {
340 font-size: 15px;
341 font-weight: 600;
342 margin: 0 0 6px;
343 color: var(--text);
344 }
345 .landing-feature-desc {
346 font-size: 13px;
347 color: var(--text-muted);
348 line-height: 1.55;
349 margin: 0;
350 }
351
352 /* ---------- Compare strip ---------- */
353 .landing-compare-grid {
354 display: grid;
355 grid-template-columns: 1fr 1fr;
356 gap: 16px;
357 }
358 .landing-compare-col {
359 border: 1px solid var(--border);
360 border-radius: var(--radius);
361 padding: 24px;
362 background: var(--bg-secondary);
363 }
364 .landing-compare-us { border-color: var(--accent); }
365 .landing-compare-col h3 {
366 font-size: 18px;
367 font-weight: 700;
368 margin: 0 0 16px;
369 color: var(--text);
370 }
371 .landing-compare-col ul {
372 list-style: none;
373 margin: 0;
374 padding: 0;
375 }
376 .landing-compare-col li {
377 font-size: 14px;
378 color: var(--text);
379 padding: 6px 0;
380 line-height: 1.5;
381 }
382 .landing-check { color: var(--green); font-weight: 700; margin-right: 8px; }
383 .landing-dash { color: var(--text-muted); font-weight: 700; margin-right: 8px; }
384 .landing-compare-them li { color: var(--text-muted); }
385
386 /* ---------- How it works ---------- */
387 .landing-steps {
388 display: grid;
389 grid-template-columns: repeat(3, 1fr);
390 gap: 16px;
391 }
392 .landing-step {
393 border: 1px solid var(--border);
394 border-radius: var(--radius);
395 padding: 24px;
396 background: var(--bg-secondary);
397 }
398 .landing-step-num {
399 display: inline-flex;
400 align-items: center;
401 justify-content: center;
402 width: 28px;
403 height: 28px;
404 border-radius: 50%;
405 background: var(--accent);
406 color: #fff;
407 font-weight: 700;
408 font-size: 14px;
409 margin-bottom: 12px;
410 }
411 .landing-step h3 {
412 font-size: 16px;
413 font-weight: 600;
414 margin: 0 0 6px;
415 color: var(--text);
416 }
417 .landing-step p {
418 font-size: 13px;
419 color: var(--text-muted);
420 line-height: 1.55;
421 margin: 0;
422 }
423 .landing-step code {
424 font-family: var(--font-mono);
425 font-size: 12px;
426 background: var(--bg-tertiary);
427 padding: 1px 6px;
428 border-radius: 3px;
429 color: var(--text);
430 }
431
432 /* ---------- Final CTA band ---------- */
433 .landing-cta-band {
434 padding: 72px 16px;
435 text-align: center;
436 max-width: 860px;
437 margin: 0 auto;
438 border-bottom: 1px solid var(--border);
439 }
440 .landing-cta-band h2 {
441 font-size: 32px;
442 font-weight: 700;
443 margin: 0 0 12px;
444 color: var(--text);
445 }
446 .landing-cta-band p {
447 font-size: 15px;
448 color: var(--text-muted);
449 margin: 0 auto 28px;
450 max-width: 560px;
451 }
452
453 /* ---------- Foot row ---------- */
454 .landing-foot {
455 padding: 32px 16px 48px;
456 text-align: center;
457 font-size: 13px;
458 color: var(--text-muted);
459 }
460 .landing-foot a { color: var(--text-muted); }
461 .landing-foot a:hover { color: var(--text); text-decoration: none; }
462 .landing-foot-sep { margin: 0 10px; color: var(--border); }
463
464 /* ---------- Responsive ---------- */
465 @media (max-width: 820px) {
466 .landing-hero { padding: 48px 16px 40px; }
467 .landing-hero-title { font-size: 32px; }
468 .landing-hero-sub { font-size: 16px; }
469 .landing-section { padding: 48px 16px; }
470 .landing-section-title { font-size: 24px; }
471 .landing-grid { grid-template-columns: 1fr; }
472 .landing-compare-grid { grid-template-columns: 1fr; }
473 .landing-steps { grid-template-columns: 1fr; }
474 .landing-cta-band { padding: 48px 16px; }
475 .landing-cta-band h2 { font-size: 24px; }
476 }
477`;
478
479export default LandingPage;