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-pro.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-pro.tsxBlame1454 lines · 1 contributor
f8e5feaccanty labs1/**
2 * LandingProPage — comprehensive marketing homepage.
3 *
4 * Design: Stripe/Linear aesthetic — pure white, Inter typography,
5 * single indigo accent (#4353c9), generous whitespace. No cyberpunk
6 * elements (no gradient text fills, no glowing orbs, no pulsing dots,
7 * no ticker animations). Sections alternate #ffffff / #fafafb.
8 *
9 * Self-contained standalone document (own <html>, own CSS, own fonts)
10 * — does NOT use the dark app Layout so the marketing surface stays
11 * pristine. CSS scoped under .lp-* to avoid any bleed into the app.
12 *
13 * Data: accepts publicStats (L4) and liveFeed (L3 demo activity) from
14 * the route. Both are optional — page degrades gracefully when absent.
15 */
16
17import type { FC } from "hono/jsx";
18import type { PublicStats } from "../lib/public-stats";
19import type {
20 QueuedAiBuildIssue,
21 RecentAutoMerge,
22 RecentAiReview,
23 DemoActivityEntry,
24} from "../lib/demo-activity";
25import { DEMO_USERNAME } from "../lib/demo-seed";
26
27// ─── prop interfaces ───────────────────────────────────────────────
28
29export interface LandingProLiveFeed {
30 queued: QueuedAiBuildIssue[];
31 merges: RecentAutoMerge[];
32 reviews: RecentAiReview[];
33 reviewCount: number;
34 feed: DemoActivityEntry[];
35}
36
37export interface LandingProProps {
38 stats?: { publicRepos?: number; users?: number };
39 publicStats?: PublicStats | null;
40 liveFeed?: LandingProLiveFeed | null;
41}
42
43// ─── helper: relative time ─────────────────────────────────────────
44
45export function relTime(
46 value: string | Date | null | undefined,
47 now: number = Date.now()
48): string {
49 if (value === null || value === undefined) return "just now";
50 const t = value instanceof Date ? value.getTime() : new Date(value).getTime();
51 if (!Number.isFinite(t)) return "just now";
52 const d = now - t;
53 if (d < 0) return "just now";
54 const s = Math.floor(d / 1000);
55 if (s < 60) return "just now";
56 const m = Math.floor(s / 60);
57 if (m < 60) return `${m}m ago`;
58 const h = Math.floor(m / 60);
59 if (h < 24) return `${h}h ago`;
60 return `${Math.floor(h / 24)}d ago`;
61}
62
63// ─── page ──────────────────────────────────────────────────────────
64
65export const LandingProPage: FC<LandingProProps> = ({
66 publicStats,
67 liveFeed,
68} = {}) => {
69 const title = "Gluecron — The AI-native git platform";
70 const desc =
71 "The git platform that does the work. AI code review on every PR, secrets scanned at push, features built from plain-English issues. Self-hosted, git-native, Claude-first.";
72
73 const liveQueued = liveFeed?.queued ?? [];
74 const liveMerges = liveFeed?.merges ?? [];
75 const liveReviews = liveFeed?.reviews ?? [];
76 const liveReviewCount = liveFeed?.reviewCount ?? 0;
77 const liveEntries = liveFeed?.feed ?? [];
78
79 return (
80 <html lang="en">
81 <head>
82 <meta charset="UTF-8" />
83 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
84 <meta name="theme-color" content="#ffffff" />
85 <title>{title}</title>
86 <meta name="description" content={desc} />
87 <meta property="og:title" content={title} />
88 <meta property="og:description" content={desc} />
89 <meta property="og:type" content="website" />
90 <meta name="twitter:card" content="summary_large_image" />
91 <link rel="icon" type="image/svg+xml" href="/icon.svg" />
92 <link rel="preconnect" href="https://fonts.googleapis.com" />
93 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="" />
94 <link
95 rel="stylesheet"
96 href="https://fonts.googleapis.com/css2?family=Inter:wght@400;450;500;600&family=Inter+Tight:wght@600;700;800&family=JetBrains+Mono:wght@400;500&display=swap"
97 />
98 <style dangerouslySetInnerHTML={{ __html: css }} />
99 </head>
100 <body>
101
102 {/* ── Nav ────────────────────────────────────────────────── */}
103 <header class="lp-nav" id="lp-nav">
104 <div class="lp-nav-in">
105 <a href="/" class="lp-logo" aria-label="Gluecron home">
106 <span class="lp-logo-mark" aria-hidden="true" />
107 gluecron
108 </a>
109 <nav class="lp-nav-links" aria-label="Primary">
110 <a href="#platform">Platform</a>
111 <a href="#ai">AI suite</a>
112 <a href="#security">Security</a>
113 <a href="/pricing">Pricing</a>
114 <a href="/explore">Explore</a>
115 </nav>
116 <div class="lp-nav-ctas">
117 <a href="/login" class="lp-btn lp-btn-ghost">Sign in</a>
118 <a href="/register" class="lp-btn lp-btn-solid">Start building</a>
119 </div>
120 </div>
121 </header>
122
123 {/* ── Hero ───────────────────────────────────────────────── */}
124 <section class="lp-hero">
125 <div class="lp-wrap lp-hero-in">
126 <div class="lp-hero-text">
127 <div class="lp-kicker">The AI-native git platform</div>
128 <h1 class="lp-h1">
129 The platform<br />that does the work.
130 </h1>
131 <p class="lp-hero-sub">
132 AI code review on every PR. Secrets scanned at push.
133 Features built from plain-English issues.
134 Gluecron closes the loop from spec to deployed code —
135 while you focus on what matters.
136 </p>
137
138 <div class="lp-hero-ctas">
139 <a href="/register" class="lp-btn lp-btn-solid lp-btn-lg">
140 Sign up free
141 </a>
142 <a href="/import" class="lp-btn lp-btn-outline lp-btn-lg">
143 Migrate from GitHub
144 </a>
145 </div>
146 <div class="lp-hero-links">
147 <a href="/demo">Try the live demo</a>
148 <span aria-hidden="true">·</span>
149 <a href="/vs-github">Compare to GitHub</a>
150 <span aria-hidden="true">·</span>
151 <a href="/pricing">See pricing</a>
152 </div>
153
154 {publicStats && (
155 <dl class="lp-hero-stats">
156 <div class="lp-hero-stat">
157 <dt>{publicStats.weeklyPrsAutoMerged.toLocaleString()}</dt>
158 <dd>PRs auto-merged this week</dd>
159 </div>
160 <div class="lp-hero-stat">
161 <dt>{publicStats.weeklyIssuesBuiltByAi.toLocaleString()}</dt>
162 <dd>issues built by AI</dd>
163 </div>
164 <div class="lp-hero-stat">
165 <dt>{`~${Math.round(publicStats.weeklyHoursSaved)}h`}</dt>
166 <dd>saved by AI this week</dd>
167 </div>
168 <div class="lp-hero-stat">
169 <dt>{publicStats.weeklyDeploysShipped.toLocaleString()}</dt>
170 <dd>deploys shipped</dd>
171 </div>
172 </dl>
173 )}
174 </div>
175
176 {/* Product mock card */}
177 <div class="lp-hero-card" aria-hidden="true">
178 <div class="lp-hc-bar">
179 <span class="lp-hc-dot" /><span class="lp-hc-dot" /><span class="lp-hc-dot" />
180 <span class="lp-hc-path">gluecron.com / your-org / api · PR #128</span>
181 </div>
182 <div class="lp-hc-body">
183 <div class="lp-hc-pr-row">
184 <span class="lp-badge lp-badge-merged">Merged</span>
185 <span class="lp-hc-title">Fix race condition in token refresh</span>
186 </div>
187 <div class="lp-hc-review">
188 <div class="lp-hc-ava">C</div>
189 <div class="lp-hc-rev">
190 <div class="lp-hc-rev-head">Claude review · <em>approved</em></div>
191 <div class="lp-hc-rev-body">
192 Mutex now guards the refresh path. The double-fetch under contention is resolved. Gates green. Auto-merging.
193 </div>
194 </div>
195 </div>
196 <div class="lp-hc-checks">
197 <span class="lp-check lp-check-ok">✓ gate: security</span>
198 <span class="lp-check lp-check-ok">✓ gate: tests</span>
199 <span class="lp-check lp-check-ok">✓ review: Claude</span>
200 <span class="lp-check lp-check-ok">✓ deploy: live</span>
201 </div>
202 <div class="lp-hc-meta">
203 Deployed to <code>api.your-org.com</code> · 4.1s · push-to-live
204 </div>
205 </div>
206 </div>
207 </div>
208 </section>
209
210 {/* ── Trust strip ────────────────────────────────────────── */}
211 <div class="lp-trust-strip">
212 <div class="lp-wrap lp-trust-in">
213 <span>Self-hosted on your hardware</span>
214 <span class="lp-sep" aria-hidden="true" />
215 <span>Git-native · Smart HTTP + SSH</span>
216 <span class="lp-sep" aria-hidden="true" />
217 <span>Claude-powered AI review</span>
218 <span class="lp-sep" aria-hidden="true" />
219 <span>MCP-native for Claude Code, Cursor</span>
220 <span class="lp-sep" aria-hidden="true" />
221 <span>Free to start</span>
222 </div>
223 </div>
224
225 {/* ── Live-now autopilot ─────────────────────────────────── */}
226 <section class="lp-sec lp-sec-soft" aria-labelledby="lp-live-h">
227 <div class="lp-wrap">
228 <div class="lp-sec-head">
229 <div class="lp-kicker">
230 <span class="lp-live-dot" aria-hidden="true">●</span> Live now
231 </div>
232 <h2 id="lp-live-h" class="lp-h2">
233 Claude is working on demo repos as you read this.
234 </h2>
235 <p class="lp-sub">
236 Real data from the public <code>{DEMO_USERNAME}/*</code> repos.
237 Refreshes every 30 seconds.
238 </p>
239 </div>
240
241 <div class="lp-live-grid" data-livenow-grid>
242 <LiveCard title="Issues queued for AI">
243 <ul class="lp-live-list" data-livecard="queued">
244 {liveQueued.length === 0 ? (
245 <li class="lp-live-empty">Quiet right now.</li>
246 ) : (
247 liveQueued.slice(0, 3).map((i) => (
248 <li class="lp-live-row" data-row-id={`queued|${i.repo}|${i.number}`}>
249 <a href={`/${DEMO_USERNAME}/${i.repo}/issues/${i.number}`} class="lp-live-link">
250 <span class="lp-live-num">#{i.number}</span>
251 {" "}{i.title}
252 </a>
253 <span class="lp-live-meta">{i.repo}</span>
254 </li>
255 ))
256 )}
257 </ul>
258 </LiveCard>
259
260 <LiveCard title="Recently merged by AI">
261 <ul class="lp-live-list" data-livecard="merges">
262 {liveMerges.length === 0 ? (
263 <li class="lp-live-empty">No auto-merges in the last 24h.</li>
264 ) : (
265 liveMerges.slice(0, 3).map((m) => (
266 <li class="lp-live-row" data-row-id={`merges|${m.repo}|${m.number}`}>
267 <a href={`/${DEMO_USERNAME}/${m.repo}/pulls/${m.number}`} class="lp-live-link">
268 <span class="lp-live-num">#{m.number}</span>
269 {" "}{m.title}
270 </a>
271 <span class="lp-live-meta">
272 {m.repo} ·{" "}
273 <span data-rel={m.mergedAt instanceof Date ? m.mergedAt.toISOString() : String(m.mergedAt)}>
274 {relTime(m.mergedAt)}
275 </span>
276 </span>
277 </li>
278 ))
279 )}
280 </ul>
281 </LiveCard>
282
283 <LiveCard title="AI reviews posted">
284 <div class="lp-live-bignum">
285 <span data-livecard-count="reviews" data-tick-target={String(liveReviewCount)}>
286 {liveReviewCount}
287 </span>
288 <span class="lp-live-bignum-label">today</span>
289 </div>
290 <ul class="lp-live-list" data-livecard="reviews">
291 {liveReviews.length === 0 ? (
292 <li class="lp-live-empty">No reviews in the last 24h.</li>
293 ) : (
294 liveReviews.slice(0, 2).map((r) => (
295 <li class="lp-live-row" data-row-id={`reviews|${r.repo}|${r.prNumber}`}>
296 <a href={`/${DEMO_USERNAME}/${r.repo}/pulls/${r.prNumber}`} class="lp-live-link">
297 <span class="lp-live-num">#{r.prNumber}</span>
298 {" "}{r.commentSnippet}
299 </a>
300 <span class="lp-live-meta">{r.repo}</span>
301 </li>
302 ))
303 )}
304 </ul>
305 </LiveCard>
306
307 <LiveCard title="Activity feed">
308 <ul class="lp-live-list" data-livecard="feed">
309 {liveEntries.length === 0 ? (
310 <li class="lp-live-empty">Quiet right now — check back in a minute.</li>
311 ) : (
312 liveEntries.slice(0, 6).map((e) => {
313 const path = e.ref.type === "pr" ? "pulls" : "issues";
314 const kindLabel =
315 e.kind === "auto_merge.merged" ? "auto-merged" :
316 e.kind === "ai_build.dispatched" ? "AI-built" :
317 "AI review";
318 const id = `${e.kind}|${e.repo}|${e.ref.type}|${e.ref.number}`;
319 return (
320 <li class="lp-live-feedrow" data-row-id={id}>
321 <span class={`lp-feed-kind lp-feed-kind-${e.kind.replace(/\./g, "-")}`}>
322 {kindLabel}
323 </span>
324 {" "}
325 <a
326 class="lp-live-link"
327 href={`/${DEMO_USERNAME}/${e.repo}/${path}/${e.ref.number}`}
328 >
329 {e.repo} #{e.ref.number}
330 </a>
331 {" "}
332 <span
333 class="lp-live-rel"
334 data-rel={e.at instanceof Date ? e.at.toISOString() : String(e.at)}
335 >
336 {relTime(e.at)}
337 </span>
338 </li>
339 );
340 })
341 )}
342 </ul>
343 </LiveCard>
344 </div>
345
346 <div class="lp-live-cta">
347 <a href="/register" class="lp-btn lp-btn-solid">Sign up free</a>
348 <a href="/demo" class="lp-text-link">Open the live demo →</a>
349 </div>
350 </div>
351 <script dangerouslySetInnerHTML={{ __html: liveNowJs }} />
352 </section>
353
354 {/* ── Platform breadth ───────────────────────────────────── */}
355 <section class="lp-sec" id="platform" aria-labelledby="lp-platform-h">
356 <div class="lp-wrap">
357 <div class="lp-sec-head">
358 <div class="lp-kicker">The platform</div>
359 <h2 id="lp-platform-h" class="lp-h2">
360 Everything your team needs.<br />Nothing you don't.
361 </h2>
362 <p class="lp-sub">
363 Gluecron ships the surfaces GitHub charges extra for — and the
364 ones it never built. AI is a first-class contributor, not a plugin.
365 </p>
366 </div>
367
368 <div class="lp-platform-grid">
369 <PlatformGroup title="Code hosting">
370 {["Git Smart HTTP · SSH keys", "Forks · Stars · Topics", "Templates · Mirroring", "Releases · Protected tags", "Push policy rulesets", "Commit signature verify"]}
371 </PlatformGroup>
372 <PlatformGroup title="Collaboration">
373 {["Issues · PRs · Inline review", "Draft PRs · Merge queues", "Discussions · Wikis", "Projects / kanban", "Gists · Reactions", "Mentions · Notifications"]}
374 </PlatformGroup>
375 <PlatformGroup title="AI suite">
376 {["Spec-to-PR from plain English", "AI code review on every PR", "AI auto-merge when gates pass", "PR triage · Issue triage", "Sleep Mode — ships overnight", "AI changelog · AI test stubs"]}
377 </PlatformGroup>
378 <PlatformGroup title="Security">
379 {["Secret scanner (15 patterns)", "AI semantic security review", "GateTest integration", "Dependency CVE alerts", "2FA · Passkeys · SAML SSO", "Audit log (100% coverage)"]}
380 </PlatformGroup>
381 <PlatformGroup title="CI / CD">
382 {["Workflow runner (YAML-native)", "Cron triggers · Secrets", "Branch protection · Gates", "Required status checks", "Auto-repair on gate failure", "Protected tags enforcement"]}
383 </PlatformGroup>
384 <PlatformGroup title="Platform">
385 {["Organizations · Teams · Roles", "npm package registry", "Pages / static hosting", "App marketplace", "Protected environments", "Billing · Quotas"]}
386 </PlatformGroup>
387 <PlatformGroup title="Observability">
388 {["DORA metrics dashboard", "Developer velocity", "Repository health score", "Hot files heatmap", "Traffic analytics", "Org insights"]}
389 </PlatformGroup>
390 <PlatformGroup title="Integrations">
391 {["REST API v2 · GraphQL", "MCP server (Claude, Cursor)", "Official CLI · VS Code ext", "Webhooks (HMAC-signed)", "OAuth provider · GitHub Apps", "GitHub import (single + bulk)"]}
392 </PlatformGroup>
393 </div>
394 </div>
395 </section>
396
397 {/* ── Developer painkiller ───────────────────────────────── */}
398 <section class="lp-sec lp-sec-soft">
399 <div class="lp-wrap">
400 <div class="lp-sec-head">
401 <div class="lp-kicker">Why developers switch</div>
402 <h2 class="lp-h2">Pain out. Productivity in.</h2>
403 <p class="lp-sub">
404 Every friction point in modern development has a specific answer in Gluecron.
405 </p>
406 </div>
407
408 <div class="lp-pain-table">
409 <PainRow
410 problem="Waiting on CI to queue before a gate runs"
411 fix="Gates fire at the moment of push — before the branch even moves"
412 />
413 <PainRow
414 problem="Manual code review as a bottleneck"
415 fix="AI reviews every PR in under 30 seconds, line-level, with risk flags"
416 />
417 <PainRow
418 problem="A failed gate halts your whole day"
419 fix="Auto-repair tries to fix it, re-runs the gate, and continues — you may never see it"
420 />
421 <PainRow
422 problem="AI as a sidebar you talk at"
423 fix="Claude has a bot account, makes commits, and appears in your git history like a teammate"
424 />
425 <PainRow
426 problem="Platform lock-in and surprise bills"
427 fix="Self-hosted, single Bun binary, git-native — run it on a $6 VPS"
428 />
429 <PainRow
430 problem="Dependency CVEs discovered after merge"
431 fix="CVE scanner runs on every push touching a manifest; opens issues automatically"
432 />
433 </div>
434 </div>
435 </section>
436
437 {/* ── AI showcase ────────────────────────────────────────── */}
438 <section class="lp-sec" id="ai" aria-labelledby="lp-ai-h">
439 <div class="lp-wrap">
440 <div class="lp-sec-head">
441 <div class="lp-kicker">AI-native, not AI-bolted-on</div>
442 <h2 id="lp-ai-h" class="lp-h2">
443 Claude does the work.<br />You stay in control.
444 </h2>
445 <p class="lp-sub">
446 Every AI action is attributed, auditable, and reversible.
447 The manual path is always one click away.
448 </p>
449 </div>
450
451 <div class="lp-ai-features">
452
453 <AiFeature
454 n="01"
455 title="Spec-to-PR"
456 body="Describe a feature in plain English — or just label an issue ai:build. Claude opens a branch, writes the change, and submits a draft PR against your gates. Spec to PR in under 90 seconds."
457 link={{ href: "/demo", label: "Try on a live repo" }}
458 mock={[
459 { icon: "●", color: "muted", text: "issue #42 labelled ai:build" },
460 { icon: "↗", color: "brand", text: "branch opened: feat/add-auth-provider" },
461 { icon: "↗", color: "brand", text: "PR #43 created — diff ready for review" },
462 { icon: "✓", color: "green", text: "gates queued — waiting on your approval" },
463 ]}
464 />
465
466 <AiFeature
467 n="02"
468 title="AI code review on every PR"
469 body="The moment a PR opens, Claude reviews the diff: line-level comments, security flags, logic issues, and a verdict. Under 30 seconds. Every PR, every time, before a human has to look."
470 link={{ href: "/vs-github", label: "Compare to Copilot" }}
471 mock={[
472 { icon: "C", color: "brand", text: "Claude review · PR #128" },
473 { icon: " ", color: "muted", text: "auth.ts:84 — mutex guards refresh path ✓" },
474 { icon: " ", color: "muted", text: "No security issues found" },
475 { icon: "✓", color: "green", text: "approved — auto-merging" },
476 ]}
477 reverse
478 />
479
480 <AiFeature
481 n="03"
482 title="Sleep Mode"
483 body="Enable Sleep Mode and set your wake-up hour. Gluecron's autopilot works overnight: merging ready PRs, building labelled issues, repairing failed gates, fixing secrets. You wake up to a digest of what shipped."
484 link={{ href: "/sleep-mode", label: "See Sleep Mode" }}
485 mock={[
486 { icon: "✉", color: "brand", text: "Your overnight digest — 9:00 AM" },
487 { icon: "✓", color: "green", text: "3 PRs auto-merged" },
488 { icon: "✓", color: "green", text: "1 feature built from issue #37" },
489 { icon: "✓", color: "green", text: "2 secrets auto-repaired before push" },
490 ]}
491 />
492
493 <AiFeature
494 n="04"
495 title="Auto-repair on gate failure"
496 body="When a gate fails, auto-repair runs: it reads the failure, drafts a fix, commits it with a bot account, and re-runs the gate. If it can't fix it, it opens an incident issue with a root-cause summary. You see the problem only if the robot couldn't solve it."
497 link={{ href: "/demo", label: "Watch a live repair" }}
498 mock={[
499 { icon: "✗", color: "red", text: "gate: tests — 2 failing" },
500 { icon: "↗", color: "brand", text: "auto-repair: reading failure log" },
501 { icon: "↗", color: "brand", text: "patch drafted — +9 −3" },
502 { icon: "✓", color: "green", text: "gates re-run — all 412 passing" },
503 ]}
504 reverse
505 />
506
507 </div>
508 </div>
509 </section>
510
511 {/* ── Security ───────────────────────────────────────────── */}
512 <section class="lp-sec lp-sec-soft" id="security" aria-labelledby="lp-sec-h">
513 <div class="lp-wrap">
514 <div class="lp-sec-head">
515 <div class="lp-kicker">Security</div>
516 <h2 id="lp-sec-h" class="lp-h2">Secure by default at every layer.</h2>
517 <p class="lp-sub">
518 Security isn't a settings toggle. On Gluecron it runs automatically
519 on every push, every PR, and every dependency update.
520 </p>
521 </div>
522
523 <div class="lp-security-grid">
524 <SecurityCard
525 title="Push-time secret scanner"
526 body="15 regex patterns + AI semantic scan runs before the branch moves. AWS keys, GitHub tokens, private keys, database URLs — stopped cold."
527 />
528 <SecurityCard
529 title="AI security review"
530 body="Claude reviews every diff for SSRF, SQL injection, XSS, and unsafe deserialization patterns. Line-level findings posted as PR comments."
531 />
532 <SecurityCard
533 title="Dependency CVE alerts"
534 body="Scans package.json, go.mod, Cargo.toml, requirements.txt, and Gemfile on every push. Critical findings open issues automatically."
535 />
536 <SecurityCard
537 title="Push policy rulesets"
538 body="Enforce commit message patterns, block file paths, cap file sizes, and forbid force-pushes — enforced at the HTTP layer, not advisory."
539 />
540 <SecurityCard
541 title="Commit signature verification"
542 body="GPG and SSH key registration with Verified badges on every commit. Full OpenPGP packet parsing, SSHSIG support, fingerprint matching."
543 />
544 <SecurityCard
545 title="Enterprise-grade auth"
546 body="TOTP 2FA, passkeys (WebAuthn), SAML/OIDC SSO (Okta, Azure AD, Google Workspace), personal access tokens, OAuth provider."
547 />
548 </div>
549 </div>
550 </section>
551
552 {/* ── For teams ──────────────────────────────────────────── */}
553 <section class="lp-sec">
554 <div class="lp-wrap">
555 <div class="lp-sec-head">
556 <div class="lp-kicker">For teams</div>
557 <h2 class="lp-h2">Built for organisations, not just solo developers.</h2>
558 <p class="lp-sub">
559 Everything a growing engineering team needs — including the parts
560 enterprise platforms charge separately for.
561 </p>
562 </div>
563
564 <div class="lp-teams-grid">
565 <TeamCard
566 title="Organizations &amp; teams"
567 items={[
568 "Org-owned repositories",
569 "Team-based CODEOWNERS (@org/team)",
570 "Role-based access (read / write / admin)",
571 "Per-repo collaborator invites",
572 "Org-wide secrets manager (AES-256-GCM)",
573 ]}
574 />
575 <TeamCard
576 title="Enterprise auth"
577 items={[
578 "SAML/OIDC SSO (Okta, Azure AD, Google)",
579 "TOTP 2FA + recovery codes",
580 "Passkeys / WebAuthn",
581 "Email-domain allowlists",
582 "Auto-provision on first SSO login",
583 ]}
584 />
585 <TeamCard
586 title="Compliance &amp; audit"
587 items={[
588 "100% audit log coverage",
589 "Per-repo + org-level audit views",
590 "Signed commits with Verified badges",
591 "Protected environments (reviewer-gated)",
592 "DORA metrics for engineering health",
593 ]}
594 />
595 <TeamCard
596 title="Developer insights"
597 items={[
598 "DORA metrics (deploy freq, lead time, MTTR)",
599 "Developer velocity dashboard",
600 "Repository health score (0–100)",
601 "Hot files heatmap (churn-based risk tiers)",
602 "Org-wide green rate and PR activity",
603 ]}
604 />
605 </div>
606 </div>
607 </section>
608
609 {/* ── Developer ecosystem ────────────────────────────────── */}
610 <section class="lp-sec lp-sec-soft">
611 <div class="lp-wrap">
612 <div class="lp-sec-head">
613 <div class="lp-kicker">Developer ecosystem</div>
614 <h2 class="lp-h2">Meet developers where they work.</h2>
615 </div>
616
617 <div class="lp-eco-grid">
618 <EcoCard
619 title="Official CLI"
620 sub="gluecron repo ls · gluecron issues · gluecron gql"
621 body="A Bun-compiled single binary. Login, repo CRUD, issue listing, GraphQL queries, and server info from your terminal."
622 link="/install"
623 />
624 <EcoCard
625 title="VS Code extension"
626 sub="Explain · Semantic search · Generate tests · Open on web"
627 body="Four commands wired to your active Gluecron remote. AI explain-this-file and test generation without leaving the editor."
628 link="/vscode"
629 />
630 <EcoCard
631 title="MCP server"
632 sub="Claude Code · Claude Desktop · Cursor"
633 body="Model Context Protocol tools for repo search, file reads, issue and PR management — all natively available to Claude and Cursor."
634 link="/mcp"
635 />
636 <EcoCard
637 title="Full API surface"
638 sub="REST v2 · GraphQL · Webhooks"
639 body="A comprehensive REST API v2, a GraphQL mirror, HMAC-signed webhook delivery, and an OAuth 2.0 provider for third-party apps."
640 link="/api"
641 />
642 </div>
643 </div>
644 </section>
645
646 {/* ── Comparison ─────────────────────────────────────────── */}
647 <section class="lp-sec">
648 <div class="lp-wrap">
649 <div class="lp-sec-head">
650 <div class="lp-kicker">vs the incumbent</div>
651 <h2 class="lp-h2">Everything GitHub charges for.<br />And the parts they never built.</h2>
652 </div>
653
654 <div class="lp-compare">
655 <div class="lp-compare-head" aria-hidden="true">
656 <div />
657 <div>GitHub</div>
658 <div>Gluecron</div>
659 </div>
660 <CompareRow feature="Git hosting + Smart HTTP push" them="✓" us="✓" />
661 <CompareRow feature="Issues, PRs, code review" them="✓" us="✓" />
662 <CompareRow feature="Workflow runner" them="Metered minutes" us="Self-hosted, unmetered" ours />
663 <CompareRow feature="AI code review on every PR" them="Copilot subscription" us="Built in, always on" ours />
664 <CompareRow feature="Spec-to-PR (issue → draft PR)" them="—" us="✓" ours />
665 <CompareRow feature="Auto-repair on gate failure" them="—" us="✓" ours />
666 <CompareRow feature="Secret scanner on push" them="Paid add-on" us="Built in" ours />
667 <CompareRow feature="MCP server (Claude / Cursor)" them="—" us="✓" ours />
668 <CompareRow feature="DORA metrics + velocity dashboard" them="Paid add-on" us="Built in" ours />
669 <CompareRow feature="Self-host on your own hardware" them="Enterprise tier" us="Single binary, free" ours />
670 <CompareRow feature="npm package registry" them="✓" us="✓" />
671 <CompareRow feature="Pre-receive policy enforcement" them="GitHub Enterprise" us="✓ on all plans" ours />
672 </div>
673
674 <div class="lp-compare-foot">
675 <a href="/vs-github" class="lp-text-link">See the full 26-row comparison →</a>
676 </div>
677 </div>
678 </section>
679
680 {/* ── Pricing teaser ─────────────────────────────────────── */}
681 <section class="lp-sec lp-sec-soft">
682 <div class="lp-wrap">
683 <div class="lp-sec-head">
684 <div class="lp-kicker">Pricing</div>
685 <h2 class="lp-h2">Free to start. Honest at scale.</h2>
686 <p class="lp-sub">
687 Self-hosting is free forever. Hosted plans price the AI calls,
688 not the seats. You own your data either way.
689 </p>
690 </div>
691
692 <div class="lp-price-grid">
693 <PricingCard
694 tier="Free"
695 price="$0"
696 cadence="forever"
697 desc="For personal projects and open source. Public and private repos, full AI suite, fair quotas."
698 features={["Unlimited public repos", "3 private repos", "5K AI calls / month", "Community support"]}
699 cta="Start free"
700 href="/register"
701 />
702 <PricingCard
703 tier="Pro"
704 price="$12"
705 cadence="per user / month"
706 desc="For working developers. Lifts every quota, adds priority routing, no Gluecron branding on deploys."
707 features={["Unlimited private repos", "100K AI calls / month", "Priority queue", "Custom domains"]}
708 cta="Go Pro"
709 href="/settings/billing"
710 highlight
711 />
712 <PricingCard
713 tier="Team"
714 price="Talk to us"
715 cadence="custom"
716 desc="For orgs running production on Gluecron. SSO, audit retention, enterprise SLA, on-prem."
717 features={["SSO + SCIM", "On-prem deploy", "Dedicated capacity", "24/7 incident response"]}
718 cta="Contact us"
719 href="mailto:hello@gluecron.com"
720 />
721 </div>
722
723 <div class="lp-price-foot">
724 <a href="/pricing" class="lp-text-link">Full pricing details →</a>
725 </div>
726 </div>
727 </section>
728
729 {/* ── One-command install ────────────────────────────────── */}
730 <section class="lp-sec">
731 <div class="lp-wrap lp-install-wrap">
732 <div class="lp-install-text">
733 <div class="lp-kicker">One command to start</div>
734 <h2 class="lp-h2 lp-h2-tight">Already on GitHub? Migrate in 30 seconds.</h2>
735 <p class="lp-sub">
736 Signs you in, mints a PAT, imports your repo, wires the Claude Desktop MCP
737 server, and drops the Gluecron skill files — all in one shot.
738 </p>
739 <a href="/import" class="lp-btn lp-btn-solid lp-btn-lg" style="margin-top:20px">
740 Or import a single repo →
741 </a>
742 </div>
743 <div class="lp-install-terminal" aria-label="One-line install command">
744 <div class="lp-term-bar">
745 <span class="lp-term-dot" /><span class="lp-term-dot" /><span class="lp-term-dot" />
746 <span class="lp-term-title">Terminal</span>
747 </div>
748 <div class="lp-term-body">
749 <div class="lp-term-line">
750 <span class="lp-term-prompt">$</span>
751 <span id="lp-install-cmd">curl -sSL gluecron.com/install | bash</span>
752 <button
753 type="button"
754 class="lp-copy-btn"
755 data-copy-target="lp-install-cmd"
756 aria-label="Copy install command"
757 >
758 Copy
759 </button>
760 </div>
761 <div class="lp-term-out lp-term-ok">✓ signed in as you@example.com</div>
762 <div class="lp-term-out lp-term-ok">✓ PAT created (admin scope)</div>
763 <div class="lp-term-out lp-term-ok">✓ your-repo imported from GitHub</div>
764 <div class="lp-term-out lp-term-ok">✓ MCP server added to Claude Desktop</div>
765 <div class="lp-term-out lp-term-ok">✓ gluecron-pr, gluecron-issue skills ready</div>
766 </div>
767 </div>
768 </div>
769 </section>
770
771 {/* ── Closing CTA ────────────────────────────────────────── */}
772 <section class="lp-cta">
773 <div class="lp-wrap lp-cta-in">
774 <div class="lp-kicker">Ready when you are</div>
775 <h2 class="lp-cta-h">Stop managing the platform.<br />Start shipping the product.</h2>
776 <p class="lp-cta-sub">
777 Free to start. Self-hosted-friendly. MCP-native.
778 Migrate from GitHub in one command.
779 </p>
780 <div class="lp-cta-btns">
781 <a href="/register" class="lp-btn lp-btn-solid lp-btn-xl">
782 Create your account
783 </a>
784 <a href="/import" class="lp-btn lp-btn-outline lp-btn-xl">
785 Migrate a repo
786 </a>
787 </div>
788 <div class="lp-cta-links">
789 <a href="/demo">Try the live demo</a>
790 <span aria-hidden="true">·</span>
791 <a href="/vs-github">Compare to GitHub</a>
792 <span aria-hidden="true">·</span>
793 <a href="/pricing">See pricing</a>
794 </div>
795 </div>
796 </section>
797
798 {/* ── Footer ─────────────────────────────────────────────── */}
799 <footer class="lp-footer">
800 <div class="lp-wrap lp-footer-in">
801 <div class="lp-footer-brand">
802 <a href="/" class="lp-logo">
803 <span class="lp-logo-mark" aria-hidden="true" />
804 gluecron
805 </a>
806 <p class="lp-footer-tag">
807 The AI-native git platform.<br />
808 Self-hosted, auditable, git-native.
809 </p>
810 </div>
811 <div class="lp-footer-cols">
812 <div class="lp-footer-col">
813 <h4>Product</h4>
814 <a href="#platform">Features</a>
815 <a href="#ai">AI suite</a>
816 <a href="#security">Security</a>
817 <a href="/pricing">Pricing</a>
818 <a href="/vs-github">vs GitHub</a>
819 <a href="/sleep-mode">Sleep Mode</a>
820 </div>
821 <div class="lp-footer-col">
822 <h4>Platform</h4>
823 <a href="/explore">Explore repos</a>
824 <a href="/demo">Live demo</a>
825 <a href="/marketplace">Marketplace</a>
826 <a href="/install">Install script</a>
827 <a href="/api">API docs</a>
828 <a href="/mcp">MCP server</a>
829 </div>
830 <div class="lp-footer-col">
831 <h4>Developers</h4>
832 <a href="/install">CLI install</a>
833 <a href="/vscode">VS Code ext</a>
834 <a href="/api/graphql">GraphQL explorer</a>
835 <a href="/help">Migration guide</a>
836 <a href="/import">Import from GitHub</a>
837 <a href="/connect/claude-guide">Claude Code guide</a>
838 </div>
839 <div class="lp-footer-col">
840 <h4>Account</h4>
841 <a href="/login">Sign in</a>
842 <a href="/register">Register</a>
843 <a href="/settings">Settings</a>
844 <a href="/settings/billing">Billing</a>
845 <a href="/settings/tokens">API tokens</a>
846 <a href="/status">Platform status</a>
847 </div>
848 </div>
849 </div>
850 <div class="lp-footer-bottom">
851 <div class="lp-wrap lp-footer-bottom-in">
852 <span>© {new Date().getFullYear()} Gluecron</span>
853 <span>Self-hosted · Git-native · Claude-first</span>
854 </div>
855 </div>
856 </footer>
857
858 <script dangerouslySetInnerHTML={{ __html: copyJs }} />
859 </body>
860 </html>
861 );
862};
863
864// ─── sub-components ────────────────────────────────────────────────
865
866const LiveCard: FC<{ title: string; children?: any }> = ({ title, children }) => (
867 <article class="lp-live-card">
868 <h3 class="lp-live-card-title">{title}</h3>
869 {children}
870 </article>
871);
872
873const PlatformGroup: FC<{ title: string; children: string[] }> = ({ title, children }) => (
874 <div class="lp-plat-group">
875 <h3 class="lp-plat-title">{title}</h3>
876 <ul class="lp-plat-list">
877 {children.map((item) => (
878 <li>{item}</li>
879 ))}
880 </ul>
881 </div>
882);
883
884const PainRow: FC<{ problem: string; fix: string }> = ({ problem, fix }) => (
885 <div class="lp-pain-row">
886 <div class="lp-pain-problem">
887 <span class="lp-pain-icon lp-pain-no" aria-label="Before">✗</span>
888 {problem}
889 </div>
890 <div class="lp-pain-arrow" aria-hidden="true">→</div>
891 <div class="lp-pain-fix">
892 <span class="lp-pain-icon lp-pain-yes" aria-label="After">✓</span>
893 {fix}
894 </div>
895 </div>
896);
897
898interface MockLine { icon: string; color: "brand" | "green" | "red" | "muted"; text: string; }
899
900const AiFeature: FC<{
901 n: string;
902 title: string;
903 body: string;
904 link: { href: string; label: string };
905 mock: MockLine[];
906 reverse?: boolean;
907}> = ({ n, title, body, link, mock, reverse }) => (
908 <div class={`lp-ai-feature${reverse ? " lp-ai-feature-rev" : ""}`}>
909 <div class="lp-ai-text">
910 <div class="lp-ai-n">{n}</div>
911 <h3 class="lp-ai-title">{title}</h3>
912 <p class="lp-ai-body">{body}</p>
913 <a href={link.href} class="lp-text-link">{link.label} →</a>
914 </div>
915 <div class="lp-ai-mock" aria-hidden="true">
916 <div class="lp-mock-bar">
917 <span class="lp-mock-dot" /><span class="lp-mock-dot" /><span class="lp-mock-dot" />
918 </div>
919 <div class="lp-mock-body">
920 {mock.map((line) => (
921 <div class={`lp-mock-line lp-mock-${line.color}`}>
922 <span class="lp-mock-icon">{line.icon}</span>
923 <span>{line.text}</span>
924 </div>
925 ))}
926 </div>
927 </div>
928 </div>
929);
930
931const SecurityCard: FC<{ title: string; body: string }> = ({ title, body }) => (
932 <div class="lp-sec-card">
933 <h3 class="lp-sec-card-title">{title}</h3>
934 <p class="lp-sec-card-body">{body}</p>
935 </div>
936);
937
938const TeamCard: FC<{ title: string; items: string[] }> = ({ title, items }) => (
939 <div class="lp-team-card">
940 <h3 class="lp-team-title">{title}</h3>
941 <ul class="lp-team-list">
942 {items.map((item) => (
943 <li>
944 <span aria-hidden="true">✓</span>
945 {item}
946 </li>
947 ))}
948 </ul>
949 </div>
950);
951
952const EcoCard: FC<{ title: string; sub: string; body: string; link: string }> = ({
953 title, sub, body, link,
954}) => (
955 <div class="lp-eco-card">
956 <h3 class="lp-eco-title">{title}</h3>
957 <div class="lp-eco-sub">{sub}</div>
958 <p class="lp-eco-body">{body}</p>
959 <a href={link} class="lp-text-link">Learn more →</a>
960 </div>
961);
962
963const CompareRow: FC<{ feature: string; them: string; us: string; ours?: boolean }> = ({
964 feature, them, us, ours,
965}) => (
966 <div class={`lp-cmp-row${ours ? " lp-cmp-ours" : ""}`}>
967 <div class="lp-cmp-feature">{feature}</div>
968 <div class="lp-cmp-them">{them}</div>
969 <div class={`lp-cmp-us${ours ? " lp-cmp-us-hl" : ""}`}>{us}</div>
970 </div>
971);
972
973const PricingCard: FC<{
974 tier: string; price: string; cadence: string; desc: string;
975 features: string[]; cta: string; href: string; highlight?: boolean;
976}> = ({ tier, price, cadence, desc, features, cta, href, highlight }) => (
977 <div class={`lp-price-card${highlight ? " lp-price-hl" : ""}`}>
978 {highlight && <div class="lp-price-badge">Most popular</div>}
979 <div class="lp-price-tier">{tier}</div>
980 <div class="lp-price-amount">
981 <span class="lp-price-num">{price}</span>
982 <span class="lp-price-cad">{cadence}</span>
983 </div>
984 <p class="lp-price-desc">{desc}</p>
985 <ul class="lp-price-features">
986 {features.map((f) => <li><span aria-hidden="true">✓</span>{f}</li>)}
987 </ul>
988 <a
989 href={href}
990 class={`lp-btn ${highlight ? "lp-btn-solid" : "lp-btn-outline"} lp-btn-block`}
991 >
992 {cta}
993 </a>
994 </div>
995);
996
997// ─── JavaScript ────────────────────────────────────────────────────
998
999const copyJs = `
1000(function(){
1001 document.addEventListener('click',function(e){
1002 var btn=e.target.closest('[data-copy-target]');
1003 if(!btn) return;
1004 var el=document.getElementById(btn.getAttribute('data-copy-target'));
1005 if(!el) return;
1006 var text=el.textContent||'';
1007 navigator.clipboard.writeText(text.trim()).then(function(){
1008 var orig=btn.textContent;btn.textContent='Copied!';
1009 setTimeout(function(){btn.textContent=orig;},1500);
1010 }).catch(function(){});
1011 });
1012})();
1013`;
1014
1015// Plain-JS poller for the live-now section. Hits the L3 endpoints every 30s.
1016const liveNowJs = `
1017(function(){
1018try{
1019 var DEMO=${JSON.stringify(DEMO_USERNAME)};
1020 var INTERVAL=30000;
1021 function esc(s){return String(s==null?'':s).replace(/[&<>"']/g,function(c){return{'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c];});}
1022 function rel(v){
1023 if(v==null) return 'just now';
1024 var t=(v instanceof Date)?v.getTime():(typeof v==='number'?v:new Date(v).getTime());
1025 if(!isFinite(t)) return 'just now';
1026 var d=Date.now()-t;if(d<0) return 'just now';
1027 var s=Math.floor(d/1000);if(s<60) return 'just now';
1028 var m=Math.floor(s/60);if(m<60) return m+'m ago';
1029 var h=Math.floor(m/60);if(h<24) return h+'h ago';
1030 return Math.floor(h/24)+'d ago';
1031 }
1032 function diffMount(ul,newHtml){
1033 if(!ul) return;
1034 var prev={};
1035 ul.querySelectorAll('[data-row-id]').forEach(function(n){prev[n.getAttribute('data-row-id')]=true;});
1036 ul.innerHTML=newHtml;
1037 ul.querySelectorAll('[data-row-id]').forEach(function(n){
1038 if(!prev[n.getAttribute('data-row-id')]){n.style.background='rgba(67,83,201,.06)';setTimeout(function(){n.style.background='';},1200);}
1039 });
1040 }
1041 function pollQueued(){
1042 fetch('/api/v2/demo/queued',{credentials:'omit'}).then(function(r){return r.json();}).then(function(d){
1043 var ul=document.querySelector('[data-livecard="queued"]');if(!ul) return;
1044 var items=(d&&d.items)||[];
1045 if(!items.length){ul.innerHTML='<li class="lp-live-empty">Quiet right now.</li>';return;}
1046 diffMount(ul,items.slice(0,3).map(function(i){
1047 var id='queued|'+i.repo+'|'+i.number;
1048 return '<li class="lp-live-row" data-row-id="'+esc(id)+'"><a href="/'+esc(DEMO)+'/'+esc(i.repo)+'/issues/'+i.number+'" class="lp-live-link"><span class="lp-live-num">#'+i.number+'</span> '+esc(i.title)+'</a><span class="lp-live-meta">'+esc(i.repo)+'</span></li>';
1049 }).join(''));
1050 }).catch(function(){});
1051 }
1052 function pollMerges(){
1053 fetch('/api/v2/demo/merges',{credentials:'omit'}).then(function(r){return r.json();}).then(function(d){
1054 var ul=document.querySelector('[data-livecard="merges"]');if(!ul) return;
1055 var items=(d&&d.items)||[];
1056 if(!items.length){ul.innerHTML='<li class="lp-live-empty">No auto-merges in the last 24h.</li>';return;}
1057 diffMount(ul,items.slice(0,3).map(function(m){
1058 var id='merges|'+m.repo+'|'+m.number;
1059 return '<li class="lp-live-row" data-row-id="'+esc(id)+'"><a href="/'+esc(DEMO)+'/'+esc(m.repo)+'/pulls/'+m.number+'" class="lp-live-link"><span class="lp-live-num">#'+m.number+'</span> '+esc(m.title)+'</a><span class="lp-live-meta">'+esc(m.repo)+' <span data-rel="'+esc(m.mergedAt)+'">'+esc(rel(m.mergedAt))+'</span></span></li>';
1060 }).join(''));
1061 }).catch(function(){});
1062 }
1063 function pollReviews(){
1064 fetch('/api/v2/demo/reviews',{credentials:'omit'}).then(function(r){return r.json();}).then(function(d){
1065 var ul=document.querySelector('[data-livecard="reviews"]');if(!ul) return;
1066 var cnt=document.querySelector('[data-livecard-count="reviews"]');
1067 if(cnt&&typeof d.count==='number') cnt.textContent=d.count;
1068 var items=(d&&d.items)||[];
1069 if(!items.length){ul.innerHTML='<li class="lp-live-empty">No reviews in the last 24h.</li>';return;}
1070 diffMount(ul,items.slice(0,2).map(function(r){
1071 var id='reviews|'+r.repo+'|'+r.prNumber;
1072 return '<li class="lp-live-row" data-row-id="'+esc(id)+'"><a href="/'+esc(DEMO)+'/'+esc(r.repo)+'/pulls/'+r.prNumber+'" class="lp-live-link"><span class="lp-live-num">#'+r.prNumber+'</span> '+esc(r.commentSnippet)+'</a><span class="lp-live-meta">'+esc(r.repo)+'</span></li>';
1073 }).join(''));
1074 }).catch(function(){});
1075 }
1076 function pollFeed(){
1077 fetch('/api/v2/demo/activity',{credentials:'omit'}).then(function(r){return r.json();}).then(function(d){
1078 var ul=document.querySelector('[data-livecard="feed"]');if(!ul) return;
1079 var entries=(d&&d.entries)||[];
1080 if(!entries.length){ul.innerHTML='<li class="lp-live-empty">Quiet right now.</li>';return;}
1081 diffMount(ul,entries.slice(0,6).map(function(e){
1082 var path=(e.ref&&e.ref.type==='pr')?'pulls':'issues';
1083 var num=(e.ref&&e.ref.number)||0;
1084 var label=e.kind==='auto_merge.merged'?'auto-merged':e.kind==='ai_build.dispatched'?'AI-built':'AI review';
1085 var kc=String(e.kind||'').replace(/\\./g,'-');
1086 var id=e.kind+'|'+e.repo+'|'+(e.ref&&e.ref.type)+'|'+num;
1087 return '<li class="lp-live-feedrow" data-row-id="'+esc(id)+'"><span class="lp-feed-kind lp-feed-kind-'+esc(kc)+'">'+esc(label)+'</span> <a class="lp-live-link" href="/'+esc(DEMO)+'/'+esc(e.repo)+'/'+path+'/'+num+'">'+esc(e.repo)+' #'+num+'</a> <span data-rel="'+esc(e.at)+'">'+esc(rel(e.at))+'</span></li>';
1088 }).join(''));
1089 }).catch(function(){});
1090 }
1091 function refreshRel(){
1092 document.querySelectorAll('[data-rel]').forEach(function(el){el.textContent=rel(el.getAttribute('data-rel'));});
1093 }
1094 function tick(){pollQueued();pollMerges();pollReviews();pollFeed();}
1095 setInterval(tick,INTERVAL);
1096 setInterval(refreshRel,60000);
1097}catch(err){}
1098})();
1099`;
1100
1101// ─── CSS ───────────────────────────────────────────────────────────
1102
1103const css = `
1104/* ── tokens ─────────────────────────────────────────────────────── */
1105:root{
1106 --lp-brand: #4353c9;
1107 --lp-brand-h: #3848b6;
1108 --lp-bg: #ffffff;
1109 --lp-soft: #fafafb;
1110 --lp-ink: #0a0b0d;
1111 --lp-ink-2: #3a3d45;
1112 --lp-muted: #6b7280;
1113 --lp-border: rgba(0,0,0,.08);
1114 --lp-shadow: 0 1px 3px rgba(0,0,0,.04),0 8px 20px rgba(0,0,0,.06);
1115 --lp-green: #059669;
1116 --lp-red: #dc2626;
1117 --lp-max: 1200px;
1118 --lp-r: 10px;
1119}
1120*{box-sizing:border-box;margin:0;padding:0}
1121html{scroll-behavior:smooth;font-size:17px;line-height:1.6;
1122 -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
1123body{background:var(--lp-bg);color:var(--lp-ink);
1124 font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',system-ui,sans-serif}
1125a{color:inherit;text-decoration:none}
1126img{display:block;max-width:100%}
1127
1128/* ── layout ─────────────────────────────────────────────────────── */
1129.lp-wrap{max-width:var(--lp-max);margin:0 auto;padding:0 24px}
1130.lp-sec{padding:96px 0}
1131.lp-sec-soft{background:var(--lp-soft)}
1132
1133/* ── typography ─────────────────────────────────────────────────── */
1134.lp-h1{font-family:'Inter Tight','Inter',sans-serif;font-weight:800;
1135 font-size:clamp(42px,6.5vw,76px);line-height:1.02;letter-spacing:-.025em;
1136 color:var(--lp-ink)}
1137.lp-h2{font-family:'Inter Tight','Inter',sans-serif;font-weight:700;
1138 font-size:clamp(32px,4.5vw,52px);line-height:1.08;letter-spacing:-.022em;
1139 color:var(--lp-ink)}
1140.lp-h2-tight{font-size:clamp(28px,3.5vw,42px)}
1141.lp-kicker{font-size:13px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
1142 color:var(--lp-brand);margin-bottom:12px}
1143.lp-sub{font-size:18px;color:var(--lp-ink-2);line-height:1.65;max-width:60ch}
1144.lp-text-link{color:var(--lp-brand);font-size:15px;font-weight:500}
1145.lp-text-link:hover{color:var(--lp-brand-h);text-decoration:underline}
1146code{font-family:'JetBrains Mono',ui-monospace,monospace;font-size:.88em;
1147 background:rgba(0,0,0,.05);padding:2px 5px;border-radius:4px}
1148
1149/* ── buttons ─────────────────────────────────────────────────────── */
1150.lp-btn{display:inline-flex;align-items:center;gap:6px;font-family:inherit;
1151 font-weight:600;font-size:15px;border-radius:var(--lp-r);padding:10px 18px;
1152 border:1px solid transparent;cursor:pointer;text-decoration:none;
1153 transition:transform .12s,box-shadow .2s,background .15s,border-color .15s;
1154 white-space:nowrap}
1155.lp-btn:hover{transform:translateY(-1px)}
1156.lp-btn-solid{background:var(--lp-ink);color:#fff;border-color:var(--lp-ink)}
1157.lp-btn-solid:hover{box-shadow:0 6px 18px rgba(10,11,13,.22)}
1158.lp-btn-outline{color:var(--lp-ink);border-color:rgba(0,0,0,.2)}
1159.lp-btn-outline:hover{border-color:var(--lp-ink);background:rgba(0,0,0,.03)}
1160.lp-btn-ghost{color:var(--lp-ink-2);border-color:var(--lp-border)}
1161.lp-btn-ghost:hover{color:var(--lp-ink);border-color:rgba(0,0,0,.2)}
1162.lp-btn-lg{padding:13px 22px;font-size:16px;border-radius:12px}
1163.lp-btn-xl{padding:15px 28px;font-size:17px;border-radius:14px}
1164.lp-btn-block{width:100%;justify-content:center}
1165
1166/* ── badge ───────────────────────────────────────────────────────── */
1167.lp-badge{font-size:12px;font-weight:700;padding:3px 8px;border-radius:6px}
1168.lp-badge-merged{color:var(--lp-green);background:rgba(5,150,105,.1)}
1169
1170/* ── nav ─────────────────────────────────────────────────────────── */
1171.lp-nav{position:sticky;top:0;z-index:50;background:rgba(255,255,255,.88);
1172 backdrop-filter:blur(12px) saturate(180%);border-bottom:1px solid transparent;
1173 transition:border-color .2s,background .2s}
1174.lp-nav.stuck{border-bottom-color:var(--lp-border)}
1175.lp-nav-in{max-width:var(--lp-max);margin:0 auto;padding:14px 24px;
1176 display:flex;align-items:center;gap:20px}
1177.lp-logo{display:inline-flex;align-items:center;gap:9px;
1178 font-family:'Inter Tight',sans-serif;font-weight:700;font-size:18px;
1179 letter-spacing:-.02em;color:var(--lp-ink)}
1180.lp-logo:hover{color:var(--lp-ink)}
1181.lp-logo-mark{width:18px;height:18px;border-radius:6px;background:var(--lp-brand);display:inline-block}
1182.lp-nav-links{display:flex;gap:24px;margin-left:10px}
1183.lp-nav-links a{font-size:15px;font-weight:500;color:var(--lp-ink-2);transition:color .15s}
1184.lp-nav-links a:hover{color:var(--lp-ink)}
1185.lp-nav-ctas{margin-left:auto;display:flex;align-items:center;gap:10px}
1186@media(max-width:720px){.lp-nav-links{display:none}.lp-nav-ctas .lp-btn-ghost{display:none}}
1187
1188/* ── hero ────────────────────────────────────────────────────────── */
1189.lp-hero{padding:80px 0 60px;border-bottom:1px solid var(--lp-border)}
1190.lp-hero-in{display:grid;grid-template-columns:1fr 1fr;gap:64px;align-items:center}
1191.lp-hero-text{display:flex;flex-direction:column;gap:0}
1192.lp-hero-text .lp-kicker{margin-bottom:16px}
1193.lp-hero-sub{font-size:18px;color:var(--lp-ink-2);line-height:1.65;margin:20px 0 0}
1194.lp-hero-ctas{display:flex;gap:12px;flex-wrap:wrap;margin-top:28px}
1195.lp-hero-links{display:flex;gap:10px;align-items:center;margin-top:14px;font-size:14px;color:var(--lp-muted)}
1196.lp-hero-links a{color:var(--lp-brand);font-weight:500}
1197.lp-hero-links span{color:var(--lp-border)}
1198.lp-hero-stats{display:flex;gap:28px;margin-top:32px;padding-top:24px;
1199 border-top:1px solid var(--lp-border);list-style:none;flex-wrap:wrap}
1200.lp-hero-stat dt{font-family:'Inter Tight',sans-serif;font-weight:700;font-size:22px;
1201 color:var(--lp-ink);letter-spacing:-.01em}
1202.lp-hero-stat dd{font-size:13px;color:var(--lp-muted);margin-top:2px}
1203@media(max-width:900px){
1204 .lp-hero-in{grid-template-columns:1fr}
1205 .lp-hero-card{order:-1}
1206}
1207
1208/* ── hero card mock ──────────────────────────────────────────────── */
1209.lp-hero-card{background:#fff;border:1px solid var(--lp-border);border-radius:16px;
1210 box-shadow:var(--lp-shadow);overflow:hidden}
1211.lp-hc-bar{display:flex;align-items:center;gap:6px;padding:10px 14px;
1212 border-bottom:1px solid var(--lp-border);background:var(--lp-soft)}
1213.lp-hc-dot{width:10px;height:10px;border-radius:50%;background:#d1d5db}
1214.lp-hc-path{margin-left:8px;font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--lp-muted)}
1215.lp-hc-body{padding:18px}
1216.lp-hc-pr-row{display:flex;align-items:center;gap:10px;margin-bottom:14px}
1217.lp-hc-title{font-size:14px;font-weight:600;color:var(--lp-ink)}
1218.lp-hc-review{display:flex;gap:10px;margin-bottom:14px}
1219.lp-hc-ava{width:30px;height:30px;border-radius:50%;background:var(--lp-brand);
1220 color:#fff;font-size:13px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0}
1221.lp-hc-rev{flex:1}
1222.lp-hc-rev-head{font-size:12.5px;font-weight:600;color:var(--lp-ink-2);margin-bottom:4px}
1223.lp-hc-rev-head em{color:var(--lp-green);font-style:normal}
1224.lp-hc-rev-body{font-size:13px;color:var(--lp-ink-2);line-height:1.5}
1225.lp-hc-checks{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:10px}
1226.lp-check{font-family:'JetBrains Mono',monospace;font-size:12px;padding:3px 8px;
1227 border-radius:5px;font-weight:500}
1228.lp-check-ok{color:var(--lp-green);background:rgba(5,150,105,.08)}
1229.lp-hc-meta{font-size:12px;color:var(--lp-muted);font-family:'JetBrains Mono',monospace}
1230
1231/* ── trust strip ─────────────────────────────────────────────────── */
1232.lp-trust-strip{border-bottom:1px solid var(--lp-border);padding:14px 0;background:var(--lp-soft)}
1233.lp-trust-in{display:flex;align-items:center;gap:20px;flex-wrap:wrap;
1234 font-size:13.5px;font-weight:500;color:var(--lp-muted);justify-content:center}
1235.lp-sep{width:1px;height:14px;background:var(--lp-border)}
1236
1237/* ── section header ─────────────────────────────────────────────── */
1238.lp-sec-head{max-width:680px;margin-bottom:52px}
1239.lp-sec-head .lp-h2{margin:8px 0 16px}
1240
1241/* ── live-now ────────────────────────────────────────────────────── */
1242.lp-live-dot{color:var(--lp-brand);font-size:10px;margin-right:4px}
1243.lp-live-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-bottom:32px}
1244@media(max-width:1000px){.lp-live-grid{grid-template-columns:repeat(2,1fr)}}
1245@media(max-width:560px){.lp-live-grid{grid-template-columns:1fr}}
1246.lp-live-card{background:#fff;border:1px solid var(--lp-border);border-radius:var(--lp-r);
1247 padding:20px;box-shadow:var(--lp-shadow)}
1248.lp-live-card-title{font-size:13px;font-weight:600;color:var(--lp-ink-2);
1249 text-transform:uppercase;letter-spacing:.05em;margin-bottom:14px}
1250.lp-live-list{list-style:none;display:flex;flex-direction:column;gap:10px}
1251.lp-live-empty{font-size:13.5px;color:var(--lp-muted)}
1252.lp-live-row{display:flex;flex-direction:column;gap:3px}
1253.lp-live-link{font-size:13.5px;color:var(--lp-ink);font-weight:500;line-height:1.4}
1254.lp-live-link:hover{color:var(--lp-brand)}
1255.lp-live-num{font-family:'JetBrains Mono',monospace;font-size:11.5px;color:var(--lp-muted)}
1256.lp-live-meta{font-size:12px;color:var(--lp-muted)}
1257.lp-live-rel{color:var(--lp-muted)}
1258.lp-live-bignum{display:flex;align-items:baseline;gap:6px;margin-bottom:10px}
1259.lp-live-bignum span:first-child{font-family:'Inter Tight',sans-serif;font-size:32px;
1260 font-weight:700;color:var(--lp-ink)}
1261.lp-live-bignum-label{font-size:13.5px;color:var(--lp-muted)}
1262.lp-live-feedrow{display:flex;align-items:baseline;gap:5px;flex-wrap:wrap;
1263 font-size:13px;line-height:1.5}
1264.lp-feed-kind{font-size:11.5px;font-weight:600;padding:2px 6px;border-radius:4px}
1265.lp-feed-kind-auto_merge-merged{color:var(--lp-green);background:rgba(5,150,105,.09)}
1266.lp-feed-kind-ai_build-dispatched{color:var(--lp-brand);background:rgba(67,83,201,.09)}
1267.lp-feed-kind-ai_review-posted{color:#7c3aed;background:rgba(124,58,237,.09)}
1268.lp-live-cta{display:flex;align-items:center;gap:16px}
1269
1270/* ── platform grid ───────────────────────────────────────────────── */
1271.lp-platform-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:2px;
1272 border:1px solid var(--lp-border);border-radius:14px;overflow:hidden;
1273 background:var(--lp-border)}
1274@media(max-width:900px){.lp-platform-grid{grid-template-columns:repeat(2,1fr)}}
1275@media(max-width:500px){.lp-platform-grid{grid-template-columns:1fr}}
1276.lp-plat-group{background:#fff;padding:24px;display:flex;flex-direction:column;gap:12px}
1277.lp-plat-title{font-size:13px;font-weight:700;text-transform:uppercase;
1278 letter-spacing:.06em;color:var(--lp-brand)}
1279.lp-plat-list{list-style:none;display:flex;flex-direction:column;gap:6px}
1280.lp-plat-list li{font-size:13.5px;color:var(--lp-ink-2)}
1281
1282/* ── pain points ─────────────────────────────────────────────────── */
1283.lp-pain-table{display:flex;flex-direction:column;gap:0;
1284 border:1px solid var(--lp-border);border-radius:14px;overflow:hidden}
1285.lp-pain-row{display:grid;grid-template-columns:1fr 40px 1fr;align-items:center;
1286 gap:16px;padding:20px 24px;border-bottom:1px solid var(--lp-border)}
1287.lp-pain-row:last-child{border-bottom:0}
1288.lp-pain-row:hover{background:var(--lp-soft)}
1289.lp-pain-problem{display:flex;align-items:flex-start;gap:10px;font-size:15px;color:var(--lp-ink-2)}
1290.lp-pain-fix{display:flex;align-items:flex-start;gap:10px;font-size:15px;color:var(--lp-ink);font-weight:500}
1291.lp-pain-arrow{font-size:18px;color:var(--lp-muted);text-align:center}
1292.lp-pain-icon{font-size:13px;font-weight:700;margin-top:2px;flex-shrink:0}
1293.lp-pain-no{color:var(--lp-red)}
1294.lp-pain-yes{color:var(--lp-green)}
1295@media(max-width:700px){
1296 .lp-pain-row{grid-template-columns:1fr;gap:8px}
1297 .lp-pain-arrow{display:none}
1298}
1299
1300/* ── AI features ─────────────────────────────────────────────────── */
1301.lp-ai-features{display:flex;flex-direction:column;gap:64px}
1302.lp-ai-feature{display:grid;grid-template-columns:1fr 1fr;gap:64px;align-items:center}
1303.lp-ai-feature-rev{direction:rtl}
1304.lp-ai-feature-rev>*{direction:ltr}
1305@media(max-width:800px){.lp-ai-feature,.lp-ai-feature-rev{grid-template-columns:1fr;direction:ltr}}
1306.lp-ai-n{font-family:'JetBrains Mono',monospace;font-size:11px;
1307 font-weight:500;color:var(--lp-muted);margin-bottom:10px;letter-spacing:.04em}
1308.lp-ai-title{font-family:'Inter Tight',sans-serif;font-weight:700;
1309 font-size:clamp(22px,3vw,30px);line-height:1.12;letter-spacing:-.018em;
1310 color:var(--lp-ink);margin-bottom:14px}
1311.lp-ai-body{font-size:16px;color:var(--lp-ink-2);line-height:1.65;margin-bottom:18px}
1312/* AI mock terminal */
1313.lp-ai-mock{background:#fff;border:1px solid var(--lp-border);border-radius:12px;
1314 overflow:hidden;box-shadow:var(--lp-shadow)}
1315.lp-mock-bar{display:flex;align-items:center;gap:6px;padding:10px 14px;
1316 border-bottom:1px solid var(--lp-border);background:var(--lp-soft)}
1317.lp-mock-dot{width:10px;height:10px;border-radius:50%;background:#d1d5db}
1318.lp-mock-body{padding:16px;display:flex;flex-direction:column;gap:10px}
1319.lp-mock-line{display:flex;align-items:flex-start;gap:10px;font-size:13.5px;line-height:1.5}
1320.lp-mock-icon{font-family:'JetBrains Mono',monospace;font-size:12px;
1321 width:18px;text-align:center;flex-shrink:0;margin-top:1px}
1322.lp-mock-brand .lp-mock-icon,.lp-mock-brand span{color:var(--lp-brand)}
1323.lp-mock-green .lp-mock-icon,.lp-mock-green span{color:var(--lp-green)}
1324.lp-mock-red .lp-mock-icon,.lp-mock-red span{color:var(--lp-red)}
1325.lp-mock-muted .lp-mock-icon,.lp-mock-muted span{color:var(--lp-muted)}
1326
1327/* ── security cards ──────────────────────────────────────────────── */
1328.lp-security-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}
1329@media(max-width:900px){.lp-security-grid{grid-template-columns:repeat(2,1fr)}}
1330@media(max-width:560px){.lp-security-grid{grid-template-columns:1fr}}
1331.lp-sec-card{background:#fff;border:1px solid var(--lp-border);border-radius:var(--lp-r);
1332 padding:24px;box-shadow:var(--lp-shadow);transition:transform .15s,box-shadow .2s}
1333.lp-sec-card:hover{transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,0,0,.07),0 20px 40px rgba(0,0,0,.06)}
1334.lp-sec-card-title{font-size:15px;font-weight:700;color:var(--lp-ink);margin-bottom:10px}
1335.lp-sec-card-body{font-size:14px;color:var(--lp-ink-2);line-height:1.6}
1336
1337/* ── teams ───────────────────────────────────────────────────────── */
1338.lp-teams-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px}
1339@media(max-width:1000px){.lp-teams-grid{grid-template-columns:repeat(2,1fr)}}
1340@media(max-width:560px){.lp-teams-grid{grid-template-columns:1fr}}
1341.lp-team-card{background:#fff;border:1px solid var(--lp-border);border-radius:var(--lp-r);padding:24px;box-shadow:var(--lp-shadow)}
1342.lp-team-title{font-size:15px;font-weight:700;color:var(--lp-ink);margin-bottom:14px;
1343 padding-bottom:12px;border-bottom:1px solid var(--lp-border)}
1344.lp-team-list{list-style:none;display:flex;flex-direction:column;gap:8px}
1345.lp-team-list li{display:flex;align-items:flex-start;gap:8px;font-size:14px;color:var(--lp-ink-2);line-height:1.4}
1346.lp-team-list li span{color:var(--lp-green);font-size:12px;margin-top:2px;flex-shrink:0}
1347
1348/* ── ecosystem ───────────────────────────────────────────────────── */
1349.lp-eco-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:16px}
1350@media(max-width:900px){.lp-eco-grid{grid-template-columns:repeat(2,1fr)}}
1351@media(max-width:500px){.lp-eco-grid{grid-template-columns:1fr}}
1352.lp-eco-card{background:#fff;border:1px solid var(--lp-border);border-radius:var(--lp-r);
1353 padding:24px;box-shadow:var(--lp-shadow);display:flex;flex-direction:column;gap:10px}
1354.lp-eco-title{font-size:15px;font-weight:700;color:var(--lp-ink)}
1355.lp-eco-sub{font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--lp-brand)}
1356.lp-eco-body{font-size:14px;color:var(--lp-ink-2);line-height:1.6;flex:1}
1357
1358/* ── comparison ──────────────────────────────────────────────────── */
1359.lp-compare{border:1px solid var(--lp-border);border-radius:14px;overflow:hidden}
1360.lp-compare-head{display:grid;grid-template-columns:1fr 160px 160px;
1361 gap:0;padding:12px 20px;background:var(--lp-soft);
1362 font-size:13px;font-weight:600;color:var(--lp-muted);text-transform:uppercase;letter-spacing:.04em;
1363 border-bottom:1px solid var(--lp-border)}
1364.lp-cmp-row{display:grid;grid-template-columns:1fr 160px 160px;
1365 gap:0;padding:14px 20px;border-bottom:1px solid var(--lp-border);
1366 font-size:14.5px;align-items:center}
1367.lp-cmp-row:last-child{border-bottom:0}
1368.lp-cmp-ours{background:rgba(67,83,201,.025)}
1369.lp-cmp-feature{color:var(--lp-ink-2)}
1370.lp-cmp-them{color:var(--lp-muted);font-size:14px}
1371.lp-cmp-us{color:var(--lp-muted);font-size:14px}
1372.lp-cmp-us-hl{color:var(--lp-green);font-weight:600}
1373.lp-compare-foot{margin-top:16px}
1374@media(max-width:700px){
1375 .lp-compare-head,.lp-cmp-row{grid-template-columns:1fr 100px 100px}
1376 .lp-cmp-row{font-size:13px}
1377}
1378
1379/* ── pricing ─────────────────────────────────────────────────────── */
1380.lp-price-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}
1381@media(max-width:800px){.lp-price-grid{grid-template-columns:1fr}}
1382.lp-price-card{background:#fff;border:1px solid var(--lp-border);border-radius:14px;
1383 padding:28px;box-shadow:var(--lp-shadow);display:flex;flex-direction:column;gap:0;position:relative}
1384.lp-price-hl{border-color:var(--lp-brand)}
1385.lp-price-badge{position:absolute;top:-12px;left:50%;transform:translateX(-50%);
1386 background:var(--lp-brand);color:#fff;font-size:12px;font-weight:700;
1387 padding:4px 12px;border-radius:999px}
1388.lp-price-tier{font-size:13px;font-weight:700;text-transform:uppercase;
1389 letter-spacing:.05em;color:var(--lp-brand);margin-bottom:12px}
1390.lp-price-amount{display:flex;align-items:baseline;gap:6px;margin-bottom:12px}
1391.lp-price-num{font-family:'Inter Tight',sans-serif;font-size:36px;font-weight:800;
1392 color:var(--lp-ink);letter-spacing:-.02em}
1393.lp-price-cad{font-size:14px;color:var(--lp-muted)}
1394.lp-price-desc{font-size:14px;color:var(--lp-ink-2);line-height:1.6;margin-bottom:20px}
1395.lp-price-features{list-style:none;display:flex;flex-direction:column;gap:8px;
1396 margin-bottom:24px;flex:1}
1397.lp-price-features li{display:flex;align-items:flex-start;gap:8px;font-size:14px;color:var(--lp-ink-2)}
1398.lp-price-features li span{color:var(--lp-green);font-size:12px;margin-top:2px}
1399.lp-price-foot{margin-top:16px}
1400
1401/* ── install section ─────────────────────────────────────────────── */
1402.lp-install-wrap{display:grid;grid-template-columns:1fr 1fr;gap:64px;align-items:center}
1403@media(max-width:800px){.lp-install-wrap{grid-template-columns:1fr}}
1404.lp-install-text{display:flex;flex-direction:column}
1405.lp-install-text .lp-kicker{margin-bottom:16px}
1406.lp-install-terminal{background:#fff;border:1px solid var(--lp-border);border-radius:14px;
1407 overflow:hidden;box-shadow:var(--lp-shadow)}
1408.lp-term-bar{display:flex;align-items:center;gap:6px;padding:10px 16px;
1409 background:var(--lp-soft);border-bottom:1px solid var(--lp-border)}
1410.lp-term-dot{width:10px;height:10px;border-radius:50%;background:#d1d5db}
1411.lp-term-title{margin-left:8px;font-size:12px;color:var(--lp-muted);font-family:'JetBrains Mono',monospace}
1412.lp-term-body{padding:16px;display:flex;flex-direction:column;gap:8px}
1413.lp-term-line{display:flex;align-items:center;gap:10px;font-family:'JetBrains Mono',monospace;font-size:13px}
1414.lp-term-prompt{color:var(--lp-brand);font-weight:500}
1415.lp-term-out{font-family:'JetBrains Mono',monospace;font-size:13px;color:var(--lp-muted);padding-left:20px}
1416.lp-term-ok{color:var(--lp-green)}
1417.lp-copy-btn{margin-left:auto;font-size:12px;font-family:'Inter',sans-serif;font-weight:600;
1418 padding:4px 10px;border:1px solid var(--lp-border);border-radius:6px;
1419 background:#fff;color:var(--lp-ink-2);cursor:pointer;transition:background .15s,color .15s}
1420.lp-copy-btn:hover{background:var(--lp-soft);color:var(--lp-ink)}
1421
1422/* ── closing CTA ─────────────────────────────────────────────────── */
1423.lp-cta{padding:96px 0;border-top:1px solid var(--lp-border)}
1424.lp-cta-in{text-align:center;display:flex;flex-direction:column;align-items:center;gap:0}
1425.lp-cta-in .lp-kicker{margin-bottom:16px}
1426.lp-cta-h{font-family:'Inter Tight',sans-serif;font-weight:800;
1427 font-size:clamp(32px,5vw,56px);line-height:1.06;letter-spacing:-.025em;
1428 color:var(--lp-ink);margin-bottom:16px}
1429.lp-cta-sub{font-size:18px;color:var(--lp-ink-2);max-width:52ch;margin-bottom:32px}
1430.lp-cta-btns{display:flex;gap:12px;flex-wrap:wrap;justify-content:center;margin-bottom:20px}
1431.lp-cta-links{display:flex;gap:12px;align-items:center;font-size:14px;color:var(--lp-muted)}
1432.lp-cta-links a{color:var(--lp-brand);font-weight:500}
1433.lp-cta-links span{color:var(--lp-border)}
1434
1435/* ── footer ──────────────────────────────────────────────────────── */
1436.lp-footer{border-top:1px solid var(--lp-border);padding:64px 0 0}
1437.lp-footer-in{display:grid;grid-template-columns:280px 1fr;gap:64px;margin-bottom:48px}
1438@media(max-width:800px){.lp-footer-in{grid-template-columns:1fr;gap:32px}}
1439.lp-footer-brand{display:flex;flex-direction:column;gap:12px}
1440.lp-footer-tag{font-size:14px;color:var(--lp-muted);line-height:1.6}
1441.lp-footer-cols{display:grid;grid-template-columns:repeat(4,1fr);gap:32px}
1442@media(max-width:700px){.lp-footer-cols{grid-template-columns:repeat(2,1fr)}}
1443.lp-footer-col{display:flex;flex-direction:column;gap:10px}
1444.lp-footer-col h4{font-size:13px;font-weight:700;color:var(--lp-ink);
1445 text-transform:uppercase;letter-spacing:.05em;margin-bottom:4px}
1446.lp-footer-col a{font-size:14px;color:var(--lp-muted);transition:color .15s}
1447.lp-footer-col a:hover{color:var(--lp-ink)}
1448.lp-footer-bottom{border-top:1px solid var(--lp-border);padding:20px 0}
1449.lp-footer-bottom-in{display:flex;justify-content:space-between;align-items:center;
1450 font-size:13px;color:var(--lp-muted)}
1451
1452/* ── nav stuck JS class ──────────────────────────────────────────── */
1453.lp-nav.lp-stuck{border-bottom-color:var(--lp-border)}
1454`;