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.tsxBlame483 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>
2316be6Claude222 <a href="/status">Status</a>
223 <span class="landing-foot-sep">&middot;</span>
224 <a href="/demo">Try the demo</a>
225 <span class="landing-foot-sep">&middot;</span>
2b821b7Claude226 <a href="/terms">Terms</a>
227 </section>
228 </>
229 );
230};
231
232const FeatureCard: FC<{ icon: string; title: string; desc: string }> = ({
233 icon,
234 title,
235 desc,
236}) => (
237 <div class="landing-feature">
238 <div class="landing-feature-icon" aria-hidden="true">
239 {icon}
240 </div>
241 <h3 class="landing-feature-title">{title}</h3>
242 <p class="landing-feature-desc">{desc}</p>
243 </div>
244);
245
246const landingCss = `
247 /* ---------- Hero ---------- */
248 .landing-hero {
249 padding: 72px 16px 56px;
250 text-align: center;
251 max-width: 860px;
252 margin: 0 auto;
253 border-bottom: 1px solid var(--border);
254 }
255 .landing-hero-title {
256 font-size: 44px;
257 line-height: 1.1;
258 letter-spacing: -0.02em;
259 margin: 0 0 20px;
260 color: var(--text);
261 font-weight: 700;
262 }
263 .landing-hero-sub {
264 font-size: 18px;
265 color: var(--text-muted);
266 margin: 0 auto 32px;
267 max-width: 640px;
268 line-height: 1.5;
269 }
270 .landing-hero-ctas {
271 display: flex;
272 gap: 12px;
273 justify-content: center;
274 flex-wrap: wrap;
275 margin-bottom: 16px;
276 }
277 .landing-cta-primary,
278 .landing-cta-secondary {
279 padding: 10px 20px;
280 font-size: 15px;
281 font-weight: 600;
282 }
283 .landing-trust {
284 font-size: 13px;
285 color: var(--text-muted);
286 margin-top: 12px;
287 letter-spacing: 0.02em;
288 }
8e9f1d9Claude289 .landing-stats {
290 margin-top: 20px;
291 font-size: 14px;
292 color: var(--text-muted);
293 }
294 .landing-stats strong {
295 color: var(--fg);
296 font-weight: 600;
297 }
298 .landing-stats-sep {
299 opacity: 0.6;
300 }
2b821b7Claude301
302 /* ---------- Section scaffolding ---------- */
303 .landing-section {
304 padding: 64px 16px;
305 max-width: 1080px;
306 margin: 0 auto;
307 border-bottom: 1px solid var(--border);
308 }
309 .landing-section-title {
310 font-size: 28px;
311 font-weight: 700;
312 margin: 0 0 12px;
313 color: var(--text);
314 letter-spacing: -0.01em;
315 }
316 .landing-section-sub {
317 font-size: 15px;
318 color: var(--text-muted);
319 margin: 0 0 36px;
320 max-width: 680px;
321 line-height: 1.6;
322 }
323
324 /* ---------- Features grid ---------- */
325 .landing-grid {
326 display: grid;
327 grid-template-columns: repeat(3, 1fr);
328 gap: 16px;
329 }
330 .landing-feature {
331 border: 1px solid var(--border);
332 border-radius: var(--radius);
333 padding: 20px;
334 background: var(--bg-secondary);
335 transition: border-color 0.15s;
336 }
337 .landing-feature:hover { border-color: var(--text-muted); }
338 .landing-feature-icon {
339 font-size: 22px;
340 margin-bottom: 10px;
341 line-height: 1;
342 }
343 .landing-feature-title {
344 font-size: 15px;
345 font-weight: 600;
346 margin: 0 0 6px;
347 color: var(--text);
348 }
349 .landing-feature-desc {
350 font-size: 13px;
351 color: var(--text-muted);
352 line-height: 1.55;
353 margin: 0;
354 }
355
356 /* ---------- Compare strip ---------- */
357 .landing-compare-grid {
358 display: grid;
359 grid-template-columns: 1fr 1fr;
360 gap: 16px;
361 }
362 .landing-compare-col {
363 border: 1px solid var(--border);
364 border-radius: var(--radius);
365 padding: 24px;
366 background: var(--bg-secondary);
367 }
368 .landing-compare-us { border-color: var(--accent); }
369 .landing-compare-col h3 {
370 font-size: 18px;
371 font-weight: 700;
372 margin: 0 0 16px;
373 color: var(--text);
374 }
375 .landing-compare-col ul {
376 list-style: none;
377 margin: 0;
378 padding: 0;
379 }
380 .landing-compare-col li {
381 font-size: 14px;
382 color: var(--text);
383 padding: 6px 0;
384 line-height: 1.5;
385 }
386 .landing-check { color: var(--green); font-weight: 700; margin-right: 8px; }
387 .landing-dash { color: var(--text-muted); font-weight: 700; margin-right: 8px; }
388 .landing-compare-them li { color: var(--text-muted); }
389
390 /* ---------- How it works ---------- */
391 .landing-steps {
392 display: grid;
393 grid-template-columns: repeat(3, 1fr);
394 gap: 16px;
395 }
396 .landing-step {
397 border: 1px solid var(--border);
398 border-radius: var(--radius);
399 padding: 24px;
400 background: var(--bg-secondary);
401 }
402 .landing-step-num {
403 display: inline-flex;
404 align-items: center;
405 justify-content: center;
406 width: 28px;
407 height: 28px;
408 border-radius: 50%;
409 background: var(--accent);
410 color: #fff;
411 font-weight: 700;
412 font-size: 14px;
413 margin-bottom: 12px;
414 }
415 .landing-step h3 {
416 font-size: 16px;
417 font-weight: 600;
418 margin: 0 0 6px;
419 color: var(--text);
420 }
421 .landing-step p {
422 font-size: 13px;
423 color: var(--text-muted);
424 line-height: 1.55;
425 margin: 0;
426 }
427 .landing-step code {
428 font-family: var(--font-mono);
429 font-size: 12px;
430 background: var(--bg-tertiary);
431 padding: 1px 6px;
432 border-radius: 3px;
433 color: var(--text);
434 }
435
436 /* ---------- Final CTA band ---------- */
437 .landing-cta-band {
438 padding: 72px 16px;
439 text-align: center;
440 max-width: 860px;
441 margin: 0 auto;
442 border-bottom: 1px solid var(--border);
443 }
444 .landing-cta-band h2 {
445 font-size: 32px;
446 font-weight: 700;
447 margin: 0 0 12px;
448 color: var(--text);
449 }
450 .landing-cta-band p {
451 font-size: 15px;
452 color: var(--text-muted);
453 margin: 0 auto 28px;
454 max-width: 560px;
455 }
456
457 /* ---------- Foot row ---------- */
458 .landing-foot {
459 padding: 32px 16px 48px;
460 text-align: center;
461 font-size: 13px;
462 color: var(--text-muted);
463 }
464 .landing-foot a { color: var(--text-muted); }
465 .landing-foot a:hover { color: var(--text); text-decoration: none; }
466 .landing-foot-sep { margin: 0 10px; color: var(--border); }
467
468 /* ---------- Responsive ---------- */
469 @media (max-width: 820px) {
470 .landing-hero { padding: 48px 16px 40px; }
471 .landing-hero-title { font-size: 32px; }
472 .landing-hero-sub { font-size: 16px; }
473 .landing-section { padding: 48px 16px; }
474 .landing-section-title { font-size: 24px; }
475 .landing-grid { grid-template-columns: 1fr; }
476 .landing-compare-grid { grid-template-columns: 1fr; }
477 .landing-steps { grid-template-columns: 1fr; }
478 .landing-cta-band { padding: 48px 16px; }
479 .landing-cta-band h2 { font-size: 24px; }
480 }
481`;
482
483export default LandingPage;