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

marketing.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.

marketing.tsxBlame1649 lines · 1 contributor
b0148e9Claude1/**
2 * Marketing surface — public pages that don't fit the app shell.
3 * Pricing, features, about. Logged-out and logged-in safe (softAuth).
4 *
5 * All pages use the new Editorial-Technical design system: .display,
6 * .eyebrow, .section-header, .stagger, .gradient-text utilities are
7 * defined globally in src/views/layout.tsx.
8 */
9
10import { Hono } from "hono";
11import type { FC } from "hono/jsx";
12import { Layout } from "../views/layout";
13import { softAuth } from "../middleware/auth";
14import type { AuthEnv } from "../middleware/auth";
15
16const marketing = new Hono<AuthEnv>();
17marketing.use("*", softAuth);
18
19// ============================================================
20// /pricing
21// ============================================================
22
23marketing.get("/pricing", (c) => {
24 const user = c.get("user");
25 return c.html(
26 <Layout title="Pricing — gluecron" user={user}>
27 <PricingPage />
28 </Layout>,
29 );
30});
31
32const PricingPage: FC = () => (
33 <>
fa880f2Claude34 <style dangerouslySetInnerHTML={{ __html: pricingCss }} />
b0148e9Claude35 <div class="mkt-root">
36 <header class="mkt-hero">
37 <div class="eyebrow">Pricing</div>
38 <h1 class="display mkt-hero-title">
39 Honest pricing for{" "}
40 <span class="gradient-text">teams that ship.</span>
41 </h1>
42 <p class="mkt-hero-sub">
43 Self-hosting is free forever. Hosted plans price the AI calls, not
44 the seats. No one pays per developer.
45 </p>
46 </header>
47
48 <section class="mkt-pricing-grid stagger">
49 <PricingTier
50 tier="Free"
51 price="$0"
52 cadence="forever"
53 desc="For personal projects + open source. Full AI suite included."
54 features={[
55 "Unlimited public repos",
56 "3 private repos",
57 "5,000 AI calls / month",
58 "GateTest + auto-repair",
59 "Webhooks + workflows",
60 "Community support",
61 ]}
62 cta="Start free"
63 href="/register"
64 />
65 <PricingTier
66 tier="Pro"
67 price="$12"
68 cadence="per user / month"
69 desc="For working developers. Lifts every quota and adds priority routing."
70 features={[
71 "Unlimited private repos",
72 "100,000 AI calls / month",
73 "Priority AI queue",
74 "Custom domains",
75 "Advanced analytics",
76 "Email support",
77 ]}
78 cta="Go Pro"
79 href="/settings/billing"
80 highlight
81 />
82 <PricingTier
83 tier="Team"
84 price="$29"
85 cadence="per user / month"
86 desc="For organisations running production on Gluecron."
87 features={[
88 "Everything in Pro",
89 "Unlimited AI calls",
90 "Org-level SSO + SCIM",
91 "Audit log retention",
92 "SLA-backed uptime",
93 "Slack support channel",
94 ]}
95 cta="Talk to us"
96 href="mailto:hello@gluecron.com"
97 />
98 <PricingTier
99 tier="Enterprise"
100 price="Custom"
101 cadence="contact us"
102 desc="On-prem deploy, dedicated capacity, 24/7 incident response."
103 features={[
104 "Everything in Team",
105 "On-prem / VPC deploy",
106 "Dedicated AI capacity",
107 "Private model routing",
108 "DPA + custom contracts",
109 "24/7 incident response",
110 ]}
111 cta="Contact sales"
112 href="mailto:enterprise@gluecron.com"
113 />
114 </section>
115
116 <section class="mkt-section">
117 <div class="section-header">
118 <div class="eyebrow">Self-hosted</div>
119 <h2>Run it on your own metal. No license, no telemetry.</h2>
120 <p>
121 Gluecron is a single Bun binary plus Postgres. Deploy to Fly,
122 Railway, your own VPS, or air-gapped infra. Free forever for
123 self-hosters of any size.
124 </p>
125 </div>
126 <div class="mkt-selfhost-card surface-glow">
127 <div class="mkt-selfhost-grid">
128 <div class="mkt-selfhost-cell">
129 <div class="mkt-selfhost-num">$0</div>
130 <div class="mkt-selfhost-label">License</div>
131 </div>
132 <div class="mkt-selfhost-cell">
133 <div class="mkt-selfhost-num">∞</div>
134 <div class="mkt-selfhost-label">Users</div>
135 </div>
136 <div class="mkt-selfhost-cell">
137 <div class="mkt-selfhost-num">∞</div>
138 <div class="mkt-selfhost-label">Repos</div>
139 </div>
140 <div class="mkt-selfhost-cell">
141 <div class="mkt-selfhost-num">0</div>
142 <div class="mkt-selfhost-label">Telemetry</div>
143 </div>
144 </div>
145 <div class="mkt-selfhost-cta">
146 <a href="/help" class="btn btn-primary btn-lg">Self-host guide</a>
147 <a
148 href="https://github.com/ccantynz-alt/Gluecron.com"
149 class="btn btn-ghost btn-lg"
150 >
151 View source
152 </a>
153 </div>
154 </div>
155 </section>
156
157 <section class="mkt-section">
158 <div class="section-header">
159 <div class="eyebrow">Questions</div>
160 <h2>The fine print, in plain English.</h2>
161 </div>
162 <div class="mkt-faq">
163 <FaqItem
164 q="What counts as an AI call?"
165 a="Every Claude inference: PR review, security scan, spec-to-PR draft, commit message suggestion, chat reply. We don't bill for failed calls or retries on our end."
166 />
167 <FaqItem
168 q="Can I bring my own Anthropic key?"
169 a="Yes. Pro and above can supply ANTHROPIC_API_KEY; calls run against your account and don't count toward our quota. Useful for orgs with prepaid commits or enterprise rate limits."
170 />
171 <FaqItem
172 q="What happens if I hit my AI quota?"
173 a="AI features degrade gracefully — gates still run, code still hosts. AI suggestions queue at the back of the line. No surprise overage bills, ever."
174 />
175 <FaqItem
176 q="Do you charge for runner minutes?"
177 a="No. Workflow runs are unmetered on hosted plans. Self-hosters bring their own compute, of course."
178 />
179 <FaqItem
180 q="What's the uptime SLA?"
181 a="Team and Enterprise carry a 99.9% monthly SLA with credits. Free and Pro are best-effort but we publish live status at /status and historical incidents in CHANGELOG.md."
182 />
183 <FaqItem
184 q="How do I migrate off Gluecron?"
185 a="Same way you migrate off GitHub: git remote set-url and push. We're git-compatible to the byte. No vendor lock, no migration tax."
186 />
187 </div>
188 </section>
189
190 <CtaBlock />
191 </div>
192 </>
193);
194
195const PricingTier: FC<{
196 tier: string;
197 price: string;
198 cadence: string;
199 desc: string;
200 features: string[];
201 cta: string;
202 href: string;
203 highlight?: boolean;
204}> = ({ tier, price, cadence, desc, features, cta, href, highlight }) => (
205 <div class={`mkt-tier${highlight ? " mkt-tier-hl" : ""}`}>
206 {highlight && <div class="mkt-tier-badge">Most popular</div>}
207 <div class="mkt-tier-name">{tier}</div>
208 <div class="mkt-tier-amount">
209 <span class="mkt-tier-num">{price}</span>
210 <span class="mkt-tier-cad">{cadence}</span>
211 </div>
212 <p class="mkt-tier-desc">{desc}</p>
213 <ul class="mkt-tier-features">
214 {features.map((f) => (
215 <li>
216 <span class="mkt-tier-check">{"✓"}</span>
217 {f}
218 </li>
219 ))}
220 </ul>
221 <a
222 href={href}
223 class={`btn ${highlight ? "btn-primary" : "btn-secondary"} btn-block`}
224 style="margin-top:auto"
225 >
226 {cta}
227 </a>
228 </div>
229);
230
231const FaqItem: FC<{ q: string; a: string }> = ({ q, a }) => (
232 <details class="mkt-faq-item">
233 <summary class="mkt-faq-q">
234 <span>{q}</span>
235 <span class="mkt-faq-toggle" aria-hidden="true">{"+"}</span>
236 </summary>
237 <p class="mkt-faq-a">{a}</p>
238 </details>
239);
240
241// ============================================================
242// /features
243// ============================================================
244
245marketing.get("/features", (c) => {
246 const user = c.get("user");
247 return c.html(
248 <Layout title="Features — gluecron" user={user}>
249 <FeaturesPage />
250 </Layout>,
251 );
252});
253
254const FeaturesPage: FC = () => (
255 <>
fa880f2Claude256 <style dangerouslySetInnerHTML={{ __html: featuresCss }} />
b0148e9Claude257 <div class="mkt-root">
258 <header class="mkt-hero">
259 <div class="eyebrow">Features</div>
260 <h1 class="display mkt-hero-title">
261 A complete dev platform.{" "}
262 <span class="gradient-text">Nothing extra to buy.</span>
263 </h1>
264 <p class="mkt-hero-sub">
265 Hosting, CI, AI review, security scanning, deploy webhooks,
266 marketplace, packages, pages — every surface you need, ready on
267 day one.
268 </p>
269 </header>
270
271 <FeatureCategory
272 eyebrow="Code intelligence"
273 title="The AI is a teammate, not an upsell."
274 items={[
275 {
276 title: "AI code review",
277 desc: "Real Claude review on every PR open. Inline file/line comments. Idempotent across re-runs.",
278 },
279 {
280 title: "Spec-to-PR",
281 desc: "Drop a feature spec in plain English. AI drafts the entire PR — branch, commits, description.",
282 },
283 {
284 title: "AI security review",
285 desc: "Sonnet 4 reads diffs for OWASP-class issues. Posts as inline review comments, not noise.",
286 },
287 {
288 title: "Auto-repair",
289 desc: "Failed gate? AI tries to fix it and pushes a follow-up commit. Your repo self-corrects.",
290 },
291 {
292 title: "AI commit messages",
293 desc: "One-click 'suggest with AI' on the web editor. Concise, conventional-commit format.",
294 },
295 {
296 title: "AI changelogs",
297 desc: "Generated on release create. Plus an arbitrary-range viewer at /:repo/ai/changelog.",
298 },
299 {
300 title: "AI incident responder",
301 desc: "Failed deploy? AI opens an issue with the failing logs + suggested fix + linked PR.",
302 },
303 {
304 title: "AI test generation",
305 desc: "Drops test stubs for uncovered functions. You review, edit, commit.",
306 },
307 ]}
308 />
309
310 <FeatureCategory
311 eyebrow="Quality gate"
312 title="Nothing broken ever reaches production."
313 items={[
314 {
315 title: "GateTest integration",
316 desc: "Push triggers GateTest. Results post back as inline annotations on the commit.",
317 },
318 {
319 title: "Secret scanner",
320 desc: "15 patterns, runs on every push. Blocks the push if a real secret leaks.",
321 },
322 {
323 title: "Branch protection",
324 desc: "Required checks, code-owner reviews, push restrictions, force-push blocking.",
325 },
326 {
327 title: "Repository rulesets",
328 desc: "Named policy bundles. Six rule types from commit message regex to max file size.",
329 },
330 {
331 title: "Required checks matrix",
332 desc: "Per branch-protection list of named checks that must pass before merge.",
333 },
334 {
335 title: "Protected tags",
336 desc: "Owners declare patterns (v*, release-*) that only owners can push.",
337 },
338 {
339 title: "Merge queue",
340 desc: "Serialised merge with re-test against latest base. No more 'green when merged, red on main'.",
341 },
342 {
343 title: "Pre-receive policy",
344 desc: "Ref-name patterns and push policies enforced at the HTTP layer with 403s.",
345 },
346 ]}
347 />
348
349 <FeatureCategory
350 eyebrow="Real-time"
351 title="No polling, no refresh, no waiting."
352 items={[
353 {
354 title: "Live workflow logs",
355 desc: "Step-by-step output streams over SSE the moment your runner emits it.",
356 },
357 {
358 title: "Live PR comments",
359 desc: "New comment in another tab? You see a 'reload to view' banner immediately.",
360 },
361 {
362 title: "Live deploy events",
363 desc: "Crontech-Gluecron event bus pushes deploy state. Watch deploys happen.",
364 },
365 {
366 title: "Live presence",
367 desc: "See who's looking at the same PR right now. Avoid double review work.",
368 },
369 ]}
370 />
371
372 <FeatureCategory
373 eyebrow="Platform"
374 title="Everything GitHub charges extra for."
375 items={[
376 {
377 title: "Workflow runner",
378 desc: "Drop yaml in `.gluecron/workflows/`. Runs on push. Cron triggers, secrets, matrix.",
379 },
380 {
381 title: "Packages registry",
382 desc: "npm protocol. Publish, install, yank with `glc_` PAT auth. Container registry deferred.",
383 },
384 {
385 title: "Pages hosting",
386 desc: "Serves blobs from the latest gh-pages commit. Custom domains, short cache headers.",
387 },
388 {
389 title: "Marketplace + apps",
390 desc: "Install third-party apps with permission scopes. App-bot push auth via ghi_ tokens.",
391 },
392 {
393 title: "Discussions",
394 desc: "Categorised threads, pinned, locked, Q&A answers. Zero extra config.",
395 },
396 {
397 title: "Wikis + Gists + Projects",
398 desc: "All shipped, all free, all integrated. No upsell tier.",
399 },
400 ]}
401 />
402
403 <FeatureCategory
404 eyebrow="Identity + governance"
405 title="Enterprise-tier auth from day one."
406 items={[
407 {
408 title: "TOTP + WebAuthn",
409 desc: "Both 2FA paths shipped. Passkey-only login if you want it.",
410 },
411 {
412 title: "OIDC SSO",
413 desc: "Okta, Azure AD, Auth0, Google Workspace. Auto-create users. Email-domain allowlist.",
414 },
415 {
416 title: "OAuth provider",
417 desc: "Third-party apps request scoped access to user repos. Standard auth-code flow.",
418 },
419 {
420 title: "Personal access tokens",
421 desc: "SHA-256 hashed, scoped, revocable. The clean way to script Gluecron.",
422 },
423 {
424 title: "Audit log (per-user + per-repo)",
425 desc: "Every sensitive action recorded. Browseable at /settings/audit.",
426 },
427 {
428 title: "Site admin panel",
429 desc: "/admin for site-wide flags: registration locks, banners, read-only mode.",
430 },
431 ]}
432 />
433
434 <FeatureCategory
435 eyebrow="Integrations"
436 title="Speak every protocol your tools use."
437 items={[
438 {
439 title: "MCP server",
440 desc: "Claude Desktop, Cursor, Code, Cline plug in natively. Read + scoped write tools.",
441 },
442 {
443 title: "REST API v2",
444 desc: "Full CRUD across resources. Versioned, documented, stable.",
445 },
446 {
447 title: "GraphQL endpoint",
448 desc: "Single-request fetch for complex client views. GraphiQL explorer at /api/graphql.",
449 },
450 {
451 title: "Webhooks",
452 desc: "HMAC-signed outbound to your URLs on push, issue, PR, star, comment, deploy.",
453 },
454 {
455 title: "Smart-HTTP git",
456 desc: "git push, git pull, git clone — exactly what your tools already speak.",
457 },
458 {
459 title: "VS Code extension",
460 desc: "Explain, open-on-web, semantic search, generate tests — all from the editor.",
461 },
462 ]}
463 />
464
465 <CtaBlock />
466 </div>
467 </>
468);
469
470const FeatureCategory: FC<{
471 eyebrow: string;
472 title: string;
473 items: Array<{ title: string; desc: string }>;
474}> = ({ eyebrow, title, items }) => (
475 <section class="mkt-section">
476 <div class="section-header left">
477 <div class="eyebrow">{eyebrow}</div>
478 <h2>{title}</h2>
479 </div>
480 <div class="mkt-feat-grid stagger">
481 {items.map((it) => (
482 <div class="mkt-feat-cell">
483 <h3 class="mkt-feat-title">{it.title}</h3>
484 <p class="mkt-feat-desc">{it.desc}</p>
485 </div>
486 ))}
487 </div>
488 </section>
489);
490
491// ============================================================
492// /about
493// ============================================================
494
495marketing.get("/about", (c) => {
496 const user = c.get("user");
497 return c.html(
498 <Layout title="About — gluecron" user={user}>
499 <AboutPage />
500 </Layout>,
501 );
502});
503
504const AboutPage: FC = () => (
505 <>
fa880f2Claude506 <style dangerouslySetInnerHTML={{ __html: aboutCss }} />
b0148e9Claude507 <div class="mkt-root">
508 <header class="mkt-hero">
509 <div class="eyebrow">About</div>
510 <h1 class="display mkt-hero-title">
511 We're building the platform{" "}
512 <span class="gradient-text">software writes itself on.</span>
513 </h1>
514 <p class="mkt-hero-sub">
515 Most code in 2026 is written by AI. Most reviews are too. The
516 platforms hosting that code were built for a previous era.
517 Gluecron is built for this one.
518 </p>
519 </header>
520
521 <section class="mkt-section">
522 <div class="mkt-prose">
523 <div class="eyebrow">Mission</div>
524 <h2>An IDE for your repo. A teammate, not a sidebar.</h2>
525 <p>
526 GitHub treats AI as a feature you bolt onto your workflow. We
527 treat it as a peer that opens PRs, reviews diffs, fixes
528 regressions, and ships its own changes — visibly, accountably,
529 with a real identity in your history.
530 </p>
531 <p>
532 Everything is green by default. Every new repo auto-configures
533 gates, branch protection, labels, CODEOWNERS, and a welcome issue.
534 Users opt out per feature. Defaults are maximum-green so
535 <strong> nothing broken ever reaches production, the website,
536 or the customer.</strong>
537 </p>
538 </div>
539 </section>
540
541 <section class="mkt-section">
542 <div class="section-header">
543 <div class="eyebrow">Principles</div>
544 <h2>Six rules we don't compromise on.</h2>
545 </div>
546 <div class="mkt-principles stagger">
547 <PrincipleCard
548 n="01"
549 title="No vendor lock"
550 desc="We're git-compatible to the byte. Migrate off Gluecron the same way you migrate off GitHub: git remote set-url. Your code is yours."
551 />
552 <PrincipleCard
553 n="02"
554 title="No surprise bills"
555 desc="AI quotas degrade gracefully when hit. No overage, no automatic upgrade. We publish prices in dollars, not credits."
556 />
557 <PrincipleCard
558 n="03"
559 title="Self-host is first-class"
560 desc="Single Bun binary, single Postgres, zero telemetry. The same product the hosted version runs on, free forever for self-hosters."
561 />
562 <PrincipleCard
563 n="04"
564 title="AI is accountable"
565 desc="Every AI commit is signed by an app-bot identity. Every AI comment is labelled. You can audit, revert, or disable any AI agent at any time."
566 />
567 <PrincipleCard
568 n="05"
569 title="Real-time over polling"
570 desc="SSE for logs, comments, deploys, presence. The web should feel like a desktop app. No spinners on a tab you've already loaded."
571 />
572 <PrincipleCard
573 n="06"
574 title="Open by default"
575 desc="REST + GraphQL + MCP + Smart-HTTP + webhooks. Your tools speak our platform without asking. Programmatic access is not an enterprise tier."
576 />
577 </div>
578 </section>
579
580 <section class="mkt-section">
581 <div class="section-header">
582 <div class="eyebrow">Stack</div>
583 <h2>Built on the boring, fast parts.</h2>
584 </div>
585 <div class="mkt-stack">
586 <StackPill name="Bun" desc="runtime" />
587 <StackPill name="Hono" desc="server framework" />
588 <StackPill name="Drizzle" desc="ORM" />
589 <StackPill name="Neon Postgres" desc="primary database" />
590 <StackPill name="Claude Sonnet 4" desc="AI review + chat" />
591 <StackPill name="Claude Haiku 4.5" desc="AI commits + summaries" />
592 <StackPill name="Smart-HTTP git" desc="protocol" />
593 <StackPill name="Fly.io" desc="deploy target" />
594 </div>
595 </section>
596
597 <section class="mkt-section mkt-contact">
598 <div class="section-header">
599 <div class="eyebrow">Contact</div>
600 <h2>Three places to find us.</h2>
601 </div>
602 <div class="mkt-contact-grid">
603 <ContactCard
604 label="Product + sales"
605 email="hello@gluecron.com"
606 line="For demos, pricing questions, partnership ideas."
607 />
608 <ContactCard
609 label="Security"
610 email="security@gluecron.com"
611 line="Responsible disclosure. PGP key on request."
612 />
613 <ContactCard
614 label="Support"
615 email="support@gluecron.com"
616 line="Bugs, account help, anything that's broken."
617 />
618 </div>
619 </section>
620
621 <CtaBlock />
622 </div>
623 </>
624);
625
626const PrincipleCard: FC<{ n: string; title: string; desc: string }> = ({
627 n,
628 title,
629 desc,
630}) => (
631 <div class="mkt-principle">
632 <div class="mkt-principle-num">{n}</div>
633 <h3 class="mkt-principle-title">{title}</h3>
634 <p class="mkt-principle-desc">{desc}</p>
635 </div>
636);
637
638const StackPill: FC<{ name: string; desc: string }> = ({ name, desc }) => (
639 <div class="mkt-stack-pill">
640 <span class="mkt-stack-name">{name}</span>
641 <span class="mkt-stack-desc">{desc}</span>
642 </div>
643);
644
645const ContactCard: FC<{ label: string; email: string; line: string }> = ({
646 label,
647 email,
648 line,
649}) => (
650 <a href={`mailto:${email}`} class="mkt-contact-card">
651 <div class="mkt-contact-label">{label}</div>
652 <div class="mkt-contact-email">{email}</div>
653 <p class="mkt-contact-line">{line}</p>
654 </a>
655);
656
657// ============================================================
658// Shared closing CTA block
659// ============================================================
660
661const CtaBlock: FC = () => (
662 <section class="mkt-cta">
663 <div class="mkt-cta-card">
664 <div class="mkt-cta-bg" aria-hidden="true" />
665 <div class="eyebrow">Get started</div>
666 <h2 class="mkt-cta-title">
667 Stop maintaining the platform.<br />
668 <span class="gradient-text">Start shipping the product.</span>
669 </h2>
670 <div class="mkt-cta-buttons">
671 <a href="/register" class="btn btn-primary btn-xl">
672 Create your account
673 <span aria-hidden="true">{"→"}</span>
674 </a>
675 <a href="/import" class="btn btn-ghost btn-xl">
676 Migrate from GitHub
677 </a>
678 </div>
679 </div>
680 </section>
681);
682
683// ============================================================
684// Styles — namespaced under .mkt- so they don't leak
685// ============================================================
686
687const sharedMktCss = `
688 .mkt-root {
689 max-width: 1180px;
690 margin: 0 auto;
691 padding: 0 16px;
692 }
693
694 /* Hero */
695 .mkt-hero {
696 text-align: center;
697 padding: var(--s-16) 0 var(--s-12);
698 max-width: 920px;
699 margin: 0 auto;
700 position: relative;
701 }
702 .mkt-hero::before {
703 content: '';
704 position: absolute;
705 top: 0; left: 50%;
706 transform: translateX(-50%);
707 width: 70%; height: 60%;
708 background: radial-gradient(ellipse at center, rgba(140,109,255,0.14), transparent 65%);
709 z-index: -1;
710 pointer-events: none;
711 }
712 .mkt-hero .eyebrow { justify-content: center; margin: 0 auto var(--s-4); }
713 .mkt-hero-title {
714 font-size: clamp(36px, 6.5vw, 76px);
715 line-height: 1.02;
716 letter-spacing: -0.038em;
717 margin: 0 0 var(--s-5);
718 }
719 .mkt-hero-sub {
720 font-size: clamp(15px, 1.5vw, 18px);
721 color: var(--text-muted);
722 max-width: 640px;
723 margin: 0 auto;
724 line-height: 1.55;
725 }
726
727 /* Section spacing */
728 .mkt-section { margin: var(--s-16) auto; }
729
730 /* Closing CTA */
731 .mkt-cta { margin: var(--s-20) auto var(--s-12); }
732 .mkt-cta-card {
733 position: relative;
734 text-align: center;
735 padding: var(--s-14) var(--s-7);
736 border: 1px solid var(--border-strong);
737 border-radius: var(--r-2xl);
738 background: var(--bg-elevated);
739 overflow: hidden;
740 isolation: isolate;
741 }
742 .mkt-cta-bg {
743 position: absolute;
744 inset: 0;
745 z-index: -1;
746 background:
747 radial-gradient(60% 100% at 50% 0%, rgba(140,109,255,0.16), transparent 65%),
748 radial-gradient(40% 80% at 80% 100%, rgba(54,197,214,0.10), transparent 65%);
749 }
750 .mkt-cta-card .eyebrow { justify-content: center; }
751 .mkt-cta-title {
752 font-family: var(--font-display);
753 font-size: clamp(28px, 4vw, 52px);
754 line-height: 1.05;
755 letter-spacing: -0.03em;
756 font-weight: 600;
757 margin: var(--s-3) 0 var(--s-7);
758 color: var(--text-strong);
759 }
760 .mkt-cta-buttons {
761 display: flex;
762 gap: 12px;
763 justify-content: center;
764 flex-wrap: wrap;
765 }
766
767 @media (max-width: 640px) {
768 .mkt-hero { padding: var(--s-10) 0 var(--s-8); }
769 .mkt-section { margin: var(--s-12) auto; }
770 .mkt-cta-buttons .btn { width: 100%; justify-content: center; }
771 }
772`;
773
774const pricingCss = sharedMktCss + `
775 .mkt-pricing-grid {
776 display: grid;
777 grid-template-columns: repeat(4, 1fr);
778 gap: 12px;
779 margin: var(--s-12) auto var(--s-16);
780 align-items: stretch;
781 }
782 .mkt-tier {
783 position: relative;
784 background: var(--bg-elevated);
785 border: 1px solid var(--border);
786 border-radius: var(--r-lg);
787 padding: var(--s-7) var(--s-6);
788 display: flex;
789 flex-direction: column;
790 gap: var(--s-4);
791 transition: border-color var(--t-base) var(--ease), transform var(--t-base) var(--ease-out-quart);
792 }
793 .mkt-tier:hover { border-color: var(--border-strong); transform: translateY(-3px); }
794 .mkt-tier-hl {
795 border-color: rgba(140,109,255,0.40);
796 box-shadow: var(--elev-2), 0 0 0 1px rgba(140,109,255,0.30);
797 background:
798 linear-gradient(180deg, rgba(140,109,255,0.05), transparent 50%),
799 var(--bg-elevated);
800 }
801 .mkt-tier-hl:hover { border-color: rgba(140,109,255,0.60); }
802 .mkt-tier-badge {
803 position: absolute;
804 top: -10px;
805 left: 50%;
806 transform: translateX(-50%);
807 padding: 3px 12px;
808 background: var(--accent-gradient);
809 color: #fff;
810 font-family: var(--font-mono);
811 font-size: 10px;
812 letter-spacing: 0.1em;
813 text-transform: uppercase;
814 font-weight: 600;
815 border-radius: var(--r-full);
816 box-shadow: 0 4px 14px -2px rgba(140,109,255,0.45);
817 white-space: nowrap;
818 }
819 .mkt-tier-name {
820 font-family: var(--font-mono);
821 font-size: 11px;
822 text-transform: uppercase;
823 letter-spacing: 0.16em;
824 color: var(--text-muted);
825 }
826 .mkt-tier-amount {
827 display: flex;
828 align-items: baseline;
829 gap: 6px;
830 flex-wrap: wrap;
831 }
832 .mkt-tier-num {
833 font-family: var(--font-display);
834 font-size: 36px;
835 font-weight: 600;
836 letter-spacing: -0.03em;
837 color: var(--text-strong);
838 }
839 .mkt-tier-cad {
840 font-size: 12px;
841 color: var(--text-faint);
842 }
843 .mkt-tier-desc {
844 font-size: var(--t-sm);
845 color: var(--text-muted);
846 line-height: 1.5;
847 margin: 0;
848 }
849 .mkt-tier-features {
850 list-style: none;
851 padding: 0;
852 margin: 0;
853 display: flex;
854 flex-direction: column;
855 gap: 7px;
856 font-size: var(--t-sm);
857 color: var(--text);
858 }
859 .mkt-tier-features li {
860 display: flex;
861 align-items: flex-start;
862 gap: 9px;
863 line-height: 1.45;
864 }
865 .mkt-tier-check {
866 color: var(--accent);
867 font-weight: 600;
868 flex-shrink: 0;
869 line-height: 1.45;
870 }
871
872 .mkt-selfhost-card {
873 max-width: 880px;
874 margin: 0 auto;
875 padding: var(--s-10);
876 text-align: center;
877 }
878 .mkt-selfhost-grid {
879 display: grid;
880 grid-template-columns: repeat(4, 1fr);
881 gap: var(--s-6);
882 margin-bottom: var(--s-8);
883 padding-bottom: var(--s-8);
884 border-bottom: 1px solid var(--border-subtle);
885 }
886 .mkt-selfhost-cell { text-align: center; }
887 .mkt-selfhost-num {
888 font-family: var(--font-display);
889 font-size: 44px;
890 font-weight: 600;
891 letter-spacing: -0.03em;
892 color: var(--text-strong);
893 line-height: 1;
894 background: var(--accent-gradient);
895 -webkit-background-clip: text;
896 background-clip: text;
897 -webkit-text-fill-color: transparent;
898 }
899 .mkt-selfhost-label {
900 font-family: var(--font-mono);
901 font-size: 11px;
902 text-transform: uppercase;
903 letter-spacing: 0.14em;
904 color: var(--text-muted);
905 margin-top: var(--s-2);
906 }
907 .mkt-selfhost-cta {
908 display: flex;
909 gap: 12px;
910 justify-content: center;
911 flex-wrap: wrap;
912 }
913
914 .mkt-faq {
915 max-width: 760px;
916 margin: 0 auto;
917 border: 1px solid var(--border);
918 border-radius: var(--r-lg);
919 overflow: hidden;
920 background: var(--bg-elevated);
921 }
922 .mkt-faq-item {
923 border-bottom: 1px solid var(--border-subtle);
924 }
925 .mkt-faq-item:last-child { border-bottom: none; }
926 .mkt-faq-q {
927 display: flex;
928 justify-content: space-between;
929 align-items: center;
930 gap: 16px;
931 padding: 18px 24px;
932 cursor: pointer;
933 font-size: var(--t-md);
934 font-weight: 500;
935 color: var(--text-strong);
936 list-style: none;
937 transition: background var(--t-fast) var(--ease);
938 }
939 .mkt-faq-q::-webkit-details-marker { display: none; }
940 .mkt-faq-q:hover { background: var(--bg-hover); }
941 .mkt-faq-toggle {
942 font-family: var(--font-mono);
943 font-size: 18px;
944 color: var(--text-muted);
945 transition: transform var(--t-base) var(--ease-spring);
946 flex-shrink: 0;
947 }
948 .mkt-faq-item[open] .mkt-faq-toggle { transform: rotate(45deg); color: var(--accent); }
949 .mkt-faq-a {
950 padding: 0 24px 20px;
951 color: var(--text-muted);
952 font-size: var(--t-sm);
953 line-height: 1.6;
954 margin: 0;
955 }
956
957 @media (max-width: 960px) {
958 .mkt-pricing-grid { grid-template-columns: repeat(2, 1fr); }
959 }
960 @media (max-width: 640px) {
961 .mkt-pricing-grid { grid-template-columns: 1fr; }
962 .mkt-selfhost-grid { grid-template-columns: repeat(2, 1fr); }
963 .mkt-selfhost-card { padding: var(--s-7); }
964 }
965`;
966
967const featuresCss = sharedMktCss + `
968 .mkt-feat-grid {
969 display: grid;
970 grid-template-columns: repeat(2, 1fr);
971 gap: 1px;
972 background: var(--border-subtle);
973 border: 1px solid var(--border);
974 border-radius: var(--r-lg);
975 overflow: hidden;
976 }
977 .mkt-feat-cell {
978 padding: var(--s-6);
979 background: var(--bg-elevated);
980 transition: background var(--t-fast) var(--ease);
981 position: relative;
982 }
983 .mkt-feat-cell:hover { background: var(--bg-surface); }
984 .mkt-feat-cell::before {
985 content: '';
986 position: absolute;
987 left: var(--s-6);
988 top: var(--s-6);
989 width: 4px;
990 height: 4px;
991 border-radius: 50%;
992 background: var(--accent);
993 opacity: 0;
994 transition: opacity var(--t-fast) var(--ease);
995 }
996 .mkt-feat-cell:hover::before { opacity: 1; }
997 .mkt-feat-title {
998 font-family: var(--font-display);
999 font-size: var(--t-md);
1000 font-weight: 600;
1001 letter-spacing: -0.012em;
1002 margin: 0 0 var(--s-2);
1003 color: var(--text-strong);
1004 padding-left: 14px;
1005 }
1006 .mkt-feat-desc {
1007 font-size: var(--t-sm);
1008 color: var(--text-muted);
1009 line-height: 1.55;
1010 margin: 0;
1011 padding-left: 14px;
1012 }
1013 @media (max-width: 720px) {
1014 .mkt-feat-grid { grid-template-columns: 1fr; }
1015 }
1016`;
1017
1018const aboutCss = sharedMktCss + `
1019 .mkt-prose {
1020 max-width: 720px;
1021 margin: 0 auto;
1022 }
1023 .mkt-prose h2 {
1024 font-size: clamp(24px, 3vw, 36px);
1025 line-height: 1.15;
1026 letter-spacing: -0.025em;
1027 margin: var(--s-3) 0 var(--s-5);
1028 }
1029 .mkt-prose p {
1030 color: var(--text);
1031 font-size: var(--t-md);
1032 line-height: 1.7;
1033 margin: 0 0 var(--s-4);
1034 }
1035 .mkt-prose p strong { color: var(--text-strong); font-weight: 600; }
1036
1037 .mkt-principles {
1038 display: grid;
1039 grid-template-columns: repeat(3, 1fr);
1040 gap: 16px;
1041 }
1042 .mkt-principle {
1043 background: var(--bg-elevated);
1044 border: 1px solid var(--border);
1045 border-radius: var(--r-lg);
1046 padding: var(--s-7);
1047 transition: border-color var(--t-base) var(--ease), transform var(--t-base) var(--ease-out-quart);
1048 }
1049 .mkt-principle:hover { border-color: var(--border-strong); transform: translateY(-2px); }
1050 .mkt-principle-num {
1051 font-family: var(--font-mono);
1052 font-size: 11px;
1053 color: var(--accent);
1054 background: var(--accent-gradient-faint);
1055 border: 1px solid rgba(140,109,255,0.30);
1056 padding: 3px 9px;
1057 border-radius: var(--r-full);
1058 letter-spacing: 0.06em;
1059 display: inline-block;
1060 margin-bottom: var(--s-4);
1061 }
1062 .mkt-principle-title {
1063 font-family: var(--font-display);
1064 font-size: 19px;
1065 font-weight: 600;
1066 letter-spacing: -0.018em;
1067 margin: 0 0 var(--s-2);
1068 color: var(--text-strong);
1069 }
1070 .mkt-principle-desc {
1071 font-size: var(--t-sm);
1072 color: var(--text-muted);
1073 line-height: 1.6;
1074 margin: 0;
1075 }
1076
1077 .mkt-stack {
1078 display: flex;
1079 flex-wrap: wrap;
1080 gap: 10px;
1081 justify-content: center;
1082 max-width: 880px;
1083 margin: 0 auto;
1084 }
1085 .mkt-stack-pill {
1086 display: inline-flex;
1087 align-items: center;
1088 gap: 8px;
1089 padding: 9px 16px;
1090 background: var(--bg-elevated);
1091 border: 1px solid var(--border);
1092 border-radius: var(--r-full);
1093 transition: border-color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
1094 }
1095 .mkt-stack-pill:hover { border-color: rgba(140,109,255,0.4); transform: translateY(-1px); }
1096 .mkt-stack-name {
1097 font-family: var(--font-display);
1098 font-weight: 600;
1099 color: var(--text-strong);
1100 font-size: var(--t-sm);
1101 letter-spacing: -0.01em;
1102 }
1103 .mkt-stack-desc {
1104 font-family: var(--font-mono);
1105 font-size: 11px;
1106 color: var(--text-faint);
1107 letter-spacing: 0.04em;
1108 }
1109
1110 .mkt-contact-grid {
1111 display: grid;
1112 grid-template-columns: repeat(3, 1fr);
1113 gap: 16px;
1114 max-width: 880px;
1115 margin: 0 auto;
1116 }
1117 .mkt-contact-card {
1118 display: block;
1119 padding: var(--s-7);
1120 background: var(--bg-elevated);
1121 border: 1px solid var(--border);
1122 border-radius: var(--r-lg);
1123 text-decoration: none;
1124 transition: border-color var(--t-base) var(--ease), transform var(--t-base) var(--ease-out-quart);
1125 }
1126 .mkt-contact-card:hover {
1127 border-color: rgba(140,109,255,0.4);
1128 transform: translateY(-2px);
1129 text-decoration: none;
1130 }
1131 .mkt-contact-label {
1132 font-family: var(--font-mono);
1133 font-size: 11px;
1134 text-transform: uppercase;
1135 letter-spacing: 0.14em;
1136 color: var(--text-muted);
1137 margin-bottom: var(--s-3);
1138 }
1139 .mkt-contact-email {
1140 font-family: var(--font-display);
1141 font-size: var(--t-md);
1142 font-weight: 600;
1143 letter-spacing: -0.012em;
1144 color: var(--accent);
1145 margin-bottom: var(--s-2);
1146 word-break: break-all;
1147 }
1148 .mkt-contact-line {
1149 font-size: var(--t-sm);
1150 color: var(--text-muted);
1151 line-height: 1.5;
1152 margin: 0;
1153 }
1154
1155 @media (max-width: 880px) {
1156 .mkt-principles { grid-template-columns: repeat(2, 1fr); }
1157 .mkt-contact-grid { grid-template-columns: 1fr; }
1158 }
1159 @media (max-width: 560px) {
1160 .mkt-principles { grid-template-columns: 1fr; }
1161 }
1162`;
1163
4551df3Claude1164// ============================================================
1165// /vs-github
1166// ============================================================
1167
1168marketing.get("/vs-github", (c) => {
1169 const user = c.get("user");
1170 return c.html(
1171 <Layout title="vs GitHub — gluecron" user={user}>
1172 <VsGithubPage />
1173 </Layout>,
1174 );
1175});
1176
1177const VsGithubPage: FC = () => (
1178 <>
1179 <style dangerouslySetInnerHTML={{ __html: vsGithubCss }} />
1180 <div class="mkt-root">
1181 <header class="mkt-hero vs-hero">
1182 <div class="eyebrow vs-eyebrow">
1183 <span class="vs-eyebrow-dot" />
1184 The honest comparison
1185 </div>
1186 <h1 class="display vs-hero-title">
1187 GitHub,{" "}
1188 <span class="gradient-text">but kind.</span>
1189 </h1>
1190 <p class="mkt-hero-sub">
1191 GitHub treats AI as a paid sidebar. Failures are your problem.
1192 Every feature you actually need is gated behind Enterprise.
1193 We rebuilt the platform around a simple idea:{" "}
1194 <strong>your tools should work for you, not against you.</strong>
1195 </p>
1196 <div class="vs-hero-stats">
1197 <div class="vs-hero-stat">
1198 <div class="vs-hero-stat-num gradient-text">100%</div>
1199 <div class="vs-hero-stat-lbl">PRs reviewed by AI<br/>(free, every tier)</div>
1200 </div>
1201 <div class="vs-hero-stat">
1202 <div class="vs-hero-stat-num gradient-text">~70%</div>
1203 <div class="vs-hero-stat-lbl">CI failures auto-repaired<br/>before you see them</div>
1204 </div>
1205 <div class="vs-hero-stat">
1206 <div class="vs-hero-stat-num gradient-text">$0</div>
1207 <div class="vs-hero-stat-lbl">Self-host forever<br/>vs $21/user/mo GHE</div>
1208 </div>
1209 <div class="vs-hero-stat">
1210 <div class="vs-hero-stat-num gradient-text">∞</div>
1211 <div class="vs-hero-stat-lbl">CI minutes<br/>vs GitHub's metered tax</div>
1212 </div>
1213 </div>
1214 </header>
1215
1216 {/* ───────── The kill shot ───────── */}
1217 <section class="mkt-section vs-killshot">
1218 <div class="vs-killshot-card">
1219 <div class="vs-killshot-row">
1220 <div class="vs-killshot-col vs-them">
1221 <div class="vs-killshot-label">GitHub</div>
1222 <p class="vs-killshot-quote">
1223 "Push code. Hope it doesn't break. If it breaks,
1224 you deal with it. Want AI? Pay $19/mo. Want SSO?
1225 $21/user/mo. Want decent rulesets? Enterprise tier."
1226 </p>
1227 </div>
1228 <div class="vs-killshot-vs">VS</div>
1229 <div class="vs-killshot-col vs-us">
1230 <div class="vs-killshot-label">Gluecron</div>
1231 <p class="vs-killshot-quote">
1232 "Push code. AI reviews it. AI fixes it.
1233 AI ships it. We tell you about it after.
1234 Self-host free. No seat tax."
1235 </p>
1236 </div>
1237 </div>
1238 </div>
1239 </section>
1240
1241 {/* ───────── Side-by-side feature table ───────── */}
1242 <section class="mkt-section">
1243 <div class="section-header">
1244 <div class="eyebrow">Feature-for-feature</div>
1245 <h2>Everything they have. Plus the parts they didn't build.</h2>
1246 </div>
1247 <div class="vs-table">
1248 <VsRow feat="Smart-HTTP git clone + push" them="✓" us="✓" />
1249 <VsRow feat="Pull requests + review + merge" them="✓" us="✓" />
1250 <VsRow feat="Issues + projects + wiki + gists" them="✓" us="✓" />
1251 <VsRow feat="GitHub Actions equivalent" them="paid minutes" us="self-hosted, unmetered" win />
1252 <VsRow feat="AI code review on every PR" them="Copilot subscription" us="built-in, free" win />
1253 <VsRow feat="Auto-repair of failed CI" them="—" us="✓" win />
1254 <VsRow feat="Spec-to-PR (NL → draft PR)" them="—" us="✓" win />
1255 <VsRow feat="Real-time SSE for logs / comments" them="polling" us="streaming" win />
1256 <VsRow feat="MCP server (Claude / Cursor)" them="—" us="✓ native" win />
1257 <VsRow feat="Required status checks" them="Enterprise tier" us="free, all tiers" win />
1258 <VsRow feat="Repository rulesets" them="Enterprise tier" us="free, all tiers" win />
1259 <VsRow feat="Merge queue" them="Enterprise tier" us="free, all tiers" win />
1260 <VsRow feat="Audit log (per-user + per-repo)" them="Enterprise tier" us="free, all tiers" win />
1261 <VsRow feat="SSO (OIDC)" them="Enterprise tier" us="all tiers" win />
1262 <VsRow feat="Self-host on your hardware" them="$21/user/mo" us="free forever" win />
1263 <VsRow feat="Pre-receive policy enforcement" them="Enterprise rulesets" us="✓" />
1264 <VsRow feat="Branch protection + 2FA + Passkeys" them="✓" us="✓" />
1265 <VsRow feat="Webhooks (HMAC signed)" them="✓" us="✓" />
1266 <VsRow feat="Pages / static hosting" them="✓" us="✓" />
1267 <VsRow feat="Packages registry (npm)" them="✓" us="✓" />
1268 <VsRow feat="Native mobile apps" them="✓" us="PWA only" lose />
1269 <VsRow feat="Marketplace of pre-built actions" them="huge ecosystem" us="building" lose />
1270 <VsRow feat="Public Sponsors w/ payments" them="✓" us="post-launch" lose />
1271 </div>
1272 <p class="vs-table-foot">
1273 We're behind on three things and ahead on twelve. Three of those
1274 twelve are genuinely new categories no one has built before.
1275 </p>
1276 </section>
1277
1278 {/* ───────── The pricing knife ───────── */}
1279 <section class="mkt-section">
1280 <div class="section-header">
1281 <div class="eyebrow">Pricing philosophy</div>
1282 <h2>Stop taxing seats. Start pricing what costs us money.</h2>
1283 <p>
1284 GitHub charges $4-21 per user per month, plus minutes, plus
1285 features. We don't tax seats. We price the AI calls — the
1286 actual cost we incur. Static self-hosters pay nothing.
1287 </p>
1288 </div>
1289 <div class="vs-pricing-grid">
1290 <div class="vs-pricing-them">
1291 <div class="vs-pricing-label">GitHub</div>
1292 <ul class="vs-pricing-list">
1293 <li><span class="vs-pricing-x">✗</span> Free: limited private repos, paid minutes</li>
1294 <li><span class="vs-pricing-x">✗</span> Pro: <strong>$4/user/mo</strong> + paid minutes</li>
1295 <li><span class="vs-pricing-x">✗</span> Team: <strong>$4/user/mo</strong> + everything metered</li>
1296 <li><span class="vs-pricing-x">✗</span> Enterprise: <strong>$21/user/mo</strong> + extras</li>
1297 <li><span class="vs-pricing-x">✗</span> Copilot: <strong>$19/user/mo</strong> on top</li>
1298 <li><span class="vs-pricing-x">✗</span> Self-host: only at Enterprise tier</li>
1299 </ul>
1300 </div>
1301 <div class="vs-pricing-us">
1302 <div class="vs-pricing-label">Gluecron</div>
1303 <ul class="vs-pricing-list">
1304 <li><span class="vs-pricing-check">✓</span> Free: unlimited public, 3 private, AI included</li>
1305 <li><span class="vs-pricing-check">✓</span> Pro: <strong>$12/user/mo</strong> all-in (AI included)</li>
1306 <li><span class="vs-pricing-check">✓</span> Team: <strong>$29/user/mo</strong> unlimited AI + SSO + audit</li>
1307 <li><span class="vs-pricing-check">✓</span> Enterprise: custom + on-prem + DPA</li>
1308 <li><span class="vs-pricing-check">✓</span> CI minutes: <strong>unmetered</strong>, hosted or self</li>
1309 <li><span class="vs-pricing-check">✓</span> Self-host: <strong>$0 forever</strong>, all tiers</li>
1310 </ul>
1311 </div>
1312 </div>
1313 </section>
1314
1315 {/* ───────── Why we exist ───────── */}
1316 <section class="mkt-section">
1317 <div class="section-header">
1318 <div class="eyebrow">Why this exists</div>
1319 <h2>Most code in 2026 is written by AI. Most platforms aren't built for that.</h2>
1320 </div>
1321 <div class="vs-rationale">
1322 <p>
1323 GitHub was designed in 2008 for a world where one human
1324 engineer wrote every commit. The platform's defaults assume
1325 <em> you're the bottleneck</em>. Red X's demand your attention.
1326 Failed checks page you at 3am. Every AI feature is a
1327 $19/user upsell because, for them, AI is a bolt-on.
1328 </p>
1329 <p>
1330 For us, AI is the<strong> default teammate</strong>. It commits
1331 with its own bot identity. It opens PRs. It reviews diffs.
1332 It fixes regressions before you wake up. It tells you what
1333 broke and why. <strong>The platform works for you, not the
1334 other way around.</strong>
1335 </p>
1336 <p>
1337 We're not anti-GitHub. We use it. We came from there. But
1338 after watching engineers spend 30% of their week dealing with
1339 CI failures, infrastructure pain, and Copilot subscriptions
1340 that don't actually help, we got tired of the cruelty —
1341 so we rebuilt the parts that mattered.
1342 </p>
1343 </div>
1344 </section>
1345
1346 {/* ───────── The closing ask ───────── */}
1347 <section class="mkt-cta">
1348 <div class="mkt-cta-card">
1349 <div class="mkt-cta-bg" aria-hidden="true" />
1350 <div class="eyebrow">Try it</div>
1351 <h2 class="mkt-cta-title">
1352 Migrate from GitHub in 60 seconds.<br />
1353 <span class="gradient-text">Stay because it's actually better.</span>
1354 </h2>
1355 <p class="mkt-hero-sub" style="max-width:600px;margin:0 auto var(--s-7)">
1356 Paste a GitHub token, mirror your whole org. Your existing
1357 workflows just work. Your team keeps shipping. Nothing
1358 breaks. Everything gets kinder.
1359 </p>
1360 <div class="mkt-cta-buttons">
1361 <a href="/register" class="btn btn-primary btn-xl">
1362 Start free
1363 <span aria-hidden="true">{"→"}</span>
1364 </a>
1365 <a href="/import" class="btn btn-ghost btn-xl">
1366 Migrate from GitHub
1367 </a>
1368 </div>
1369 </div>
1370 </section>
1371 </div>
1372 </>
1373);
1374
1375const VsRow: FC<{
1376 feat: string;
1377 them: string;
1378 us: string;
1379 win?: boolean;
1380 lose?: boolean;
1381}> = ({ feat, them, us, win, lose }) => (
1382 <div
1383 class={`vs-row${win ? " vs-row-win" : ""}${lose ? " vs-row-lose" : ""}`}
1384 >
1385 <div class="vs-row-feat">{feat}</div>
1386 <div class="vs-row-them">{them}</div>
1387 <div class="vs-row-us">{us}</div>
1388 </div>
1389);
1390
1391const vsGithubCss = sharedMktCss + `
1392 /* Hero with even bigger drama */
1393 .vs-hero { padding-bottom: var(--s-8); }
1394 .vs-eyebrow { color: var(--accent); }
1395 .vs-eyebrow-dot {
1396 width: 7px; height: 7px;
1397 border-radius: 50%;
1398 background: var(--accent);
1399 box-shadow: 0 0 0 0 rgba(140,109,255,0.55);
1400 animation: vs-pulse 1.8s ease-out infinite;
1401 }
1402 @keyframes vs-pulse {
1403 0% { box-shadow: 0 0 0 0 rgba(140,109,255,0.55); }
1404 70% { box-shadow: 0 0 0 10px rgba(140,109,255,0); }
1405 100% { box-shadow: 0 0 0 0 rgba(140,109,255,0); }
1406 }
1407 .vs-hero-title {
1408 font-size: clamp(40px, 8vw, 100px);
1409 line-height: 0.98;
1410 letter-spacing: -0.04em;
1411 font-weight: 700;
1412 margin: 0 0 var(--s-5);
1413 }
1414 .vs-hero-stats {
1415 display: grid;
1416 grid-template-columns: repeat(4, 1fr);
1417 gap: var(--s-6);
1418 max-width: 880px;
1419 margin: var(--s-10) auto 0;
1420 text-align: center;
1421 }
1422 .vs-hero-stat-num {
1423 font-family: var(--font-display);
1424 font-size: clamp(36px, 4.5vw, 56px);
1425 font-weight: 700;
1426 line-height: 1;
1427 letter-spacing: -0.03em;
1428 margin-bottom: var(--s-2);
1429 }
1430 .vs-hero-stat-lbl {
1431 font-family: var(--font-mono);
1432 font-size: 11px;
1433 text-transform: uppercase;
1434 letter-spacing: 0.12em;
1435 color: var(--text-faint);
1436 line-height: 1.5;
1437 }
1438 @media (max-width: 720px) {
1439 .vs-hero-stats { grid-template-columns: repeat(2, 1fr); gap: var(--s-5); }
1440 }
1441
1442 /* The kill-shot card */
1443 .vs-killshot { margin-top: var(--s-4); }
1444 .vs-killshot-card {
1445 max-width: 980px;
1446 margin: 0 auto;
1447 padding: var(--s-10) var(--s-7);
1448 background:
1449 linear-gradient(135deg, rgba(140,109,255,0.06), transparent 50%, rgba(54,197,214,0.04)),
1450 var(--bg-elevated);
1451 border: 1px solid var(--border-strong);
1452 border-radius: var(--r-2xl);
1453 box-shadow: var(--elev-2);
1454 }
1455 .vs-killshot-row {
1456 display: grid;
1457 grid-template-columns: 1fr auto 1fr;
1458 gap: var(--s-7);
1459 align-items: center;
1460 }
1461 .vs-killshot-col { padding: var(--s-4); }
1462 .vs-killshot-label {
1463 font-family: var(--font-mono);
1464 font-size: 11px;
1465 text-transform: uppercase;
1466 letter-spacing: 0.16em;
1467 margin-bottom: var(--s-3);
1468 }
1469 .vs-them .vs-killshot-label { color: var(--red); }
1470 .vs-us .vs-killshot-label { color: var(--green); }
1471 .vs-killshot-quote {
1472 font-size: 17px;
1473 line-height: 1.55;
1474 margin: 0;
1475 color: var(--text);
1476 font-style: italic;
1477 }
1478 .vs-them .vs-killshot-quote { color: var(--text-muted); }
1479 .vs-killshot-vs {
1480 font-family: var(--font-display);
1481 font-size: 36px;
1482 font-weight: 700;
1483 color: var(--text-faint);
1484 letter-spacing: -0.02em;
1485 }
1486 @media (max-width: 720px) {
1487 .vs-killshot-row { grid-template-columns: 1fr; gap: var(--s-3); text-align: left; }
1488 .vs-killshot-vs { text-align: center; }
1489 }
1490
1491 /* Comparison table */
1492 .vs-table {
1493 max-width: 1080px;
1494 margin: 0 auto;
1495 border: 1px solid var(--border);
1496 border-radius: var(--r-lg);
1497 overflow: hidden;
1498 background: var(--bg-elevated);
1499 }
1500 .vs-row {
1501 display: grid;
1502 grid-template-columns: 1.6fr 1fr 1fr;
1503 align-items: center;
1504 padding: 14px 22px;
1505 border-bottom: 1px solid var(--border-subtle);
1506 font-size: var(--t-sm);
1507 transition: background var(--t-fast) var(--ease);
1508 }
1509 .vs-row:last-child { border-bottom: none; }
1510 .vs-row:hover { background: var(--bg-hover); }
1511 .vs-row-feat { color: var(--text-strong); font-weight: 500; }
1512 .vs-row-them, .vs-row-us {
1513 text-align: center;
1514 font-family: var(--font-mono);
1515 font-size: 12px;
1516 }
1517 .vs-row-them { color: var(--text-muted); }
1518 .vs-row-us { color: var(--text); }
1519 .vs-row-win .vs-row-us {
1520 color: var(--accent);
1521 font-weight: 600;
1522 }
1523 .vs-row-win .vs-row-feat::after {
1524 content: 'WIN';
1525 margin-left: 10px;
1526 padding: 2px 7px;
1527 border-radius: 4px;
1528 background: var(--accent-gradient-faint);
1529 border: 1px solid rgba(140,109,255,0.30);
1530 color: var(--accent);
1531 font-family: var(--font-mono);
1532 font-size: 9px;
1533 letter-spacing: 0.12em;
1534 font-weight: 700;
1535 vertical-align: 1px;
1536 }
1537 .vs-row-lose .vs-row-us {
1538 color: var(--text-faint);
1539 }
1540 .vs-row-lose .vs-row-feat::after {
1541 content: 'GAP';
1542 margin-left: 10px;
1543 padding: 2px 7px;
1544 border-radius: 4px;
1545 background: rgba(251,191,36,0.10);
1546 border: 1px solid rgba(251,191,36,0.30);
1547 color: var(--yellow);
1548 font-family: var(--font-mono);
1549 font-size: 9px;
1550 letter-spacing: 0.12em;
1551 font-weight: 700;
1552 vertical-align: 1px;
1553 }
1554 .vs-table-foot {
1555 text-align: center;
1556 color: var(--text-muted);
1557 font-size: var(--t-sm);
1558 margin-top: var(--s-5);
1559 max-width: 720px;
1560 margin-left: auto;
1561 margin-right: auto;
1562 line-height: 1.55;
1563 }
1564 @media (max-width: 720px) {
1565 .vs-row { grid-template-columns: 1fr 90px 90px; padding: 12px 14px; }
1566 .vs-row-win .vs-row-feat::after,
1567 .vs-row-lose .vs-row-feat::after { display: none; }
1568 }
1569
1570 /* Pricing knife */
1571 .vs-pricing-grid {
1572 display: grid;
1573 grid-template-columns: 1fr 1fr;
1574 gap: var(--s-5);
1575 max-width: 980px;
1576 margin: 0 auto;
1577 }
1578 .vs-pricing-them, .vs-pricing-us {
1579 padding: var(--s-7);
1580 border: 1px solid var(--border);
1581 border-radius: var(--r-lg);
1582 background: var(--bg-elevated);
1583 }
1584 .vs-pricing-them {
1585 border-color: rgba(248,113,113,0.20);
1586 }
1587 .vs-pricing-us {
1588 border-color: rgba(140,109,255,0.30);
1589 box-shadow: 0 0 0 1px rgba(140,109,255,0.20), var(--elev-2);
1590 background:
1591 linear-gradient(180deg, rgba(140,109,255,0.04), transparent 60%),
1592 var(--bg-elevated);
1593 }
1594 .vs-pricing-label {
1595 font-family: var(--font-display);
1596 font-size: 22px;
1597 font-weight: 700;
1598 margin-bottom: var(--s-5);
1599 letter-spacing: -0.02em;
1600 }
1601 .vs-pricing-them .vs-pricing-label { color: var(--text-muted); }
1602 .vs-pricing-us .vs-pricing-label { color: var(--accent); }
1603 .vs-pricing-list {
1604 list-style: none;
1605 margin: 0;
1606 padding: 0;
1607 display: flex;
1608 flex-direction: column;
1609 gap: 10px;
1610 font-size: var(--t-sm);
1611 line-height: 1.55;
1612 }
1613 .vs-pricing-list li {
1614 display: flex;
1615 gap: 10px;
1616 align-items: flex-start;
1617 }
1618 .vs-pricing-x {
1619 color: var(--red);
1620 font-weight: 700;
1621 flex-shrink: 0;
1622 }
1623 .vs-pricing-check {
1624 color: var(--green);
1625 font-weight: 700;
1626 flex-shrink: 0;
1627 }
1628 .vs-pricing-them li { color: var(--text-muted); }
1629 .vs-pricing-us li { color: var(--text); }
1630 @media (max-width: 720px) {
1631 .vs-pricing-grid { grid-template-columns: 1fr; }
1632 }
1633
1634 /* Rationale prose */
1635 .vs-rationale {
1636 max-width: 720px;
1637 margin: 0 auto;
1638 }
1639 .vs-rationale p {
1640 color: var(--text);
1641 font-size: var(--t-md);
1642 line-height: 1.7;
1643 margin: 0 0 var(--s-4);
1644 }
1645 .vs-rationale strong { color: var(--text-strong); font-weight: 600; }
1646 .vs-rationale em { color: var(--text-strong); font-style: italic; }
1647`;
1648
b0148e9Claude1649export default marketing;