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

landing.tsx

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

landing.tsxBlame3755 lines · 3 contributors
2b821b7Claude1/**
2 * Marketing landing page for logged-out visitors.
3 *
958d26aClaude4 * Editorial-Technical redesign — 2026.05.
5 * Hero · trust strip · feature grid · workflow walkthrough ·
6 * comparison · terminal · pricing teaser · closing CTA.
2b821b7Claude7 *
5f2e749Claude8 * Block L10 — hero rewrite. The hero now lands the Block L positioning
9 * ("the git host built around Claude"): gradient headline, one-line
10 * install snippet w/ copy button, three CTAs (Sign up / Demo / vs-GitHub),
b5d207bClaude11 * and a four-line activity rail driven off the L4 publicStats
5f2e749Claude12 * payload. The L4 counters tile section and L5 vs-GitHub CTA are both
13 * preserved — additive only.
14 *
15 * Also adds two new editorial sections below the L4 counters:
b5d207bClaude16 * - "Three reasons to switch" (Instant Shipping / Migrate / Demo)
5f2e749Claude17 * - "How is this different from GitHub?" pull-quote → /vs-github
18 *
958d26aClaude19 * Pure presentational. Drops into <Layout user={null}>.
20 * All styles scoped under `.landing-` so they don't bleed into app views.
2b821b7Claude21 */
22
23import type { FC } from "hono/jsx";
52ad8b1Claude24import type { PublicStats } from "../lib/public-stats";
534f04aClaude25import { DEMO_USERNAME } from "../lib/demo-seed";
26
27export interface LandingLiveFeedQueued {
28 repo: string;
29 number: number;
30 title: string;
31 createdAt: string | Date;
32}
33
34export interface LandingLiveFeedMerge {
35 repo: string;
36 number: number;
37 title: string;
38 mergedAt: string | Date;
39}
40
41export interface LandingLiveFeedReview {
42 repo: string;
43 prNumber: number;
44 commentSnippet: string;
45 createdAt: string | Date;
46}
47
48export interface LandingLiveFeedEntry {
49 kind: "auto_merge.merged" | "ai_build.dispatched" | "ai_review.posted";
50 repo: string;
51 ref: { type: "issue" | "pr"; number: number };
52 at: string | Date;
53}
54
55/**
56 * Block M1 — server-rendered snapshot of the live-now feed. The same
57 * fields are also fetched client-side every 30s from
58 * `/api/v2/demo/{queued,merges,reviews,activity}`. Optional so existing
59 * call-sites (and tests that don't care about the live block) keep
60 * compiling.
61 */
62export interface LandingLiveFeed {
63 queued: LandingLiveFeedQueued[];
64 merges: LandingLiveFeedMerge[];
65 reviews: LandingLiveFeedReview[];
66 reviewCount: number;
67 feed: LandingLiveFeedEntry[];
68}
2b821b7Claude69
70export interface LandingPageProps {
71 stats?: {
72 publicRepos?: number;
73 users?: number;
74 };
52ad8b1Claude75 /**
76 * Block L4 — full public-stats payload (lifetime + trailing-7-day
77 * AI-highlight counters). When present, the hero renders an animated
78 * six-tile social-proof row beneath the eyebrow.
79 */
80 publicStats?: PublicStats | null;
534f04aClaude81 /**
82 * Block M1 — initial SSR snapshot for the live-now feed block.
83 * The same endpoints poll client-side every 30s. When undefined the
84 * section still renders, but with empty-state copy until the first
85 * client poll lands.
86 */
87 liveFeed?: LandingLiveFeed | null;
2b821b7Claude88}
89
534f04aClaude90export const LandingHero: FC<LandingPageProps> = ({
91 stats,
92 publicStats,
93 liveFeed,
94} = {}) => {
8e9f1d9Claude95 const hasStats =
96 stats &&
97 ((stats.publicRepos !== undefined && stats.publicRepos > 0) ||
98 (stats.users !== undefined && stats.users > 0));
4c47454Claude99
52ad8b1Claude100 // Block L4 — six-tile social proof row. Rendered only when the
101 // cached public-stats payload is available; absent → fall back to
102 // the small text-only `landing-stats` row.
103 const tiles = publicStats
104 ? buildSocialProofTiles(publicStats)
105 : null;
106
534f04aClaude107 // Block M1 — SSR-friendly fallbacks so the no-JS path still renders
108 // a populated block. The client-side poller will overwrite these
109 // every 30s anyway.
110 const liveQueued = liveFeed?.queued ?? [];
111 const liveMerges = liveFeed?.merges ?? [];
112 const liveReviews = liveFeed?.reviews ?? [];
113 const liveReviewCount = liveFeed?.reviewCount ?? 0;
114 const liveEntries = liveFeed?.feed ?? [];
115
2b821b7Claude116 return (
117 <>
fa880f2Claude118 <style dangerouslySetInnerHTML={{ __html: landingCss }} />
2b821b7Claude119
4c47454Claude120 <div class="landing-root">
dc26881CC LABS App121 {/* ---------- Hero ----------
122 Block U1 — Senior polish pass. Rebuilt for tighter rhythm:
123 · 2 primary CTAs (sign up + Claude Desktop)
124 · "Try the live demo" / "Compare to GitHub" demoted to
125 a tertiary text-link row
126 · Install snippet moved BELOW the CTAs as a "power users"
127 panel — used to sit above and crowded the buttons
128 · 4-stat rail kept but rendered as a tighter horizontal
129 strip with the gradient accent rule
130 · One new muted gradient orb absolutely positioned behind
131 everything so the section reads as a product page, not
132 a tutorial
133 · vertical rhythm = var(--space-6) between every block
134 */}
4c47454Claude135 <section class="landing-hero">
958d26aClaude136 <div class="landing-hero-bg" aria-hidden="true">
137 <div class="landing-hero-blob landing-hero-blob-1" />
138 <div class="landing-hero-blob landing-hero-blob-2" />
dc26881CC LABS App139 <div class="landing-hero-blob landing-hero-orb" />
958d26aClaude140 <div class="landing-hero-grid" />
2b821b7Claude141 </div>
958d26aClaude142
143 <div class="landing-hero-inner stagger">
144 <div class="eyebrow landing-hero-eyebrow">
145 <span class="landing-hero-pulse" />
146 v1 · pre-launch · {new Date().getFullYear()}
147 </div>
148
149 <h1 class="landing-hero-title display">
3575261Claude150 <span class="gradient-text">Write the spec. Gluecron ships it.</span>
958d26aClaude151 </h1>
152
153 <p class="landing-hero-sub">
3575261Claude154 Spec to PR in 90 seconds. Push to live in 25. AI review, auto-merge, deploy — automatic.
958d26aClaude155 </p>
156
adf5e18Claude157 {/* U1 — primary CTA row. "Migrate from GitHub" added as a
158 secondary CTA alongside sign-up to capture visitors who
159 already have GitHub repos and want a one-click move. */}
dc26881CC LABS App160 <div class="landing-hero-ctas" data-testid="hero-primary-ctas">
958d26aClaude161 <a href="/register" class="btn btn-primary btn-xl landing-cta-primary">
5f2e749Claude162 Sign up free
958d26aClaude163 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
164 </a>
adf5e18Claude165 {/* Migrate from GitHub — prominent secondary CTA */}
166 <a
167 href="/import"
168 class="btn btn-xl landing-cta-migrate"
169 data-testid="cta-migrate"
170 >
171 Migrate from GitHub
172 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
173 </a>
dc26881CC LABS App174 {/* BLOCK Q1 — one-click Claude Desktop install. */}
93fe97eClaude175 <a
176 href="/gluecron.dxt"
177 class="btn btn-xl landing-cta-dxt"
178 download
179 data-testid="cta-dxt"
180 >
181 Add to Claude Desktop
182 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
183 </a>
dc26881CC LABS App184 </div>
185
186 {/* U1 — tertiary text-link row.
187 Visually subordinate to the buttons above. Keeps the
188 "Try the live demo" + "Compare to GitHub" affordances
189 from L10 + the Q3 "try without signing up" /play link
190 without crowding the primary CTA row. */}
191 <div class="landing-hero-tertiary" data-testid="hero-tertiary-row">
192 <a href="/demo" class="landing-hero-tertiary-link" data-testid="cta-tertiary-demo">
5f2e749Claude193 Try the live demo
dc26881CC LABS App194 <span aria-hidden="true">{" →"}</span>
958d26aClaude195 </a>
dc26881CC LABS App196 <span class="landing-hero-tertiary-sep" aria-hidden="true">·</span>
197 <a href="/vs-github" class="landing-hero-tertiary-link" data-testid="cta-tertiary-vs">
52ad8b1Claude198 Compare to GitHub
dc26881CC LABS App199 <span aria-hidden="true">{" →"}</span>
52ad8b1Claude200 </a>
dc26881CC LABS App201 <span class="landing-hero-tertiary-sep" aria-hidden="true">·</span>
202 <a href="/play" class="landing-hero-tertiary-link" data-testid="cta-play">
203 Try it without signing up
cd4f63bTest User204 <span aria-hidden="true">{" →"}</span>
205 </a>
206 </div>
207
dc26881CC LABS App208 {/* U1 — power-users install snippet panel.
209 Moved BELOW the CTAs so it doesn't compete for the eye
210 with the primary "Sign up free" button. */}
211 <div class="landing-hero-install-wrap" aria-label="Power users install panel">
212 <div class="landing-hero-install-label">For power users</div>
213 <div class="landing-hero-install" aria-label="One-line install">
214 <code class="landing-hero-install-code">
215 <span class="landing-hero-install-prompt" aria-hidden="true">$</span>
216 <span id="landing-install-text">curl -sSL gluecron.com/install | bash</span>
217 </code>
218 <button
219 type="button"
220 class="landing-hero-install-copy"
221 data-copy-target="landing-install-text"
222 aria-label="Copy install command"
223 >
224 Copy
225 </button>
226 </div>
227 </div>
228
b5d207bClaude229 {/* U1 — tightened activity rail.
dc26881CC LABS App230 Same data as before, rendered as a single horizontal
231 rule with the gradient accent line on top. Numbers
232 smaller, copy still scannable. */}
5f2e749Claude233 {publicStats && (
3575261Claude234 <ul class="landing-hero-rail" aria-label="Gluecron live this week">
5f2e749Claude235 <li>
236 <strong>{publicStats.weeklyPrsAutoMerged.toLocaleString()}</strong>
dc26881CC LABS App237 <span class="landing-hero-rail-label">PRs auto-merged</span>
5f2e749Claude238 </li>
239 <li>
240 <strong>{publicStats.weeklyIssuesBuiltByAi.toLocaleString()}</strong>
dc26881CC LABS App241 <span class="landing-hero-rail-label">issues built by AI</span>
5f2e749Claude242 </li>
243 <li>
244 <strong>{publicStats.weeklyDeploysShipped.toLocaleString()}</strong>
3575261Claude245 <span class="landing-hero-rail-label">deploys shipped</span>
5f2e749Claude246 </li>
247 <li>
dc26881CC LABS App248 <strong>{`~${Math.round(publicStats.weeklyHoursSaved).toLocaleString()}`}</strong>
249 <span class="landing-hero-rail-label">hours saved by AI</span>
5f2e749Claude250 </li>
251 </ul>
252 )}
253
254 {/* L8 — free-tier reassurance link. Keeps anxiety low for the AI-curious. */}
255 <p class="landing-hero-freenote">
256 Free forever for the AI-curious.{" "}
257 <a href="/pricing" class="landing-hero-freenote-link">
258 See pricing &rarr;
259 </a>
260 </p>
261
958d26aClaude262 <p class="landing-hero-caption">
263 Already have a repo?
264 <span class="landing-hero-cmd">
265 <span class="kbd">git</span>
266 <span class="kbd">remote</span>
267 <span class="kbd">add</span>
268 <span class="kbd">gluecron</span>
269 <span class="landing-hero-arrow">{"→"}</span>
270 <span class="kbd">git push</span>
271 </span>
272 </p>
273
274 {hasStats && (
275 <p class="landing-stats">
276 {stats!.publicRepos !== undefined && stats!.publicRepos > 0 && (
277 <span>
278 <strong>{stats!.publicRepos.toLocaleString()}</strong>
279 {stats!.publicRepos === 1 ? " repo" : " repos"}
280 </span>
281 )}
282 {stats!.publicRepos !== undefined &&
283 stats!.publicRepos > 0 &&
284 stats!.users !== undefined &&
285 stats!.users > 0 && <span class="landing-stats-sep">·</span>}
286 {stats!.users !== undefined && stats!.users > 0 && (
287 <span>
288 <strong>{stats!.users.toLocaleString()}</strong>
289 {stats!.users === 1 ? " developer" : " developers"}
290 </span>
291 )}
292 <span class="landing-stats-sep">·</span>
4c47454Claude293 <span>
958d26aClaude294 <strong>100%</strong> AI-native
4c47454Claude295 </span>
958d26aClaude296 </p>
297 )}
298 </div>
c963db5Claude299 </section>
c475ee6Claude300
534f04aClaude301 {/* ---------- Block M1 — Live-now demo feed ---------- */}
302 <LiveNowSection
303 queued={liveQueued}
304 merges={liveMerges}
305 reviews={liveReviews}
306 reviewCount={liveReviewCount}
307 feed={liveEntries}
308 />
309
52ad8b1Claude310 {/* ---------- L4 social-proof counters (animated count-up) ---------- */}
311 {tiles && (
312 <section class="landing-counters" aria-label="Gluecron live counters">
313 <div class="landing-counters-grid">
314 {tiles.map((t) => (
315 <div class="landing-counter">
316 <div
317 class="landing-counter-num"
318 data-counter-target={String(t.value)}
319 data-counter-suffix={t.suffix ?? ""}
320 data-counter-prefix={t.prefix ?? ""}
321 >
322 {t.prefix ?? ""}
323 {t.value.toLocaleString()}
324 {t.suffix ?? ""}
325 </div>
326 <div class="landing-counter-label">{t.label}</div>
327 </div>
328 ))}
329 </div>
330 <script dangerouslySetInnerHTML={{ __html: landingCountersJs }} />
331 </section>
332 )}
333
5f2e749Claude334 {/* ---------- L10 — Three reasons to switch ---------- */}
335 <section class="landing-section landing-reasons" aria-label="Three reasons to switch">
336 <div class="section-header">
337 <div class="eyebrow">Three reasons to switch</div>
338 <h2>Built so Claude can do the work.</h2>
339 </div>
340 <div class="landing-reasons-grid">
341 <ReasonCard
342 icon={
343 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
344 <path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z" />
345 </svg>
346 }
3575261Claude347 title="Ships in seconds, not tabs"
348 body="Spec to draft PR in 90 seconds. AI review posted in under 10. Push to live in 25. Every step streams in real time — no polling, no waiting on a CI tab. Or let Sleep Mode batch it for when you're away."
349 link={{ href: "/sleep-mode", label: "See Sleep Mode" }}
5f2e749Claude350 />
351 <ReasonCard
352 icon={
353 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
354 <polyline points="4 17 10 11 4 5" />
355 <line x1="12" y1="19" x2="20" y2="19" />
356 </svg>
357 }
358 title="One command to migrate"
359 body="Drop a single curl into your shell. Gluecron rehosts your repo, your issues, your branches — no SaaS rip-and-replace project required."
360 extra={
361 <code class="landing-reasons-code">curl -sSL gluecron.com/install | bash</code>
362 }
363 link={{ href: "/import", label: "Or import from GitHub" }}
364 />
365 <ReasonCard
366 icon={
367 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
368 <polygon points="5 3 19 12 5 21 5 3" />
369 </svg>
370 }
371 title="Open the demo, watch it work"
3575261Claude372 body="The demo repo is real. Label an issue, watch Claude open the PR in seconds. Inspect the diff. Approve the merge. Zero setup, zero credit card."
5f2e749Claude373 link={{ href: "/demo", label: "Open the live demo" }}
374 />
375 </div>
376 </section>
377
9ecf5a4Claude378 {/* ---------- Capability strip — uppercase tracked grid (vapron-style) ---------- */}
c963db5Claude379 <section class="landing-caps">
380 <div class="landing-caps-grid">
381 <span class="landing-cap">Claude-powered AI</span>
382 <span class="landing-cap">Spec-to-PR</span>
383 <span class="landing-cap">Auto-repair</span>
384 <span class="landing-cap">Real-time gates</span>
385 <span class="landing-cap">MCP-native</span>
386 <span class="landing-cap">Workflow runner</span>
387 <span class="landing-cap">Self-hostable</span>
388 <span class="landing-cap">Branch protection</span>
389 <span class="landing-cap">Bun + Hono</span>
390 <span class="landing-cap">Drizzle + Postgres</span>
391 <span class="landing-cap">JSX server-rendered</span>
392 <span class="landing-cap">Type-safe end to end</span>
c475ee6Claude393 </div>
958d26aClaude394 </section>
395
9ecf5a4Claude396 {/* ---------- Big stat row (vapron-style hero closer) ---------- */}
c963db5Claude397 <section class="landing-bigstats">
398 <div class="landing-bigstats-grid">
399 <div class="landing-bigstat">
400 <div class="landing-bigstat-num">Claude-powered</div>
401 <div class="landing-bigstat-label">The best AI, native</div>
402 </div>
403 <div class="landing-bigstat">
404 <div class="landing-bigstat-num">Self-hosted</div>
405 <div class="landing-bigstat-label">On your hardware</div>
406 </div>
407 <div class="landing-bigstat">
408 <div class="landing-bigstat-num">MCP-native</div>
409 <div class="landing-bigstat-label">Claude · Cursor · Code</div>
410 </div>
411 <div class="landing-bigstat">
412 <div class="landing-bigstat-num">Real-time</div>
413 <div class="landing-bigstat-label">SSE everywhere</div>
414 </div>
958d26aClaude415 </div>
4c47454Claude416 </section>
417
418 {/* ---------- Feature grid ---------- */}
958d26aClaude419 <section class="landing-section">
420 <div class="section-header">
421 <div class="eyebrow">The platform</div>
422 <h2>An IDE for your repo, not just a host.</h2>
423 <p>
424 Gluecron ships the surfaces GitHub charges extra for, and the
425 ones it never built. AI is a teammate with its own commits, not
426 a sidebar.
2b821b7Claude427 </p>
428 </div>
4c47454Claude429
958d26aClaude430 <div class="landing-features stagger">
431 <FeatureCard
432 icon={
433 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
434 <path d="M12 3l1.9 4.6L18.5 9l-3.6 3 1 4.8L12 14.5 8.1 16.8l1-4.8L5.5 9l4.6-1.4z" />
435 </svg>
436 }
437 title="AI as a teammate"
438 desc="Spec-to-PR drafts entire features from plain English. Auto-explain reviews every diff. The AI commits with its own bot account, visible in your history."
439 />
440 <FeatureCard
441 icon={
442 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
443 <path d="M12 2.5l8 3.5v6c0 5-3.5 8.5-8 9.5-4.5-1-8-4.5-8-9.5v-6z" />
444 <path d="M9 12l2 2 4-4" />
445 </svg>
446 }
447 title="Quality gate that learns"
448 desc="GateTest scans every push. Auto-repair fixes regressions before you see them. Required checks block bad PRs from merging. Your software self-corrects."
449 />
450 <FeatureCard
451 icon={
452 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
453 <path d="M13 2L4 14h7l-1 8 9-12h-7z" />
454 </svg>
455 }
456 title="Real-time everything"
457 desc="Live workflow logs over SSE. Live PR review presence. Live deploys you watch happen. No polling, no refresh, no waiting on a CI tab."
458 />
459 <FeatureCard
460 icon={
461 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
462 <rect x="3" y="3" width="18" height="18" rx="3" />
463 <path d="M3 9h18M9 21V9" />
464 </svg>
465 }
466 title="Workflow runner"
467 desc="Drop a yaml in `.gluecron/workflows/` and it runs on every push. Cron triggers, secret substitution, matrix runs, artifacts. No SaaS provider in the loop."
468 />
469 <FeatureCard
470 icon={
471 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
472 <circle cx="12" cy="12" r="9" />
473 <path d="M3 12h18M12 3a14 14 0 010 18M12 3a14 14 0 000 18" />
474 </svg>
475 }
476 title="MCP-native"
477 desc="Claude, Cursor, Code — they speak Model Context Protocol. Gluecron exposes search, file read, issues, codebase explain as MCP tools by default."
478 />
479 <FeatureCard
480 icon={
481 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
482 <path d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
483 <path d="M12 7v5l3 2" />
484 </svg>
485 }
486 title="Yours, on your hardware"
487 desc="Single-binary Bun runtime. Postgres + bare git repos on a volume. Deploy to Fly, Railway, your own VPS. No vendor lock, no surprise bills."
488 />
2b821b7Claude489 </div>
958d26aClaude490 </section>
2b821b7Claude491
958d26aClaude492 {/* ---------- Workflow walkthrough ---------- */}
493 <section class="landing-section landing-walk">
494 <div class="section-header">
495 <div class="eyebrow">How it works</div>
496 <h2>Push code. Watch it ship.</h2>
497 <p>
498 Every push triggers the same pipeline whether the commit came
499 from you, from CI, or from an AI agent.
4c47454Claude500 </p>
501 </div>
958d26aClaude502
503 <div class="landing-walk-grid">
504 <WalkStep n="01" title="Push" desc="git push to gluecron — Smart-HTTP, SSH, or via the web editor." />
505 <WalkStep n="02" title="Gate" desc="GateTest runs. Secret scanner runs. AI security review posts inline comments." />
506 <WalkStep n="03" title="Repair" desc="If a gate fails, auto-repair tries to fix it. New commit gets re-gated." />
9ecf5a4Claude507 <WalkStep n="04" title="Ship" desc="Green push to default branch fires deploy webhook. Vapron, Fly, your prod." />
958d26aClaude508 </div>
4c47454Claude509 </section>
2b821b7Claude510
4c47454Claude511 {/* ---------- Terminal block ---------- */}
958d26aClaude512 <section class="landing-section landing-terminal-section">
513 <div class="landing-terminal-wrap">
514 <div class="landing-terminal" role="img" aria-label="Example git push to gluecron with passing gates">
515 <div class="landing-terminal-chrome">
516 <span class="landing-terminal-dot landing-terminal-dot-r" />
517 <span class="landing-terminal-dot landing-terminal-dot-y" />
518 <span class="landing-terminal-dot landing-terminal-dot-g" />
519 <span class="landing-terminal-title">~/your-repo &mdash; zsh</span>
520 </div>
521 <div class="landing-terminal-body">
522 <div class="landing-term-line">
523 <span class="landing-term-prompt">$</span>
524 <span>git remote add gluecron https://gluecron.com/you/your-repo.git</span>
525 </div>
526 <div class="landing-term-line">
527 <span class="landing-term-prompt">$</span>
528 <span>git push -u gluecron main</span>
529 </div>
530 <div class="landing-term-line landing-term-out">
531 <span class="landing-term-meta">remote:</span>
532 <span>Resolving deltas… 100% (24/24)</span>
533 </div>
534 <div class="landing-term-line landing-term-out landing-term-ok-line">
535 <span class="landing-term-ok">{"✓"}</span>
536 <span>pushed to gluecron.com/you/your-repo</span>
537 </div>
538 <div class="landing-term-line landing-term-out landing-term-ok-line">
539 <span class="landing-term-ok">{"✓"}</span>
540 <span>GateTest passed (12 rules, 0 violations)</span>
541 </div>
542 <div class="landing-term-line landing-term-out landing-term-ok-line">
543 <span class="landing-term-ok">{"✓"}</span>
544 <span>AI review posted (2 suggestions, 0 blockers)</span>
545 </div>
546 <div class="landing-term-line landing-term-out landing-term-ok-line">
547 <span class="landing-term-ok">{"✓"}</span>
548 <span>deployed to your-repo.gluecron.com <span class="landing-term-meta">(4.1s)</span></span>
549 </div>
550 <div class="landing-term-line landing-term-cursor">
551 <span class="landing-term-prompt">$</span>
552 <span class="landing-term-blink">▍</span>
553 </div>
554 </div>
4c47454Claude555 </div>
958d26aClaude556 </div>
557 </section>
558
559 {/* ---------- Comparison ---------- */}
560 <section class="landing-section">
561 <div class="section-header">
562 <div class="eyebrow">vs the incumbent</div>
563 <h2>Everything GitHub charges for. And the parts they didn't build.</h2>
564 </div>
565
566 <div class="landing-compare">
567 <CompareRow feature="Git hosting + Smart-HTTP push" them="✓" us="✓" />
568 <CompareRow feature="Issues, PRs, code review" them="✓" us="✓" />
569 <CompareRow feature="Workflow runner (Actions-equivalent)" them="paid minutes" us="self-hosted, unmetered" highlight />
570 <CompareRow feature="AI code review on every PR" them="Copilot subscription" us="built in" highlight />
571 <CompareRow feature="Spec-to-PR (NL feature → draft PR)" them="—" us="✓" highlight />
572 <CompareRow feature="Auto-repair on failed gates" them="—" us="✓" highlight />
573 <CompareRow feature="Real-time SSE for logs + PRs" them="polling" us="streaming" highlight />
574 <CompareRow feature="MCP server (Claude / Cursor)" them="—" us="✓" highlight />
575 <CompareRow feature="Self-host on your own infra" them="enterprise tier" us="single binary" highlight />
576 <CompareRow feature="Pre-receive policy enforcement" them="rulesets (GHE)" us="✓" />
577 </div>
578 </section>
579
580 {/* ---------- Pricing teaser ---------- */}
581 <section class="landing-section">
582 <div class="section-header">
583 <div class="eyebrow">Pricing</div>
584 <h2>Free to start. Honest at scale.</h2>
585 <p>
586 Self-hosting is free forever. Hosted plans price the AI calls,
587 not the seats.
588 </p>
589 </div>
590
591 <div class="landing-pricing">
592 <PricingCard
593 tier="Free"
594 price="$0"
595 cadence="forever"
596 desc="For personal projects + open source. Public + private repos, full AI suite, fair quotas."
597 features={["Unlimited public repos", "3 private repos", "5K AI calls / mo", "Community support"]}
598 cta="Start free"
599 href="/register"
600 />
601 <PricingCard
602 tier="Pro"
603 price="$12"
604 cadence="per user / mo"
605 desc="For working developers. Lifts every quota, adds priority routing, no Gluecron branding on deploys."
606 features={["Unlimited private repos", "100K AI calls / mo", "Priority queue", "Custom domains"]}
607 cta="Go Pro"
608 href="/settings/billing"
609 highlight
610 />
611 <PricingCard
612 tier="Team"
613 price="Talk to us"
614 cadence="custom"
615 desc="For orgs running production on Gluecron. SSO, audit retention, enterprise SLA, on-prem."
616 features={["SSO + SCIM", "On-prem deploy", "Dedicated capacity", "24/7 incident response"]}
617 cta="Contact"
618 href="mailto:hello@gluecron.com"
619 />
620 </div>
621 </section>
622
5f2e749Claude623 {/* ---------- L10 — "How is this different?" pull-quote ---------- */}
624 <section class="landing-pullquote-section" aria-label="How is this different from GitHub?">
625 <figure class="landing-pullquote">
626 <div class="landing-pullquote-eyebrow">How is this different from GitHub?</div>
627 <blockquote class="landing-pullquote-text">
628 Every other host bolts AI on as a sidecar. Gluecron is the first
629 git host where Claude is a first-class developer. Built to be
630 operated by AI agents, not just augmented by them.
631 </blockquote>
632 <a href="/vs-github" class="landing-pullquote-link">
633 See the full comparison
634 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
635 </a>
636 </figure>
637 </section>
638
958d26aClaude639 {/* ---------- Closing CTA ---------- */}
640 <section class="landing-cta-section">
641 <div class="landing-cta-card">
642 <div class="landing-cta-bg" aria-hidden="true" />
643 <div class="eyebrow">Ready when you are</div>
644 <h2 class="landing-cta-title">
645 Stop maintaining the platform.<br />
646 <span class="gradient-text">Start shipping the product.</span>
647 </h2>
648 <p class="landing-cta-sub">
649 Free to start, self-hosted-friendly, MCP-native. Migrate from
650 GitHub in one click.
651 </p>
652 <div class="landing-cta-buttons">
653 <a href="/register" class="btn btn-primary btn-xl">
654 Create your account
655 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
656 </a>
657 <a href="/import" class="btn btn-ghost btn-xl">
658 Migrate a repo
659 </a>
4c47454Claude660 </div>
9a8b8deClaude661 <div style="margin-top:var(--space-3);font-size:13.5px;color:var(--text-muted)">
662 Migrating from GitHub?{" "}
663 <a href="/migrate" style="color:var(--accent)">
664 Import your entire org in one click &rarr;
665 </a>
666 </div>
4c47454Claude667 </div>
668 </section>
5f2e749Claude669
670 {/* L10 — clipboard copy script for the hero install snippet. */}
671 <script dangerouslySetInnerHTML={{ __html: landingCopyJs }} />
4c47454Claude672 </div>
2b821b7Claude673 </>
674 );
675};
676
958d26aClaude677const FeatureCard: FC<{ icon: any; title: string; desc: string }> = ({
678 icon,
679 title,
680 desc,
681}) => (
682 <div class="landing-feature">
683 <div class="landing-feature-icon" aria-hidden="true">
684 {icon}
685 </div>
686 <h3 class="landing-feature-title">{title}</h3>
687 <p class="landing-feature-desc">{desc}</p>
688 </div>
689);
690
5f2e749Claude691// Block L10 — "Three reasons to switch" column.
692const ReasonCard: FC<{
693 icon: any;
694 title: string;
695 body: string;
696 link: { href: string; label: string };
697 extra?: any;
698}> = ({ icon, title, body, link, extra }) => (
699 <div class="landing-reason">
700 <div class="landing-reason-icon" aria-hidden="true">
701 {icon}
702 </div>
703 <h3 class="landing-reason-title">{title}</h3>
704 <p class="landing-reason-body">{body}</p>
705 {extra}
706 <a href={link.href} class="landing-reason-link">
707 {link.label}
708 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
709 </a>
710 </div>
711);
712
958d26aClaude713const WalkStep: FC<{ n: string; title: string; desc: string }> = ({
714 n,
715 title,
716 desc,
717}) => (
718 <div class="landing-walk-step">
719 <div class="landing-walk-num">{n}</div>
720 <h3 class="landing-walk-title">{title}</h3>
721 <p class="landing-walk-desc">{desc}</p>
722 </div>
723);
724
725const CompareRow: FC<{
726 feature: string;
727 them: string;
728 us: string;
729 highlight?: boolean;
730}> = ({ feature, them, us, highlight }) => (
731 <div class={`landing-compare-row${highlight ? " landing-compare-hl" : ""}`}>
732 <div class="landing-compare-feature">{feature}</div>
733 <div class="landing-compare-them">{them}</div>
734 <div class="landing-compare-us">{us === "✓" ? "✓" : us}</div>
735 </div>
736);
737
738const PricingCard: FC<{
739 tier: string;
740 price: string;
741 cadence: string;
742 desc: string;
743 features: string[];
744 cta: string;
745 href: string;
746 highlight?: boolean;
747}> = ({ tier, price, cadence, desc, features, cta, href, highlight }) => (
748 <div class={`landing-price-card${highlight ? " landing-price-hl" : ""}`}>
749 {highlight && <div class="landing-price-badge">Most popular</div>}
750 <div class="landing-price-tier">{tier}</div>
751 <div class="landing-price-amount">
752 <span class="landing-price-num">{price}</span>
753 <span class="landing-price-cad">{cadence}</span>
754 </div>
755 <p class="landing-price-desc">{desc}</p>
756 <ul class="landing-price-features">
757 {features.map((f) => (
758 <li>
759 <span class="landing-price-check" aria-hidden="true">{"✓"}</span>
760 {f}
761 </li>
762 ))}
763 </ul>
764 <a
765 href={href}
766 class={`btn ${highlight ? "btn-primary" : "btn-secondary"} btn-block landing-price-cta`}
767 >
768 {cta}
769 </a>
770 </div>
771);
772
52ad8b1Claude773// ─────────────────────────────────────────────────────────────────
774// Block L4 — social-proof tile builder.
775//
776// Pure: takes the cached PublicStats payload and emits the six
777// landing-page tiles in render order. Exported so tests / future
778// surfaces (dashboard, /about, …) can share the exact same copy.
779// ─────────────────────────────────────────────────────────────────
780
781export interface SocialProofTile {
782 label: string;
783 value: number;
784 prefix?: string;
785 suffix?: string;
786}
787
788export function buildSocialProofTiles(s: PublicStats): SocialProofTile[] {
789 return [
790 { label: "Public repos", value: s.totalPublicRepos },
791 { label: "Developers", value: s.totalUsers },
792 {
793 label: "PRs auto-merged this week",
794 value: s.weeklyPrsAutoMerged,
795 },
796 {
797 label: "Issues built by AI this week",
798 value: s.weeklyIssuesBuiltByAi,
799 },
800 {
801 label: "Deploys shipped this week",
802 value: s.weeklyDeploysShipped,
803 },
804 {
805 label: "Hours saved this week",
806 // Round to whole hours for the tile — the precise 0.1 figure
807 // lives on the dashboard widget; the marketing surface keeps
808 // the number scannable.
809 value: Math.round(s.weeklyHoursSaved),
810 prefix: "~",
811 suffix: "h",
812 },
813 ];
814}
815
534f04aClaude816// ─────────────────────────────────────────────────────────────────
817// Block M1 — Live-now demo feed.
818//
819// A 4-tile row inserted between the hero and the L4 counters tile
820// section, surfacing real autopilot activity from the seeded `demo`
821// owner's repos:
822// 1. Issues queued for AI build (ai:build label, open)
823// 2. PRs auto-merged in the last 24h
824// 3. AI reviews posted today (count + latest 3)
825// 4. Combined activity feed (last 10)
826//
827// SSR-renders an initial snapshot, then re-fetches every 30s via the
828// L3 JSON endpoints so the page feels alive without a websocket.
829// Pure presentational; the route layer owns the DB reads.
830// ─────────────────────────────────────────────────────────────────
831
832/**
833 * Render an ISO timestamp (or Date) as a coarse "about N units ago"
834 * string. Tolerates strings, Dates, NaN, and future timestamps.
835 *
836 * Exported for unit testing.
837 */
838export function relativeTimeFromNow(
839 value: string | Date | number | null | undefined,
840 now: number = Date.now()
841): string {
842 if (value === null || value === undefined) return "just now";
843 let t: number;
844 if (value instanceof Date) {
845 t = value.getTime();
846 } else if (typeof value === "number") {
847 t = value;
848 } else {
849 t = new Date(value).getTime();
850 }
851 if (!Number.isFinite(t)) return "just now";
852 const delta = now - t;
853 // Future timestamps (clock skew) — treat as "just now" rather than
854 // surfacing a confusing negative.
855 if (delta < 0) return "just now";
856 const s = Math.floor(delta / 1000);
857 if (s < 60) return "just now";
858 const m = Math.floor(s / 60);
859 if (m < 60) return `about ${m} minute${m === 1 ? "" : "s"} ago`;
860 const h = Math.floor(m / 60);
861 if (h < 24) return `about ${h} hour${h === 1 ? "" : "s"} ago`;
862 const d = Math.floor(h / 24);
863 return `about ${d} day${d === 1 ? "" : "s"} ago`;
864}
865
866function feedEntryId(e: LandingLiveFeedEntry): string {
867 const at = e.at instanceof Date ? e.at.toISOString() : String(e.at);
868 return `${e.kind}|${e.repo}|${e.ref.type}|${e.ref.number}|${at}`;
869}
870
871function feedEntryLabel(kind: LandingLiveFeedEntry["kind"]): string {
872 switch (kind) {
873 case "auto_merge.merged":
874 return "auto-merged";
875 case "ai_build.dispatched":
876 return "AI-build queued";
877 case "ai_review.posted":
878 return "AI review posted";
879 }
880}
881
882interface LiveNowSectionProps {
883 queued: LandingLiveFeedQueued[];
884 merges: LandingLiveFeedMerge[];
885 reviews: LandingLiveFeedReview[];
886 reviewCount: number;
887 feed: LandingLiveFeedEntry[];
888}
889
890const LiveNowSection: FC<LiveNowSectionProps> = ({
891 queued,
892 merges,
893 reviews,
894 reviewCount,
895 feed,
896}) => {
897 return (
898 <section class="landing-livenow" aria-labelledby="landing-livenow-h">
899 <div class="landing-livenow-head">
900 <div class="landing-livenow-eyebrow">
901 <span class="landing-livenow-pulse" aria-hidden="true" />
902 Live now
903 </div>
904 <h2 id="landing-livenow-h" class="landing-livenow-title">
905 Claude is working on demo repos as you read this.
906 </h2>
907 <p class="landing-livenow-sub">
908 Every card below is real data from the public{" "}
909 <code>{DEMO_USERNAME}/*</code> repos. Refreshes every 30 seconds.
910 </p>
911 </div>
912
913 <div class="landing-livenow-grid" data-livenow-grid>
914 {/* Card 1 — queued issues */}
915 <article class="landing-livecard" aria-labelledby="lc-queued-h">
916 <header class="landing-livecard-head">
917 <span class="landing-livecard-dot" aria-hidden="true" />
918 <h3 id="lc-queued-h" class="landing-livecard-title">
919 Issues queued for AI
920 </h3>
921 </header>
922 <ul class="landing-livecard-list" data-livecard="queued">
923 {queued.length === 0 ? (
924 <li class="landing-livecard-empty">
925 No queued AI builds — quiet right now.
926 </li>
927 ) : (
928 queued.slice(0, 3).map((i) => (
929 <li
930 class="landing-livecard-row"
931 data-row-id={`queued|${i.repo}|${i.number}`}
932 >
933 <a
934 class="landing-livecard-link"
935 href={`/${DEMO_USERNAME}/${i.repo}/issues/${i.number}`}
936 >
937 <span class="landing-livecard-num">#{i.number}</span>{" "}
938 <span class="landing-livecard-title-text">{i.title}</span>
939 </a>
940 <div class="landing-livecard-meta">
941 <span class="landing-livecard-repo">{i.repo}</span>
942 </div>
943 </li>
944 ))
945 )}
946 </ul>
947 </article>
948
949 {/* Card 2 — recently merged */}
950 <article class="landing-livecard" aria-labelledby="lc-merges-h">
951 <header class="landing-livecard-head">
952 <span class="landing-livecard-dot" aria-hidden="true" />
953 <h3 id="lc-merges-h" class="landing-livecard-title">
954 Recently merged by AI
955 </h3>
956 </header>
957 <ul class="landing-livecard-list" data-livecard="merges">
958 {merges.length === 0 ? (
959 <li class="landing-livecard-empty">
960 No auto-merges in the last 24h.
961 </li>
962 ) : (
963 merges.slice(0, 3).map((m) => (
964 <li
965 class="landing-livecard-row"
966 data-row-id={`merges|${m.repo}|${m.number}`}
967 >
968 <a
969 class="landing-livecard-link"
970 href={`/${DEMO_USERNAME}/${m.repo}/pulls/${m.number}`}
971 >
972 <span class="landing-livecard-num">#{m.number}</span>{" "}
973 <span class="landing-livecard-title-text">{m.title}</span>
974 </a>
975 <div class="landing-livecard-meta">
976 AI merged in{" "}
977 <span class="landing-livecard-repo">{m.repo}</span>{" "}
978 <span
979 class="landing-livecard-rel"
980 data-rel={
981 m.mergedAt instanceof Date
982 ? m.mergedAt.toISOString()
983 : String(m.mergedAt)
984 }
985 >
986 {relativeTimeFromNow(m.mergedAt)}
987 </span>
988 </div>
989 </li>
990 ))
991 )}
992 </ul>
993 </article>
994
995 {/* Card 3 — AI reviews */}
996 <article class="landing-livecard" aria-labelledby="lc-reviews-h">
997 <header class="landing-livecard-head">
998 <span class="landing-livecard-dot" aria-hidden="true" />
999 <h3 id="lc-reviews-h" class="landing-livecard-title">
1000 AI reviews posted
1001 </h3>
1002 </header>
1003 <div class="landing-livecard-bignum">
1004 <span
1005 class="landing-livecard-bignum-n"
1006 data-livecard-count="reviews"
1007 data-tick-target={String(reviewCount)}
1008 >
1009 {reviewCount.toLocaleString()}
1010 </span>
1011 <span class="landing-livecard-bignum-label">reviews today</span>
1012 </div>
1013 <ul class="landing-livecard-list" data-livecard="reviews">
1014 {reviews.length === 0 ? (
1015 <li class="landing-livecard-empty">
1016 No AI reviews in the last 24h.
1017 </li>
1018 ) : (
1019 reviews.slice(0, 3).map((r) => (
1020 <li
1021 class="landing-livecard-row"
1022 data-row-id={`reviews|${r.repo}|${r.prNumber}`}
1023 >
1024 <a
1025 class="landing-livecard-link"
1026 href={`/${DEMO_USERNAME}/${r.repo}/pulls/${r.prNumber}`}
1027 >
1028 <span class="landing-livecard-num">#{r.prNumber}</span>{" "}
1029 <span class="landing-livecard-snippet">
1030 {r.commentSnippet}
1031 </span>
1032 </a>
1033 <div class="landing-livecard-meta">
1034 <span class="landing-livecard-repo">{r.repo}</span>
1035 </div>
1036 </li>
1037 ))
1038 )}
1039 </ul>
1040 </article>
1041
1042 {/* Card 4 — activity feed */}
1043 <article class="landing-livecard" aria-labelledby="lc-feed-h">
1044 <header class="landing-livecard-head">
1045 <span class="landing-livecard-dot" aria-hidden="true" />
1046 <h3 id="lc-feed-h" class="landing-livecard-title">
1047 Activity feed
1048 </h3>
1049 </header>
1050 <ul class="landing-livecard-list landing-livecard-feed" data-livecard="feed">
1051 {feed.length === 0 ? (
1052 <li class="landing-livecard-empty">
1053 Quiet right now — check back in a minute.
1054 </li>
1055 ) : (
1056 feed.slice(0, 10).map((e) => {
1057 const path = e.ref.type === "pr" ? "pulls" : "issues";
1058 const id = feedEntryId(e);
1059 return (
1060 <li class="landing-livecard-feedrow" data-row-id={id}>
1061 <span
1062 class={`landing-livecard-kind landing-livecard-kind-${e.kind.replace(/\./g, "-")}`}
1063 >
1064 {feedEntryLabel(e.kind)}
1065 </span>{" "}
1066 <a
1067 class="landing-livecard-link"
1068 href={`/${DEMO_USERNAME}/${e.repo}/${path}/${e.ref.number}`}
1069 >
1070 {e.repo} #{e.ref.number}
1071 </a>{" "}
1072 <span
1073 class="landing-livecard-rel"
1074 data-rel={
1075 e.at instanceof Date ? e.at.toISOString() : String(e.at)
1076 }
1077 >
1078 {relativeTimeFromNow(e.at)}
1079 </span>
1080 </li>
1081 );
1082 })
1083 )}
1084 </ul>
1085 </article>
1086 </div>
1087
1088 <div class="landing-livenow-cta">
1089 <span class="landing-livenow-cta-text">
1090 Want this for your repos?
1091 </span>
1092 <a class="landing-livenow-cta-link" href="/register">
1093 Sign up free
1094 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
1095 </a>
1096 <span class="landing-livenow-cta-sep" aria-hidden="true">·</span>
1097 <a class="landing-livenow-cta-link" href="/demo">
1098 Try the live demo
1099 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
1100 </a>
1101 </div>
1102
1103 <script dangerouslySetInnerHTML={{ __html: liveNowJs }} />
1104 </section>
1105 );
1106};
1107
1108// Inline poller. Plain JS so we don't ship a separate bundle. Hits the
1109// four L3 JSON endpoints every 30s, re-renders the four cards, ticks
1110// the big number, refreshes relative timestamps, flashes new rows.
1111const liveNowJs = `
1112(function(){
1113try{
1114 var DEMO=${JSON.stringify(DEMO_USERNAME)};
1115 var INTERVAL=30000;
1116 function esc(s){return String(s==null?'':s).replace(/[&<>"']/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c];});}
1117 function rel(v){
1118 if(v==null) return 'just now';
1119 var t=(v instanceof Date)?v.getTime():(typeof v==='number'?v:new Date(v).getTime());
1120 if(!isFinite(t)) return 'just now';
1121 var d=Date.now()-t;
1122 if(d<0) return 'just now';
1123 var s=Math.floor(d/1000);
1124 if(s<60) return 'just now';
1125 var m=Math.floor(s/60);
1126 if(m<60) return 'about '+m+' minute'+(m===1?'':'s')+' ago';
1127 var h=Math.floor(m/60);
1128 if(h<24) return 'about '+h+' hour'+(h===1?'':'s')+' ago';
1129 var dd=Math.floor(h/24);
1130 return 'about '+dd+' day'+(dd===1?'':'s')+' ago';
1131 }
1132 function tickNumber(el,target){
1133 if(!el) return;
1134 var start=parseInt(el.getAttribute('data-tick-current')||'0',10)||0;
1135 if(start===target){el.textContent=target.toLocaleString();el.setAttribute('data-tick-current',String(target));return;}
1136 var dur=800,t0=performance.now();
1137 function step(now){
1138 var p=Math.min(1,(now-t0)/dur);
1139 var eased=1-Math.pow(1-p,3);
1140 var v=Math.round(start+(target-start)*eased);
1141 el.textContent=v.toLocaleString();
1142 if(p<1) requestAnimationFrame(step); else el.setAttribute('data-tick-current',String(target));
1143 }
1144 requestAnimationFrame(step);
1145 }
1146 function flashRow(li){
1147 if(!li) return;
1148 li.classList.add('landing-livecard-flash');
1149 setTimeout(function(){li.classList.remove('landing-livecard-flash');},1100);
1150 }
1151 function diffMount(ul,newHtml,newIds){
1152 if(!ul) return;
1153 var prev={};
1154 var nodes=ul.querySelectorAll('[data-row-id]');
1155 for(var i=0;i<nodes.length;i++){prev[nodes[i].getAttribute('data-row-id')]=true;}
1156 ul.innerHTML=newHtml;
1157 var fresh=ul.querySelectorAll('[data-row-id]');
1158 for(var j=0;j<fresh.length;j++){
1159 var id=fresh[j].getAttribute('data-row-id');
1160 if(id && !prev[id]) flashRow(fresh[j]);
1161 }
1162 }
1163 function pollQueued(){
1164 return fetch('/api/v2/demo/queued',{credentials:'omit'}).then(function(r){return r.json();}).then(function(d){
1165 var ul=document.querySelector('[data-livecard="queued"]');if(!ul) return;
1166 var items=(d&&d.items)||[];
1167 if(items.length===0){ul.innerHTML='<li class="landing-livecard-empty">No queued AI builds — quiet right now.</li>';return;}
1168 var ids=[];
1169 var html=items.slice(0,3).map(function(i){
1170 var id='queued|'+i.repo+'|'+i.number;ids.push(id);
1171 return '<li class="landing-livecard-row" data-row-id="'+esc(id)+'">'+
1172 '<a class="landing-livecard-link" href="/'+esc(DEMO)+'/'+esc(i.repo)+'/issues/'+i.number+'">'+
1173 '<span class="landing-livecard-num">#'+i.number+'</span> '+
1174 '<span class="landing-livecard-title-text">'+esc(i.title)+'</span></a>'+
1175 '<div class="landing-livecard-meta"><span class="landing-livecard-repo">'+esc(i.repo)+'</span></div></li>';
1176 }).join('');
1177 diffMount(ul,html,ids);
1178 }).catch(function(){});
1179 }
1180 function pollMerges(){
1181 return fetch('/api/v2/demo/merges',{credentials:'omit'}).then(function(r){return r.json();}).then(function(d){
1182 var ul=document.querySelector('[data-livecard="merges"]');if(!ul) return;
1183 var items=(d&&d.items)||[];
1184 if(items.length===0){ul.innerHTML='<li class="landing-livecard-empty">No auto-merges in the last 24h.</li>';return;}
1185 var ids=[];
1186 var html=items.slice(0,3).map(function(m){
1187 var id='merges|'+m.repo+'|'+m.number;ids.push(id);
1188 return '<li class="landing-livecard-row" data-row-id="'+esc(id)+'">'+
1189 '<a class="landing-livecard-link" href="/'+esc(DEMO)+'/'+esc(m.repo)+'/pulls/'+m.number+'">'+
1190 '<span class="landing-livecard-num">#'+m.number+'</span> '+
1191 '<span class="landing-livecard-title-text">'+esc(m.title)+'</span></a>'+
1192 '<div class="landing-livecard-meta">AI merged in <span class="landing-livecard-repo">'+esc(m.repo)+'</span> '+
1193 '<span class="landing-livecard-rel" data-rel="'+esc(m.mergedAt)+'">'+esc(rel(m.mergedAt))+'</span></div></li>';
1194 }).join('');
1195 diffMount(ul,html,ids);
1196 }).catch(function(){});
1197 }
1198 function pollReviews(){
1199 return fetch('/api/v2/demo/reviews',{credentials:'omit'}).then(function(r){return r.json();}).then(function(d){
1200 var ul=document.querySelector('[data-livecard="reviews"]');if(!ul) return;
1201 var bigEl=document.querySelector('[data-livecard-count="reviews"]');
1202 var n=(d&&typeof d.count==='number')?d.count:0;
1203 if(bigEl) tickNumber(bigEl,n);
1204 var items=(d&&d.items)||[];
1205 if(items.length===0){ul.innerHTML='<li class="landing-livecard-empty">No AI reviews in the last 24h.</li>';return;}
1206 var ids=[];
1207 var html=items.slice(0,3).map(function(r){
1208 var id='reviews|'+r.repo+'|'+r.prNumber;ids.push(id);
1209 return '<li class="landing-livecard-row" data-row-id="'+esc(id)+'">'+
1210 '<a class="landing-livecard-link" href="/'+esc(DEMO)+'/'+esc(r.repo)+'/pulls/'+r.prNumber+'">'+
1211 '<span class="landing-livecard-num">#'+r.prNumber+'</span> '+
1212 '<span class="landing-livecard-snippet">'+esc(r.commentSnippet)+'</span></a>'+
1213 '<div class="landing-livecard-meta"><span class="landing-livecard-repo">'+esc(r.repo)+'</span></div></li>';
1214 }).join('');
1215 diffMount(ul,html,ids);
1216 }).catch(function(){});
1217 }
1218 function pollFeed(){
1219 return fetch('/api/v2/demo/activity',{credentials:'omit'}).then(function(r){return r.json();}).then(function(d){
1220 var ul=document.querySelector('[data-livecard="feed"]');if(!ul) return;
1221 var entries=(d&&d.entries)||[];
1222 if(entries.length===0){ul.innerHTML='<li class="landing-livecard-empty">Quiet right now — check back in a minute.</li>';return;}
1223 var ids=[];
1224 var html=entries.slice(0,10).map(function(e){
1225 var path=(e.ref&&e.ref.type==='pr')?'pulls':'issues';
1226 var num=(e.ref&&e.ref.number)||0;
1227 var label=e.kind==='auto_merge.merged'?'auto-merged':(e.kind==='ai_build.dispatched'?'AI-build queued':'AI review posted');
1228 var kindCls=String(e.kind||'').replace(/\\./g,'-');
1229 var id=e.kind+'|'+e.repo+'|'+(e.ref&&e.ref.type)+'|'+num+'|'+e.at;ids.push(id);
1230 return '<li class="landing-livecard-feedrow" data-row-id="'+esc(id)+'">'+
1231 '<span class="landing-livecard-kind landing-livecard-kind-'+esc(kindCls)+'">'+esc(label)+'</span> '+
1232 '<a class="landing-livecard-link" href="/'+esc(DEMO)+'/'+esc(e.repo)+'/'+path+'/'+num+'">'+esc(e.repo)+' #'+num+'</a> '+
1233 '<span class="landing-livecard-rel" data-rel="'+esc(e.at)+'">'+esc(rel(e.at))+'</span></li>';
1234 }).join('');
1235 diffMount(ul,html,ids);
1236 }).catch(function(){});
1237 }
1238 function refreshRel(){
1239 var spans=document.querySelectorAll('.landing-livecard-rel[data-rel]');
1240 for(var i=0;i<spans.length;i++){
1241 spans[i].textContent=rel(spans[i].getAttribute('data-rel'));
1242 }
1243 }
1244 function tickAll(){pollQueued();pollMerges();pollReviews();pollFeed();}
1245 // Initial counter tick (count-up from 0) on first paint.
1246 var bigEl0=document.querySelector('[data-livecard-count="reviews"]');
1247 if(bigEl0){
1248 var target=parseInt(bigEl0.getAttribute('data-tick-target')||'0',10)||0;
1249 bigEl0.textContent='0';
1250 tickNumber(bigEl0,target);
1251 }
1252 refreshRel();
1253 setInterval(tickAll,INTERVAL);
1254 setInterval(refreshRel,INTERVAL);
1255 document.addEventListener('visibilitychange',function(){
1256 if(document.visibilityState==='visible'){tickAll();refreshRel();}
1257 });
1258}catch(_){}})();
1259`.trim();
1260
0c3eee5Claude1261// ============================================================
1262// Land2030 — 2030 homepage prelude (closed-loop showcase).
1263// ============================================================
1264//
1265// Renders BEFORE the existing LandingHero so every L10/U1/Q1/M1
1266// regression assertion keeps passing. CSS is scoped under
1267// `.land-2030-*` to avoid colliding with the older `.landing-*`
1268// styles. Inline SVG only — no extra deps.
1269
1270const ClosedLoopDiagram: FC = () => (
1271 <svg
1272 viewBox="0 0 1100 280"
1273 class="land-2030-loop-svg"
1274 role="img"
1275 aria-label="Closed loop: Spec to Code to AI Review to Tests to Merge to Deploy to Monitor to Patch"
1276 >
1277 <defs>
1278 <linearGradient id="land2030LoopLine" x1="0" y1="0" x2="1" y2="0">
ba23fe2Claude1279 <stop offset="0%" stop-color="#5b6ee8" stop-opacity="0.0" />
1280 <stop offset="20%" stop-color="#5b6ee8" stop-opacity="0.5" />
1281 <stop offset="80%" stop-color="#5b6ee8" stop-opacity="0.5" />
1282 <stop offset="100%" stop-color="#5b6ee8" stop-opacity="0.0" />
0c3eee5Claude1283 </linearGradient>
1284 <radialGradient id="land2030LoopNode" cx="0.5" cy="0.5" r="0.5">
ba23fe2Claude1285 <stop offset="0%" stop-color="#5b6ee8" stop-opacity="0.95" />
1286 <stop offset="100%" stop-color="#5b6ee8" stop-opacity="0.60" />
0c3eee5Claude1287 </radialGradient>
1288 </defs>
1289 {/* connecting curve */}
1290 <path
1291 d="M 60 140 C 220 40, 880 40, 1040 140 C 880 240, 220 240, 60 140 Z"
1292 fill="none"
1293 stroke="url(#land2030LoopLine)"
1294 stroke-width="2"
1295 class="land-2030-loop-path"
1296 />
1297 {[
1298 { x: 70, y: 140, label: "Spec" },
1299 { x: 215, y: 70, label: "Code" },
1300 { x: 410, y: 50, label: "AI Review" },
1301 { x: 605, y: 50, label: "Tests" },
1302 { x: 800, y: 70, label: "Merge" },
1303 { x: 950, y: 140, label: "Deploy" },
1304 { x: 800, y: 210, label: "Monitor" },
1305 { x: 410, y: 230, label: "Patch" },
1306 ].map((n) => (
1307 <g class="land-2030-loop-node">
1308 <circle cx={n.x} cy={n.y} r="22" fill="url(#land2030LoopNode)" />
1309 <circle
1310 cx={n.x}
1311 cy={n.y}
1312 r="22"
1313 fill="none"
1314 stroke="rgba(255,255,255,0.18)"
1315 stroke-width="1"
1316 />
1317 <text
1318 x={n.x}
1319 y={n.y + (n.y > 140 ? 48 : -32)}
1320 text-anchor="middle"
1321 class="land-2030-loop-text"
1322 fill="currentColor"
1323 >
1324 {n.label}
1325 </text>
1326 </g>
1327 ))}
1328 </svg>
1329);
1330
1331interface Land2030CardProps {
1332 icon: any;
1333 title: string;
1334 desc: string;
1335 href: string;
1336 cta?: string;
1337}
1338const Land2030Card: FC<Land2030CardProps> = ({ icon, title, desc, href, cta }) => (
1339 <a href={href} class="land-2030-card">
1340 <div class="land-2030-card-icon" aria-hidden="true">
1341 {icon}
1342 </div>
1343 <h3 class="land-2030-card-title">{title}</h3>
1344 <p class="land-2030-card-desc">{desc}</p>
1345 <span class="land-2030-card-cta">
1346 {cta ?? "Try it"}
1347 <span aria-hidden="true">{" →"}</span>
1348 </span>
1349 </a>
1350);
1351
1352interface Land2030FeatureItem {
1353 title: string;
1354 desc: string;
1355 href: string;
1356 status: "live" | "beta" | "soon";
1357}
1358const LAND_2030_FEATURES: Land2030FeatureItem[] = [
1359 { title: "Spec-to-PR", desc: "Write English. Ship code.", href: "/specs", status: "live" },
1360 { title: "Voice-to-PR", desc: "Talk. Ship code.", href: "/voice-to-pr", status: "live" },
1361 { title: "Repo chat", desc: "Rubber-duck with a semantic index of your repo.", href: "/ask", status: "live" },
1362 { title: "AI CI healer", desc: "Broken CI? Claude opens a fix PR.", href: "/inbox", status: "live" },
1363 { title: "AI patch generator", desc: "Security finding → patch PR, signed by Claude.", href: "/code-scanning", status: "live" },
1364 { title: "AI proactive monitor", desc: "Claude opens issues unprompted when it spots smells.", href: "/standups", status: "live" },
1365 { title: "AI commit messages", desc: "`gluecron commit` writes a great message for the staged diff.", href: "/help", status: "live" },
1366 { title: "AI release notes", desc: "Claude reads merged PRs and writes the changelog.", href: "/ai-changelog", status: "live" },
1367 { title: "Multi-repo refactor", desc: "One English request → coordinated PRs across N repos.", href: "/refactors", status: "live" },
1368 { title: "Migration assistant", desc: "Major-version upgrades drafted PR-by-PR.", href: "/migration-assistant", status: "live" },
1369 { title: "AI test generator", desc: "Every PR auto-gets tests for the new diff.", href: "/ai-tests", status: "beta" },
1370 { title: "PR slash commands", desc: "/merge, /rebase, /explain, /test — Claude runs them.", href: "/help", status: "live" },
1371 { title: "Live co-editing on PRs", desc: "Figma-style cursors on PR descriptions and reviews.", href: "/pulls", status: "beta" },
1372 { title: "Branch preview URLs", desc: "Every push → a sharable preview URL.", href: "/previews", status: "live" },
1373 { title: "Agent multiplayer", desc: "Per-agent sessions, budgets, and branch namespacing.", href: "/settings/agents", status: "live" },
1374 { title: "Continuous semantic index", desc: "Push-time embeddings keep search and chat fresh.", href: "/semantic-search", status: "live" },
1375 { title: "VS Code extension", desc: "Inbox, PRs, and repo chat — in your editor.", href: "/help", status: "soon" },
1376 { title: "Slack / Discord bot", desc: "Mentions, reviews, deploys — in your team chat.", href: "/help", status: "soon" },
1377];
1378
1379const Land2030: FC = () => (
1380 <>
1381 <style dangerouslySetInnerHTML={{ __html: land2030Css }} />
1382 <div class="land-2030-root">
1383 {/* ---------- 2030 HERO ---------- */}
1384 <section class="land-2030-hero" aria-label="Gluecron 2030">
1385 <div class="land-2030-hairline" aria-hidden="true" />
1386 <div class="land-2030-orb" aria-hidden="true" />
1387 <div class="land-2030-hero-inner">
1388 <div class="land-2030-eyebrow">
1389 <span class="land-2030-pulse" aria-hidden="true" />
1390 Gluecron — built for 2030
1391 </div>
1392 <h1 class="land-2030-display">
1393 <span class="land-2030-grad-1">Write</span>{" "}
1394 <span class="land-2030-grad-2">English.</span>{" "}
1395 <span class="land-2030-grad-3">Ship</span>{" "}
1396 <span class="land-2030-grad-4">code.</span>{" "}
1397 <span class="land-2030-grad-5">Gluecron.</span>
1398 </h1>
1399 <p class="land-2030-sub">
1400 The git platform built for the era when AI ships most of the code.
1401 </p>
1402 <div class="land-2030-cta-row">
1403 <a href="/register" class="btn btn-primary btn-xl land-2030-cta-primary">
1404 Start free
1405 <span aria-hidden="true">{" →"}</span>
1406 </a>
1407 <a href="#land-2030-loop" class="btn btn-xl land-2030-cta-secondary">
1408 Watch the loop
1409 <span aria-hidden="true">{" ↓"}</span>
1410 </a>
1411 </div>
1412 </div>
1413 </section>
1414
1415 {/* ---------- THE CLOSED LOOP ---------- */}
1416 <section id="land-2030-loop" class="land-2030-section">
1417 <div class="land-2030-section-head">
1418 <div class="land-2030-eyebrow land-2030-eyebrow-mini">The closed loop</div>
1419 <h2 class="land-2030-h2">One platform. No glue code.</h2>
1420 <p class="land-2030-lede">
1421 Spec → Code → AI Review → Tests → Merge → Deploy → Monitor → Patch.
1422 All on Gluecron. No GitHub + Copilot + Vercel + Sentry stitching
1423 needed.
1424 </p>
1425 </div>
1426 <div class="land-2030-loop-wrap">
1427 <ClosedLoopDiagram />
1428 </div>
1429 </section>
1430
1431 {/* ---------- THE 6 THINGS NOBODY ELSE CAN DO ---------- */}
1432 <section class="land-2030-section">
1433 <div class="land-2030-section-head">
1434 <div class="land-2030-eyebrow land-2030-eyebrow-mini">Unfair advantages</div>
1435 <h2 class="land-2030-h2">What Gluecron does that nobody else can.</h2>
1436 </div>
1437 <div class="land-2030-card-grid">
1438 <Land2030Card
1439 href="/voice-to-pr"
1440 title="Voice-to-PR"
1441 desc="Speak the change you want. Claude opens the PR. Works on your commute."
1442 icon={
1443 <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="2" width="6" height="12" rx="3" /><path d="M5 10v2a7 7 0 0 0 14 0v-2" /><path d="M12 19v3" /></svg>
1444 }
1445 />
1446 <Land2030Card
1447 href="/specs"
1448 title="Spec-to-PR"
1449 desc="Write the spec in plain English. Claude implements it, opens a PR, and asks for review."
1450 icon={
1451 <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" /><path d="M14 2v6h6" /><path d="M9 13h6M9 17h4" /></svg>
1452 }
1453 />
1454 <Land2030Card
1455 href="/workflows"
1456 title="AI CI self-healing"
1457 desc="Tests go red? Claude reads the log, finds the cause, and pushes the fix to your PR branch."
1458 icon={
1459 <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" /><polyline points="22 4 12 14.01 9 11.01" /></svg>
1460 }
1461 />
1462 <Land2030Card
1463 href="/refactors"
1464 title="Multi-repo refactor agent"
1465 desc="One English request → coordinated PRs across every repo that uses the symbol."
1466 icon={
1467 <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="6" r="3" /><circle cx="18" cy="6" r="3" /><circle cx="12" cy="18" r="3" /><path d="M6 9v3a3 3 0 0 0 3 3h6a3 3 0 0 0 3-3V9" /></svg>
1468 }
1469 />
1470 <Land2030Card
1471 href="/ask"
1472 title="Repo chat with semantic search"
1473 desc="Continuous push-time embeddings. Ask the repo anything; it cites real files and commits."
1474 icon={
1475 <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" /><circle cx="9" cy="10" r="1" fill="currentColor" /><circle cx="13" cy="10" r="1" fill="currentColor" /><circle cx="17" cy="10" r="1" fill="currentColor" /></svg>
1476 }
1477 />
1478 <Land2030Card
1479 href="/pulls"
1480 title="Per-PR live co-editing"
1481 desc="Figma-style cursors and presence on PR descriptions and reviews. Goodbye stale tabs."
1482 icon={
1483 <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11l3 3L22 4" /><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11" /></svg>
1484 }
1485 />
1486 </div>
1487 </section>
1488
1489 {/* ---------- GLOBAL DASHBOARDS ---------- */}
1490 <section class="land-2030-section">
1491 <div class="land-2030-section-head">
1492 <div class="land-2030-eyebrow land-2030-eyebrow-mini">Mission control</div>
1493 <h2 class="land-2030-h2">Every signal, one inbox.</h2>
1494 <p class="land-2030-lede">
1495 Five global dashboards that span every repo you touch — no more
1496 tab-juggling across orgs.
1497 </p>
1498 </div>
1499 <div class="land-2030-dash-grid">
1500 <a href="/pulls" class="land-2030-dash">
1501 <span class="land-2030-dash-name">/pulls</span>
1502 <span class="land-2030-dash-desc">PR command center across every repo you can touch.</span>
1503 </a>
1504 <a href="/issues" class="land-2030-dash">
1505 <span class="land-2030-dash-name">/issues</span>
1506 <span class="land-2030-dash-desc">Global issue dashboard. Triage from one screen.</span>
1507 </a>
1508 <a href="/inbox" class="land-2030-dash">
1509 <span class="land-2030-dash-name">/inbox</span>
1510 <span class="land-2030-dash-desc">Unified mentions, reviews, CI, and AI events.</span>
1511 </a>
1512 <a href="/activity" class="land-2030-dash">
1513 <span class="land-2030-dash-name">/activity</span>
1514 <span class="land-2030-dash-desc">A timeline of everything that moved on your repos.</span>
1515 </a>
1516 <a href="/standups" class="land-2030-dash">
1517 <span class="land-2030-dash-name">/standups</span>
1518 <span class="land-2030-dash-desc">Daily AI-generated brief of what shipped and what's stuck.</span>
1519 </a>
1520 </div>
1521 </section>
1522
1523 {/* ---------- FULL AI FEATURE GRID ---------- */}
1524 <section class="land-2030-section">
1525 <div class="land-2030-section-head">
1526 <div class="land-2030-eyebrow land-2030-eyebrow-mini">Closed-loop AI</div>
1527 <h2 class="land-2030-h2">18 AI features. One platform.</h2>
1528 <p class="land-2030-lede">
1529 Most of these don't exist anywhere else. None of them require a
1530 second SaaS subscription.
1531 </p>
1532 </div>
1533 <div class="land-2030-feat-grid">
1534 {LAND_2030_FEATURES.map((f) => (
1535 <a href={f.href} class="land-2030-feat">
1536 <div class="land-2030-feat-head">
1537 <span class="land-2030-feat-title">{f.title}</span>
1538 <span class={`land-2030-pill land-2030-pill-${f.status}`}>
1539 {f.status === "live" ? "live" : f.status === "beta" ? "beta" : "soon"}
1540 </span>
1541 </div>
1542 <p class="land-2030-feat-desc">{f.desc}</p>
1543 </a>
1544 ))}
1545 </div>
1546 </section>
1547
1548 {/* ---------- DEVELOPER EXPERIENCE ---------- */}
1549 <section class="land-2030-section">
1550 <div class="land-2030-section-head">
1551 <div class="land-2030-eyebrow land-2030-eyebrow-mini">Developer surface</div>
1552 <h2 class="land-2030-h2">Built where you already are.</h2>
1553 </div>
1554 <div class="land-2030-dx-grid">
1555 <div class="land-2030-dx">
1556 <h3 class="land-2030-dx-title">gluecron CLI</h3>
1557 <pre class="land-2030-code"><code>$ gluecron spec "add CSV export to /api/orders"
1558{"→ Drafting PR…"}
1559{"→ Opened #482 with 3 commits"}
1560{"→ AI review queued"}</code></pre>
1561 </div>
1562 <div class="land-2030-dx">
1563 <h3 class="land-2030-dx-title">PR slash commands</h3>
1564 <pre class="land-2030-code"><code>/merge — squash + merge when checks pass
1565/rebase — rebase onto base, push --force-with-lease
1566/explain — Claude explains the diff in plain English
1567/test — Claude writes tests for the new code</code></pre>
1568 </div>
1569 <div class="land-2030-dx">
1570 <h3 class="land-2030-dx-title">Branch preview URLs</h3>
1571 <pre class="land-2030-code"><code>{"→ git push gluecron HEAD"}
1572{"→ preview: https://pr-482.preview.gluecron.com"}
1573{"→ commented on PR #482"}</code></pre>
1574 </div>
1575 </div>
1576 </section>
1577
1578 {/* ---------- BUILT FOR AGENTS ---------- */}
1579 <section class="land-2030-section land-2030-section-dark">
1580 <div class="land-2030-section-head">
1581 <div class="land-2030-eyebrow land-2030-eyebrow-mini">Agent era</div>
1582 <h2 class="land-2030-h2">Built for agents, not just humans.</h2>
1583 <p class="land-2030-lede">
1584 Per-agent tokens, per-agent budgets, per-agent branch namespaces,
1585 and a lease primitive so 50 agents don't trample one repo.
1586 </p>
1587 </div>
1588 <div class="land-2030-agent-wrap">
1589 <pre class="land-2030-code land-2030-code-wide"><code>{'# agent gets a scoped token + lease before writing'}
1590{'curl -H "Authorization: Bearer agt_3p9x…" \\\\'}
1591{' -X POST https://gluecron.com/api/v2/leases \\\\'}
1592{' -d \'{"repo":"acme/api","branch":"agent/jules/checkout-fix","ttl":300}\''}
1593{''}
1594{'{ "lease_id": "lse_8a2f", "expires_at": "2030-05-25T14:05:11Z" }'}</code></pre>
1595 <div class="land-2030-agent-stat">
1596 <div class="land-2030-agent-big">10,000</div>
1597 <div class="land-2030-agent-label">
1598 agents pushing to your repo per day. Welcome to 2030.
1599 </div>
1600 <a href="/docs/build-agent-integration" class="land-2030-agent-link">
1601 Build an agent integration{" →"}
1602 </a>
1603 </div>
1604 </div>
1605 </section>
1606
1607 {/* ---------- VS GITHUB ---------- */}
1608 <section class="land-2030-section">
1609 <div class="land-2030-section-head">
1610 <div class="land-2030-eyebrow land-2030-eyebrow-mini">vs GitHub</div>
1611 <h2 class="land-2030-h2">One platform replaces five.</h2>
1612 </div>
1613 <div class="land-2030-vs">
1614 <div class="land-2030-vs-row land-2030-vs-head">
1615 <div>Today</div>
1616 <div>On Gluecron</div>
1617 </div>
1618 <div class="land-2030-vs-row">
1619 <div>GitHub + Copilot + Vercel + Sentry + Linear</div>
1620 <div class="land-2030-vs-us">Gluecron</div>
1621 </div>
1622 <a href="/vs-github" class="land-2030-vs-link">
1623 See the full comparison{" →"}
1624 </a>
1625 </div>
1626 </section>
1627 </div>
1628 </>
1629);
1630
4c47454Claude1631// Backwards-compatible default — web.tsx imports `LandingPage`.
a819f96Claude1632// Single landing surface. The bolted-on <Land2030 /> prelude was rendering a
1633// SECOND full page above LandingHero — two stacked <h1> documents as you
1634// scrolled. Render exactly one page; the 2030 reboot replaces this wholesale.
4c47454Claude1635export const LandingPage: FC<LandingPageProps> = (props) => (
a819f96Claude1636 <LandingHero {...props} />
2b821b7Claude1637);
1638
4c47454Claude1639export default LandingPage;
1640
2b821b7Claude1641const landingCss = `
958d26aClaude1642 /* ============================================================ */
1643 /* Landing — Editorial-Technical 2026.05 */
1644 /* ============================================================ */
4c47454Claude1645 .landing-root {
1646 position: relative;
958d26aClaude1647 max-width: 1180px;
4c47454Claude1648 margin: 0 auto;
1649 padding: 0 16px;
958d26aClaude1650 }
1651 .landing-root > section { position: relative; }
1652
1653 /* ---------- Hero ---------- */
1654 .landing-hero {
1655 position: relative;
c475ee6Claude1656 padding: var(--s-16) 0 var(--s-20);
958d26aClaude1657 text-align: center;
4c47454Claude1658 overflow: hidden;
1659 }
c475ee6Claude1660 .landing-hero-blob-1 {
1661 animation: hero-blob-drift-1 18s var(--ease, ease) infinite alternate;
1662 }
1663 .landing-hero-blob-2 {
1664 animation: hero-blob-drift-2 22s var(--ease, ease) infinite alternate;
1665 }
1666 @keyframes hero-blob-drift-1 {
1667 0% { transform: translate(0, 0) scale(1); opacity: 0.55; }
1668 100% { transform: translate(8%, 6%) scale(1.18); opacity: 0.75; }
1669 }
1670 @keyframes hero-blob-drift-2 {
1671 0% { transform: translate(0, 0) scale(1); opacity: 0.40; }
1672 100% { transform: translate(-10%, -4%) scale(1.25); opacity: 0.60; }
1673 }
1674
1675 /* ---------- Hero product visual: live AI PR review card ---------- */
1676 .landing-hero-visual {
1677 position: relative;
1678 max-width: 760px;
1679 margin: var(--s-12) auto 0;
1680 padding: 0 16px;
1681 perspective: 1400px;
1682 z-index: 2;
1683 opacity: 0;
1684 animation: hero-visual-in 700ms var(--ease-out-expo, cubic-bezier(0.19, 1, 0.22, 1)) 400ms forwards;
1685 }
1686 @keyframes hero-visual-in {
1687 from { opacity: 0; transform: translateY(20px); }
1688 to { opacity: 1; transform: translateY(0); }
1689 }
1690 .hero-pr-card {
1691 position: relative;
ba23fe2Claude1692 background: var(--bg-elevated);
c475ee6Claude1693 border: 1px solid var(--border-strong);
1694 border-radius: var(--r-xl);
1695 overflow: hidden;
1696 text-align: left;
ba23fe2Claude1697 box-shadow: 0 24px 64px -16px rgba(0,0,0,0.45), 0 0 0 1px var(--border-strong);
c475ee6Claude1698 transform: rotateX(2deg) rotateY(-2deg);
1699 transition: transform 600ms var(--ease, ease);
1700 }
1701 .landing-hero-visual:hover .hero-pr-card {
1702 transform: rotateX(0deg) rotateY(0deg);
1703 }
1704 .hero-pr-header {
1705 display: flex;
1706 align-items: center;
1707 gap: 12px;
1708 padding: 14px 18px;
ba23fe2Claude1709 border-bottom: 1px solid var(--border);
1710 background: var(--bg-secondary);
c475ee6Claude1711 font-size: 13px;
1712 }
1713 .hero-pr-dot {
1714 width: 10px; height: 10px;
1715 border-radius: 50%;
1716 background: var(--green);
1717 flex-shrink: 0;
1718 }
1719 .hero-pr-title {
1720 color: var(--text-strong);
1721 font-weight: 600;
1722 flex: 1;
1723 overflow: hidden;
1724 text-overflow: ellipsis;
1725 white-space: nowrap;
1726 }
1727 .hero-pr-num {
1728 color: var(--text-faint);
1729 font-family: var(--font-mono);
1730 font-weight: 500;
1731 margin-right: 8px;
1732 }
1733 .hero-pr-status {
1734 display: inline-flex;
1735 align-items: center;
1736 gap: 6px;
1737 padding: 3px 10px;
1738 border-radius: var(--r-full);
1739 background: var(--accent-gradient-faint);
ba23fe2Claude1740 border: 1px solid var(--border);
c475ee6Claude1741 color: var(--accent);
1742 font-family: var(--font-mono);
1743 font-size: 11px;
1744 letter-spacing: 0.04em;
1745 flex-shrink: 0;
1746 }
1747 .hero-pr-status-pulse {
1748 width: 6px; height: 6px;
1749 border-radius: 50%;
1750 background: var(--accent);
ba23fe2Claude1751 animation: heroPulse 2s ease-in-out infinite;
c475ee6Claude1752 }
1753
1754 .hero-pr-body {
1755 padding: 0;
1756 }
1757 .hero-pr-file {
1758 display: flex;
1759 align-items: center;
1760 gap: 10px;
1761 padding: 10px 18px;
1762 border-bottom: 1px solid rgba(255,255,255,0.05);
1763 font-family: var(--font-mono);
1764 font-size: 12px;
1765 background: rgba(255,255,255,0.012);
1766 }
1767 .hero-pr-file-icon { color: var(--accent-2); }
1768 .hero-pr-file-name { color: var(--text); flex: 1; }
1769 .hero-pr-file-stats { display: inline-flex; gap: 8px; }
1770 .hero-pr-add { color: var(--green); font-weight: 600; }
1771 .hero-pr-del { color: var(--red); font-weight: 600; }
1772
1773 .hero-pr-diff {
1774 padding: 12px 18px;
1775 font-family: var(--font-mono);
1776 font-feature-settings: var(--mono-feat, 'calt');
1777 font-size: 12.5px;
1778 line-height: 1.7;
1779 color: rgba(237,237,242,0.85);
1780 overflow-x: auto;
1781 }
1782 .hero-pr-hunk {
ba23fe2Claude1783 color: var(--accent);
1784 background: var(--accent-gradient-faint);
c475ee6Claude1785 padding: 2px 8px;
1786 margin: 0 -8px 4px;
1787 border-radius: 4px;
1788 }
1789 .hero-pr-line-add {
1790 background: rgba(52,211,153,0.08);
1791 color: rgba(167,243,208,0.95);
1792 padding: 0 8px;
1793 margin: 0 -8px;
1794 border-left: 2px solid var(--green);
1795 padding-left: 8px;
1796 }
1797
1798 .hero-pr-comment {
1799 margin: 14px 18px;
1800 padding: 14px 16px;
ba23fe2Claude1801 background: var(--bg-secondary);
1802 border: 1px solid var(--border);
c475ee6Claude1803 border-radius: var(--r-md);
1804 }
1805 .hero-pr-bot-row {
1806 display: flex;
1807 align-items: center;
1808 gap: 8px;
1809 margin-bottom: 8px;
1810 font-size: 12px;
1811 }
1812 .hero-pr-bot-avatar {
1813 width: 22px; height: 22px;
1814 display: inline-flex;
1815 align-items: center;
1816 justify-content: center;
1817 border-radius: 50%;
1818 background: var(--accent-gradient);
1819 font-size: 11px;
1820 }
1821 .hero-pr-bot-name {
1822 color: var(--text-strong);
1823 font-weight: 600;
1824 }
1825 .hero-pr-bot-meta {
1826 color: var(--text-faint);
1827 font-family: var(--font-mono);
1828 }
1829 .hero-pr-bot-text {
1830 color: var(--text);
1831 font-size: 13px;
1832 line-height: 1.55;
1833 margin: 0;
1834 }
1835 .hero-pr-bot-text code {
1836 background: rgba(255,255,255,0.06);
1837 border: 1px solid rgba(255,255,255,0.10);
1838 padding: 1px 6px;
1839 border-radius: 4px;
1840 font-size: 11.5px;
1841 color: var(--accent);
1842 }
1843 .hero-pr-bot-link { color: var(--accent-2); text-decoration: underline; text-decoration-style: dotted; }
1844
1845 .hero-pr-gates {
1846 display: flex;
1847 flex-wrap: wrap;
1848 gap: 6px;
1849 padding: 12px 18px 16px;
1850 border-top: 1px solid rgba(255,255,255,0.06);
1851 background: rgba(255,255,255,0.012);
1852 }
1853 .hero-pr-gate {
1854 display: inline-flex;
1855 align-items: center;
1856 gap: 6px;
1857 padding: 4px 10px;
1858 border-radius: var(--r-full);
1859 font-family: var(--font-mono);
1860 font-size: 11px;
1861 border: 1px solid;
1862 }
1863 .hero-pr-gate-pass {
1864 color: var(--green);
1865 background: rgba(52,211,153,0.08);
1866 border-color: rgba(52,211,153,0.30);
1867 }
1868 .hero-pr-gate-running {
1869 color: var(--accent);
1870 background: var(--accent-gradient-faint);
ba23fe2Claude1871 border-color: var(--border-strong);
c475ee6Claude1872 }
1873 .hero-pr-gate-spin {
1874 width: 9px; height: 9px;
ba23fe2Claude1875 border: 1.5px solid var(--border);
c475ee6Claude1876 border-top-color: var(--accent);
1877 border-radius: 50%;
1878 animation: hero-spin 800ms linear infinite;
1879 }
1880 @keyframes hero-spin {
1881 to { transform: rotate(360deg); }
1882 }
1883
1884 /* Floating accent badges around the card */
1885 .hero-float {
1886 position: absolute;
1887 display: inline-flex;
1888 align-items: center;
1889 gap: 6px;
1890 padding: 6px 12px;
ba23fe2Claude1891 background: var(--bg-elevated);
1892 border: 1px solid var(--border-strong);
c475ee6Claude1893 border-radius: var(--r-full);
1894 font-family: var(--font-mono);
1895 font-size: 11px;
1896 color: var(--text);
ba23fe2Claude1897 box-shadow: var(--elev-2);
c475ee6Claude1898 backdrop-filter: blur(8px);
1899 -webkit-backdrop-filter: blur(8px);
1900 }
1901 .hero-float-icon { color: var(--accent); }
1902 .hero-float-1 {
1903 top: -14px;
1904 left: -8px;
1905 animation: hero-float-bob-1 5s var(--ease, ease) infinite alternate;
1906 }
1907 .hero-float-2 {
1908 bottom: -14px;
1909 right: -8px;
1910 animation: hero-float-bob-2 6s var(--ease, ease) infinite alternate;
1911 }
1912 @keyframes hero-float-bob-1 {
1913 from { transform: translate(0, 0); }
1914 to { transform: translate(-8px, -10px); }
1915 }
1916 @keyframes hero-float-bob-2 {
1917 from { transform: translate(0, 0); }
1918 to { transform: translate(8px, 8px); }
1919 }
1920
1921 @media (max-width: 720px) {
1922 .landing-hero-visual { padding: 0 8px; }
1923 .hero-pr-card { transform: none; }
1924 .hero-pr-title { font-size: 12px; }
1925 .hero-pr-diff { font-size: 11px; line-height: 1.6; }
1926 .hero-float { display: none; }
1927 }
ba23fe2Claude1928 /* Hero background — single restrained radial glow, no blobs or grids.
1929 Professional tools (GitHub, Linear, Stripe) use clean backgrounds. */
958d26aClaude1930 .landing-hero-bg {
1931 position: absolute;
ba23fe2Claude1932 inset: 0;
958d26aClaude1933 pointer-events: none;
1934 z-index: 0;
4c47454Claude1935 }
ba23fe2Claude1936 .landing-hero-blob { display: none; }
1937 .landing-hero-blob-1 { display: none; }
1938 .landing-hero-blob-2 { display: none; }
dc26881CC LABS App1939 .landing-hero-orb {
ba23fe2Claude1940 position: absolute;
1941 top: -20%;
dc26881CC LABS App1942 left: 50%;
1943 transform: translateX(-50%);
ba23fe2Claude1944 width: 900px;
1945 height: 500px;
1946 background: radial-gradient(ellipse at center, rgba(91,110,232,0.08) 0%, transparent 70%);
1947 pointer-events: none;
dc26881CC LABS App1948 }
1949 :root[data-theme='light'] .landing-hero-orb {
ba23fe2Claude1950 background: radial-gradient(ellipse at center, rgba(67,83,201,0.05) 0%, transparent 70%);
4c47454Claude1951 }
ba23fe2Claude1952 .landing-hero-grid { display: none; }
4c47454Claude1953
958d26aClaude1954 .landing-hero-inner {
1955 position: relative;
1956 z-index: 1;
1957 max-width: 960px;
2b821b7Claude1958 margin: 0 auto;
1959 }
dc26881CC LABS App1960 /* U1 — every block below the headline obeys a single rhythm. */
958d26aClaude1961 .landing-hero-eyebrow {
dc26881CC LABS App1962 margin: 0 auto var(--space-6);
958d26aClaude1963 color: var(--accent);
1964 }
1965 .landing-hero-eyebrow::before { display: none; }
1966 .landing-hero-pulse {
1967 width: 7px;
1968 height: 7px;
1969 border-radius: 50%;
ba23fe2Claude1970 background: var(--green);
958d26aClaude1971 flex-shrink: 0;
ba23fe2Claude1972 animation: heroPulse 2.4s ease-in-out infinite;
958d26aClaude1973 }
ba23fe2Claude1974 @keyframes heroPulse {
1975 0%, 100% { opacity: 1; }
1976 50% { opacity: 0.45; }
958d26aClaude1977 }
1978
2b821b7Claude1979 .landing-hero-title {
3a5755eClaude1980 /* 2026 polish — bigger, bolder, tighter. Inter Tight at 800 weight
1981 with -0.03em tracking is the modern "AI-startup hero" look that
1982 Vercel/Linear/Cursor all use. clamp() scales gracefully on mobile. */
1983 font-size: clamp(40px, 7vw, 84px);
1984 line-height: 1.02;
1985 letter-spacing: -0.032em;
1986 font-weight: 800;
1987 font-family: var(--font-display);
dc26881CC LABS App1988 margin: 0 0 var(--space-6);
958d26aClaude1989 color: var(--text-strong);
2b821b7Claude1990 }
c963db5Claude1991 .landing-hero-title .gradient-text {
ba23fe2Claude1992 background-image: linear-gradient(135deg, var(--text-strong) 0%, var(--accent) 100%);
c963db5Claude1993 -webkit-background-clip: text;
1994 background-clip: text;
1995 -webkit-text-fill-color: transparent;
1996 color: transparent;
ba23fe2Claude1997 }
1998 :root[data-theme='light'] .landing-hero-title .gradient-text {
1999 background-image: linear-gradient(135deg, #0e1020 0%, #4353c9 100%);
c963db5Claude2000 }
958d26aClaude2001
2b821b7Claude2002 .landing-hero-sub {
3a5755eClaude2003 font-size: clamp(16px, 1.8vw, 22px);
2b821b7Claude2004 color: var(--text-muted);
958d26aClaude2005 max-width: 680px;
dc26881CC LABS App2006 margin: 0 auto var(--space-6);
3a5755eClaude2007 line-height: 1.5;
2008 letter-spacing: -0.008em;
2009 font-weight: 400;
2b821b7Claude2010 }
4c47454Claude2011
2b821b7Claude2012 .landing-hero-ctas {
2013 display: flex;
dc26881CC LABS App2014 gap: var(--space-3);
2b821b7Claude2015 justify-content: center;
2016 flex-wrap: wrap;
dc26881CC LABS App2017 margin-top: 0;
2018 margin-bottom: var(--space-4);
2b821b7Claude2019 }
958d26aClaude2020 .landing-cta-arrow {
2021 transition: transform var(--t-base) var(--ease-spring);
2022 display: inline-block;
2b821b7Claude2023 }
cd4f63bTest User2024
dc26881CC LABS App2025 /* U1 — tertiary text-link row.
2026 Sits directly under the 2-button primary CTA row. Smaller, muted,
2027 so it reads as "by the way" rather than competing for the eye. */
2028 .landing-hero-tertiary {
2029 margin-top: 0;
2030 margin-bottom: var(--space-6);
cd4f63bTest User2031 text-align: center;
dc26881CC LABS App2032 display: inline-flex;
2033 flex-wrap: wrap;
2034 gap: var(--space-2) var(--space-3);
2035 justify-content: center;
2036 align-items: baseline;
2037 width: 100%;
cd4f63bTest User2038 font-size: 13px;
2039 color: var(--text-muted);
dc26881CC LABS App2040 }
2041 .landing-hero-tertiary-link {
2042 color: var(--text-muted);
cd4f63bTest User2043 text-decoration: none;
2044 border-bottom: 1px dashed transparent;
2045 padding-bottom: 1px;
dc26881CC LABS App2046 transition: color var(--t-fast) var(--ease),
2047 border-color var(--t-fast) var(--ease);
cd4f63bTest User2048 }
dc26881CC LABS App2049 .landing-hero-tertiary-link:hover {
2050 color: var(--text-strong);
cd4f63bTest User2051 border-bottom-color: var(--text-muted);
2052 }
dc26881CC LABS App2053 .landing-hero-tertiary-sep {
2054 color: var(--text-faint);
2055 user-select: none;
2056 }
958d26aClaude2057 .btn:hover .landing-cta-arrow,
2058 .landing-cta-primary:hover .landing-cta-arrow {
2059 transform: translateX(4px);
8e9f1d9Claude2060 }
2b821b7Claude2061
93fe97eClaude2062 /* BLOCK Q1 — flagship "Add to Claude Desktop" CTA.
2063 Gradient-bordered + accent text so it reads as a peer of the primary
bc7654bCC LABS App2064 Sign-up CTA, not a third secondary. Theme-aware: inner fill uses
2065 --bg-elevated so it's white on light and dark on dark, never the
2066 jarring near-black on white we shipped first time. Subtle elevation
2067 on hover; static when the visitor opts out of motion. */
93fe97eClaude2068 .landing-cta-dxt {
2069 position: relative;
bc7654bCC LABS App2070 background: var(--bg-elevated);
2071 color: var(--text-strong);
93fe97eClaude2072 border: 1px solid transparent;
2073 background-image:
bc7654bCC LABS App2074 linear-gradient(var(--bg-elevated), var(--bg-elevated)),
ba23fe2Claude2075 linear-gradient(var(--border-strong), var(--border-strong));
93fe97eClaude2076 background-origin: border-box;
2077 background-clip: padding-box, border-box;
2078 transition: transform var(--t-base, 180ms) var(--ease-spring, ease),
2079 box-shadow var(--t-base, 180ms) var(--ease-spring, ease);
2080 }
2081 .landing-cta-dxt:hover {
2082 transform: translateY(-2px);
ba23fe2Claude2083 box-shadow: var(--elev-2);
93fe97eClaude2084 }
2085 @media (prefers-reduced-motion: reduce) {
2086 .landing-cta-dxt,
2087 .landing-cta-dxt:hover {
2088 transform: none;
2089 transition: none;
2090 }
2091 }
2092
adf5e18Claude2093 /* "Migrate from GitHub" CTA — secondary, but strong enough to stand
2094 alongside the primary. Uses a subtle amber/violet mix so it reads as
2095 action-oriented without competing with the green primary CTA. */
2096 .landing-cta-migrate {
2097 position: relative;
2098 background: var(--bg-elevated);
2099 color: var(--text-strong);
2100 border: 1px solid var(--border-strong);
2101 transition: border-color var(--t-base, 180ms) var(--ease, ease),
2102 transform var(--t-base, 180ms) var(--ease-spring, ease),
2103 box-shadow var(--t-base, 180ms) var(--ease, ease);
2104 }
2105 .landing-cta-migrate:hover {
ba23fe2Claude2106 border-color: var(--border-strong);
adf5e18Claude2107 transform: translateY(-2px);
ba23fe2Claude2108 box-shadow: var(--elev-2);
adf5e18Claude2109 text-decoration: none;
2110 color: var(--text-strong);
2111 }
2112 @media (prefers-reduced-motion: reduce) {
2113 .landing-cta-migrate,
2114 .landing-cta-migrate:hover {
2115 transform: none;
2116 transition: none;
2117 }
2118 }
2119
dc26881CC LABS App2120 /* L8 — free-tier reassurance link beneath the CTA row.
2121 U1 — rhythm snapped to var(--space-6). */
5f2e749Claude2122 .landing-hero-freenote {
dc26881CC LABS App2123 margin: 0 auto var(--space-6);
5f2e749Claude2124 font-size: var(--t-sm);
2125 color: var(--text-muted);
2126 text-align: center;
2127 }
2128 .landing-hero-freenote-link {
2129 color: var(--accent);
2130 text-decoration: none;
2131 font-weight: 500;
ba23fe2Claude2132 border-bottom: 1px dotted var(--border);
5f2e749Claude2133 transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
2134 }
2135 .landing-hero-freenote-link:hover {
2136 color: var(--text-strong);
2137 border-bottom-color: var(--accent);
2138 }
2139
4c47454Claude2140 .landing-hero-caption {
dc26881CC LABS App2141 margin: 0 auto var(--space-6);
958d26aClaude2142 font-size: var(--t-sm);
4c47454Claude2143 color: var(--text-muted);
958d26aClaude2144 display: flex;
2145 flex-wrap: wrap;
2146 align-items: center;
2147 justify-content: center;
dc26881CC LABS App2148 gap: var(--space-3);
2b821b7Claude2149 }
958d26aClaude2150 .landing-hero-cmd {
2151 display: inline-flex;
2152 align-items: center;
2153 gap: 4px;
2154 padding: 6px 12px;
2155 background: var(--bg-elevated);
4c47454Claude2156 border: 1px solid var(--border);
958d26aClaude2157 border-radius: var(--r-full);
2158 box-shadow: var(--elev-1);
2159 }
2160 .landing-hero-cmd .kbd {
2161 border: 0;
2162 background: transparent;
2163 padding: 0 4px;
2164 color: var(--text-muted);
2165 font-size: 12px;
2166 }
2167 .landing-hero-cmd .kbd:nth-last-of-type(1) { color: var(--accent); }
2168 .landing-hero-arrow {
2169 color: var(--text-faint);
2170 font-size: 13px;
2171 margin: 0 2px;
2b821b7Claude2172 }
4c47454Claude2173
2174 .landing-stats {
dc26881CC LABS App2175 margin: 0 auto;
958d26aClaude2176 font-family: var(--font-mono);
2177 font-size: 12px;
2178 color: var(--text-muted);
2179 display: flex;
2180 align-items: center;
2181 justify-content: center;
dc26881CC LABS App2182 gap: var(--space-2);
958d26aClaude2183 flex-wrap: wrap;
2184 letter-spacing: 0.02em;
2185 }
2186 .landing-stats strong {
2187 color: var(--text-strong);
2188 font-weight: 600;
2189 font-feature-settings: 'tnum';
2190 }
2191 .landing-stats-sep { opacity: 0.4; }
2192
9ecf5a4Claude2193 /* ---------- Capability grid (vapron-style uppercase tracked) ---------- */
c963db5Claude2194 .landing-caps {
2195 margin: var(--s-12) auto var(--s-16);
2196 max-width: 1080px;
2197 padding: var(--s-7) var(--s-4);
958d26aClaude2198 border-top: 1px solid var(--border-subtle);
2199 border-bottom: 1px solid var(--border-subtle);
c963db5Claude2200 }
2201 .landing-caps-grid {
2202 display: grid;
2203 grid-template-columns: repeat(4, 1fr);
2204 gap: 24px 16px;
958d26aClaude2205 text-align: center;
2206 }
c963db5Claude2207 .landing-cap {
958d26aClaude2208 font-family: var(--font-mono);
2209 font-size: 11px;
c963db5Claude2210 font-weight: 600;
958d26aClaude2211 text-transform: uppercase;
c963db5Claude2212 letter-spacing: 0.16em;
2213 color: var(--text-muted);
2214 transition: color var(--t-fast) var(--ease);
958d26aClaude2215 }
c963db5Claude2216 .landing-cap:hover { color: var(--text-strong); }
2217 @media (max-width: 800px) {
2218 .landing-caps-grid { grid-template-columns: repeat(2, 1fr); gap: 18px 12px; }
2219 }
2220 @media (max-width: 480px) {
2221 .landing-caps-grid { grid-template-columns: 1fr; }
2222 }
2223
9ecf5a4Claude2224 /* ---------- Big stat row (vapron-style hero closer) ---------- */
c963db5Claude2225 .landing-bigstats {
2226 margin: var(--s-10) auto var(--s-20);
2227 max-width: 1180px;
2228 padding: 0 var(--s-4);
2229 }
2230 .landing-bigstats-grid {
2231 display: grid;
2232 grid-template-columns: repeat(4, 1fr);
2233 gap: 32px;
2234 text-align: left;
958d26aClaude2235 }
c963db5Claude2236 .landing-bigstat {
2237 padding: var(--s-2) 0;
2238 }
2239 .landing-bigstat-num {
958d26aClaude2240 font-family: var(--font-display);
c963db5Claude2241 font-size: clamp(28px, 3.5vw, 44px);
2242 line-height: 1.05;
2243 letter-spacing: -0.03em;
2244 font-weight: 700;
2245 color: var(--text-strong);
2246 margin-bottom: var(--s-2);
2247 }
2248 .landing-bigstat-label {
2249 font-family: var(--font-mono);
2250 font-size: 11px;
958d26aClaude2251 font-weight: 500;
c963db5Claude2252 text-transform: uppercase;
2253 letter-spacing: 0.14em;
2254 color: var(--text-faint);
2b821b7Claude2255 }
c963db5Claude2256 @media (max-width: 800px) {
2257 .landing-bigstats-grid { grid-template-columns: repeat(2, 1fr); gap: 28px 16px; }
2258 }
2259 @media (max-width: 480px) {
2260 .landing-bigstats-grid { grid-template-columns: 1fr; gap: 24px; }
958d26aClaude2261 }
2262
2263 /* ---------- Section base ---------- */
2264 .landing-section { margin: var(--s-20) auto; }
2b821b7Claude2265
4c47454Claude2266 /* ---------- Feature grid ---------- */
2267 .landing-features {
2b821b7Claude2268 display: grid;
2269 grid-template-columns: repeat(3, 1fr);
958d26aClaude2270 gap: 16px;
2b821b7Claude2271 }
2272 .landing-feature {
958d26aClaude2273 background: var(--bg-elevated);
2b821b7Claude2274 border: 1px solid var(--border);
958d26aClaude2275 border-radius: var(--r-lg);
2276 padding: var(--s-7);
2277 position: relative;
2278 overflow: hidden;
2279 isolation: isolate;
2280 transition:
2281 transform var(--t-base) var(--ease-out-quart),
2282 border-color var(--t-base) var(--ease),
2283 box-shadow var(--t-base) var(--ease);
2284 }
2285 .landing-feature::before {
2286 content: '';
2287 position: absolute;
2288 inset: 0;
ba23fe2Claude2289 background: var(--bg-surface);
958d26aClaude2290 opacity: 0;
2291 transition: opacity var(--t-base) var(--ease);
2292 z-index: -1;
4c47454Claude2293 }
2294 .landing-feature:hover {
958d26aClaude2295 transform: translateY(-3px);
2296 border-color: var(--border-strong);
2297 box-shadow: var(--elev-2);
2b821b7Claude2298 }
958d26aClaude2299 .landing-feature:hover::before { opacity: 1; }
2b821b7Claude2300 .landing-feature-icon {
4c47454Claude2301 display: inline-flex;
2302 align-items: center;
2303 justify-content: center;
958d26aClaude2304 width: 40px;
2305 height: 40px;
2306 border-radius: var(--r);
2307 background: var(--accent-gradient-soft);
2308 color: var(--accent);
2309 margin-bottom: var(--s-4);
ba23fe2Claude2310 border: 1px solid var(--border);
2b821b7Claude2311 }
2312 .landing-feature-title {
958d26aClaude2313 font-family: var(--font-display);
2314 font-size: 19px;
2315 font-weight: 600;
2316 letter-spacing: -0.018em;
2317 margin: 0 0 var(--s-2);
2318 color: var(--text-strong);
2b821b7Claude2319 }
2320 .landing-feature-desc {
958d26aClaude2321 font-size: var(--t-sm);
2322 color: var(--text-muted);
2323 line-height: 1.6;
2324 margin: 0;
2325 }
2326
2327 /* ---------- Walkthrough ---------- */
2328 .landing-walk-grid {
2329 display: grid;
2330 grid-template-columns: repeat(4, 1fr);
2331 gap: 16px;
2332 counter-reset: walk;
2333 }
2334 .landing-walk-step {
2335 position: relative;
2336 padding: var(--s-7) var(--s-6) var(--s-6);
2337 background: var(--bg-elevated);
2338 border: 1px solid var(--border);
2339 border-radius: var(--r-lg);
2340 }
2341 .landing-walk-step::after {
2342 content: '';
2343 position: absolute;
2344 top: 50%;
2345 right: -12px;
2346 width: 12px;
2347 height: 1px;
2348 background: var(--border-strong);
2349 }
2350 .landing-walk-step:last-child::after { display: none; }
2351 .landing-walk-num {
2352 display: inline-block;
2353 font-family: var(--font-mono);
2354 font-size: 11px;
2355 color: var(--accent);
2356 background: var(--accent-gradient-faint);
ba23fe2Claude2357 border: 1px solid var(--border);
958d26aClaude2358 padding: 3px 8px;
2359 border-radius: var(--r-full);
2360 letter-spacing: 0.06em;
2361 margin-bottom: var(--s-3);
2362 }
2363 .landing-walk-title {
2364 font-family: var(--font-display);
2365 font-size: 22px;
2366 font-weight: 600;
2367 letter-spacing: -0.022em;
2368 margin: 0 0 var(--s-2);
2369 color: var(--text-strong);
2370 }
2371 .landing-walk-desc {
2372 font-size: var(--t-sm);
2b821b7Claude2373 color: var(--text-muted);
2374 line-height: 1.55;
2375 margin: 0;
2376 }
2377
958d26aClaude2378 /* ---------- Terminal ---------- */
2379 .landing-terminal-section { margin-top: var(--s-16); }
4c47454Claude2380 .landing-terminal-wrap {
2381 display: flex;
2b821b7Claude2382 justify-content: center;
2383 }
4c47454Claude2384 .landing-terminal {
2385 width: 100%;
958d26aClaude2386 max-width: 820px;
2387 background: linear-gradient(180deg, #0a0b12 0%, #06070c 100%);
2388 border: 1px solid var(--border-strong);
2389 border-radius: var(--r-lg);
2390 overflow: hidden;
ba23fe2Claude2391 box-shadow: var(--elev-3);
4c47454Claude2392 text-align: left;
2b821b7Claude2393 }
958d26aClaude2394 :root[data-theme='light'] .landing-terminal {
2395 background: linear-gradient(180deg, #0f111a 0%, #06070c 100%);
2396 }
2397 .landing-terminal-chrome {
2398 display: flex;
2399 align-items: center;
2400 gap: 7px;
2401 padding: 11px 14px;
2402 background: rgba(255,255,255,0.025);
2403 border-bottom: 1px solid rgba(255,255,255,0.06);
2404 position: relative;
2405 }
2406 .landing-terminal-dot {
2407 width: 11px;
2408 height: 11px;
2409 border-radius: 50%;
2410 flex-shrink: 0;
2411 }
2412 .landing-terminal-dot-r { background: #ff5f57; }
2413 .landing-terminal-dot-y { background: #febc2e; }
2414 .landing-terminal-dot-g { background: #28c840; }
2415 .landing-terminal-title {
2416 position: absolute;
2417 left: 50%;
2418 transform: translateX(-50%);
2419 font-family: var(--font-mono);
2420 font-size: 11px;
2421 color: rgba(237,237,242,0.55);
2422 letter-spacing: 0.01em;
2423 }
2424 .landing-terminal-body {
2425 padding: var(--s-6) var(--s-7);
2426 font-family: var(--font-mono);
2427 font-feature-settings: var(--mono-feat);
2428 font-size: 13.5px;
2429 line-height: 1.85;
2430 color: rgba(237,237,242,0.92);
2431 }
4c47454Claude2432 .landing-term-line {
2433 display: flex;
2434 gap: 10px;
2435 white-space: pre-wrap;
2436 word-break: break-all;
2b821b7Claude2437 }
958d26aClaude2438 .landing-term-out { color: rgba(237,237,242,0.7); }
ba23fe2Claude2439 .landing-term-prompt { color: var(--accent); user-select: none; flex-shrink: 0; }
958d26aClaude2440 .landing-term-meta { color: rgba(237,237,242,0.45); }
2441 .landing-term-ok { color: var(--green); user-select: none; flex-shrink: 0; }
2442 .landing-term-ok-line { color: rgba(237,237,242,0.92); }
2443 .landing-term-cursor { margin-top: 4px; }
2444 .landing-term-blink {
2445 animation: blink 1.05s steps(2) infinite;
2446 color: var(--accent);
2447 }
2448 @keyframes blink { 50% { opacity: 0; } }
2449
2450 /* ---------- Comparison ---------- */
2451 .landing-compare {
2452 max-width: 920px;
2453 margin: 0 auto;
2454 border: 1px solid var(--border);
2455 border-radius: var(--r-lg);
2456 overflow: hidden;
2457 background: var(--bg-elevated);
2458 }
2459 .landing-compare-row {
2460 display: grid;
2461 grid-template-columns: 1fr 180px 180px;
2462 align-items: center;
2463 padding: 14px 20px;
2464 border-bottom: 1px solid var(--border-subtle);
2465 font-size: var(--t-sm);
2466 transition: background var(--t-fast) var(--ease);
2467 }
2468 .landing-compare-row:last-child { border-bottom: none; }
2469 .landing-compare-row:hover { background: var(--bg-hover); }
2470 .landing-compare-feature {
2471 color: var(--text-strong);
2472 font-weight: 500;
2473 }
2474 .landing-compare-them, .landing-compare-us {
2475 text-align: center;
2476 font-family: var(--font-mono);
2477 font-size: 12px;
2478 color: var(--text-muted);
2479 }
2480 .landing-compare-us { color: var(--green); font-weight: 500; }
2481 .landing-compare-hl .landing-compare-us {
2482 color: var(--accent);
2483 font-weight: 600;
2b821b7Claude2484 }
958d26aClaude2485 .landing-compare-hl .landing-compare-feature::after {
2486 content: 'NEW';
2487 margin-left: 8px;
2488 padding: 1px 6px;
2489 border-radius: 4px;
2490 background: var(--accent-gradient-faint);
2491 color: var(--accent);
2492 font-family: var(--font-mono);
2493 font-size: 9px;
2494 letter-spacing: 0.1em;
2495 font-weight: 600;
2496 vertical-align: 1px;
2497 }
2498 @media (max-width: 720px) {
2499 .landing-compare-row { grid-template-columns: 1fr 80px 80px; padding: 12px 14px; }
2500 .landing-compare-hl .landing-compare-feature::after { display: none; }
2501 }
2502
2503 /* ---------- Pricing ---------- */
2504 .landing-pricing {
2505 display: grid;
2506 grid-template-columns: repeat(3, 1fr);
2507 gap: 16px;
2508 max-width: 1080px;
2509 margin: 0 auto;
2510 align-items: stretch;
2511 }
2512 .landing-price-card {
2513 position: relative;
2514 background: var(--bg-elevated);
2515 border: 1px solid var(--border);
2516 border-radius: var(--r-lg);
2517 padding: var(--s-7);
2518 display: flex;
2519 flex-direction: column;
2520 gap: var(--s-4);
2521 transition: border-color var(--t-base) var(--ease), transform var(--t-base) var(--ease);
2522 }
2523 .landing-price-card:hover {
2524 border-color: var(--border-strong);
2525 transform: translateY(-2px);
2526 }
2527 .landing-price-hl {
ba23fe2Claude2528 border-color: var(--border-strong);
2529 box-shadow: var(--elev-2);
2530 background: var(--bg-elevated);
958d26aClaude2531 }
ba23fe2Claude2532 .landing-price-hl:hover { border-color: var(--accent); }
958d26aClaude2533 .landing-price-badge {
2534 position: absolute;
2535 top: -10px;
2536 left: 50%;
2537 transform: translateX(-50%);
2538 padding: 3px 12px;
2539 background: var(--accent-gradient);
2540 color: #fff;
2541 font-family: var(--font-mono);
2542 font-size: 10px;
2543 letter-spacing: 0.1em;
2544 text-transform: uppercase;
2545 font-weight: 600;
2546 border-radius: var(--r-full);
ba23fe2Claude2547 box-shadow: var(--elev-1);
958d26aClaude2548 }
2549 .landing-price-tier {
2550 font-family: var(--font-mono);
2551 font-size: 11px;
2552 text-transform: uppercase;
2553 letter-spacing: 0.16em;
2554 color: var(--text-muted);
2555 }
2556 .landing-price-amount {
2557 display: flex;
2558 align-items: baseline;
2559 gap: 8px;
2560 }
2561 .landing-price-num {
2562 font-family: var(--font-display);
2563 font-size: 40px;
2564 font-weight: 600;
2565 letter-spacing: -0.03em;
2566 color: var(--text-strong);
2567 }
2568 .landing-price-cad {
2569 font-size: var(--t-sm);
2570 color: var(--text-faint);
2571 }
2572 .landing-price-desc {
2573 font-size: var(--t-sm);
2574 color: var(--text-muted);
2575 line-height: 1.55;
2576 margin: 0;
2577 }
2578 .landing-price-features {
2579 list-style: none;
2580 padding: 0;
2581 margin: 0;
2582 display: flex;
2583 flex-direction: column;
2584 gap: 8px;
2585 font-size: var(--t-sm);
2586 color: var(--text);
2587 }
2588 .landing-price-features li {
2589 display: flex;
2590 align-items: center;
2591 gap: 9px;
2592 }
2593 .landing-price-check {
2594 color: var(--accent);
2595 font-weight: 600;
4c47454Claude2596 flex-shrink: 0;
2b821b7Claude2597 }
958d26aClaude2598 .landing-price-cta { margin-top: auto; }
2599
2600 /* ---------- Closing CTA ---------- */
2601 .landing-cta-section { margin: var(--s-20) auto var(--s-16); }
2602 .landing-cta-card {
2603 position: relative;
2604 text-align: center;
2605 padding: var(--s-16) var(--s-7);
2606 border: 1px solid var(--border-strong);
2607 border-radius: var(--r-2xl);
2608 background: var(--bg-elevated);
2609 overflow: hidden;
2610 isolation: isolate;
2611 }
2612 .landing-cta-bg {
2613 position: absolute;
2614 inset: 0;
2615 z-index: -1;
ba23fe2Claude2616 background: radial-gradient(60% 80% at 50% 0%, rgba(91,110,232,0.07), transparent 70%);
958d26aClaude2617 }
2618 .landing-cta-card .eyebrow { justify-content: center; }
2619 .landing-cta-title {
2620 font-family: var(--font-display);
2621 font-size: clamp(28px, 4.4vw, 56px);
2622 line-height: 1.05;
2623 letter-spacing: -0.03em;
2624 font-weight: 600;
2625 margin: var(--s-3) 0 var(--s-4);
2626 color: var(--text-strong);
2627 }
2628 .landing-cta-sub {
2629 font-size: var(--t-md);
2630 color: var(--text-muted);
2631 max-width: 560px;
2632 margin: 0 auto var(--s-8);
2633 line-height: 1.55;
2634 }
2635 .landing-cta-buttons {
2636 display: flex;
2637 gap: 12px;
2638 justify-content: center;
2639 flex-wrap: wrap;
2640 }
2b821b7Claude2641
2642 /* ---------- Responsive ---------- */
958d26aClaude2643 @media (max-width: 960px) {
2644 .landing-features { grid-template-columns: repeat(2, 1fr); }
2645 .landing-walk-grid { grid-template-columns: repeat(2, 1fr); }
2646 .landing-walk-step::after { display: none; }
2647 .landing-pricing { grid-template-columns: 1fr; max-width: 480px; }
2648 }
2649 @media (max-width: 640px) {
2650 .landing-hero { padding: var(--s-14) 0 var(--s-10); }
2651 .landing-hero-cmd { flex-wrap: wrap; justify-content: center; }
2652 .landing-hero-ctas { flex-direction: column; align-items: stretch; }
2653 .landing-hero-ctas .btn { width: 100%; justify-content: center; }
2654 .landing-features { grid-template-columns: 1fr; }
2655 .landing-walk-grid { grid-template-columns: 1fr; }
2656 .landing-section { margin: var(--s-12) auto; }
2657 .landing-cta-card { padding: var(--s-10) var(--s-5); }
2658 .landing-cta-buttons .btn { width: 100%; justify-content: center; }
2b821b7Claude2659 }
52ad8b1Claude2660
2661 /* ---------- L4 social-proof counters ---------- */
2662 .landing-counters {
2663 margin: var(--s-10) auto var(--s-12);
2664 max-width: 1180px;
2665 padding: 0 var(--s-4);
2666 }
2667 .landing-counters-grid {
2668 display: grid;
2669 grid-template-columns: repeat(6, 1fr);
2670 gap: 20px;
2671 text-align: left;
2672 }
2673 .landing-counter {
2674 padding: var(--s-3) 0;
2675 border-top: 1px solid var(--border-subtle);
2676 }
2677 .landing-counter-num {
2678 font-family: var(--font-display);
2679 font-size: clamp(24px, 3vw, 38px);
2680 line-height: 1.05;
2681 letter-spacing: -0.03em;
2682 font-weight: 700;
2683 margin-bottom: 6px;
2684 font-feature-settings: 'tnum';
ba23fe2Claude2685 color: var(--text-strong);
52ad8b1Claude2686 }
2687 .landing-counter-label {
2688 font-family: var(--font-mono);
2689 font-size: 10.5px;
2690 font-weight: 500;
2691 text-transform: uppercase;
2692 letter-spacing: 0.12em;
2693 color: var(--text-faint);
2694 line-height: 1.4;
2695 }
2696 @media (max-width: 960px) {
2697 .landing-counters-grid { grid-template-columns: repeat(3, 1fr); gap: 20px 16px; }
2698 }
2699 @media (max-width: 540px) {
2700 .landing-counters-grid { grid-template-columns: repeat(2, 1fr); gap: 18px 12px; }
2701 }
5f2e749Claude2702
dc26881CC LABS App2703 /* ---------- L10/U1 hero install snippet ----------
2704 U1: wrapped in a labelled "power users" panel and re-located
2705 beneath the CTA + tertiary rows so it no longer competes with
2706 the primary calls to action. */
2707 .landing-hero-install-wrap {
2708 margin: 0 auto var(--space-6);
2709 text-align: center;
2710 }
2711 .landing-hero-install-label {
2712 display: inline-block;
2713 margin-bottom: var(--space-2);
2714 font-family: var(--font-mono);
2715 font-size: 11px;
2716 letter-spacing: 0.08em;
2717 text-transform: uppercase;
2718 color: var(--text-faint);
2719 }
5f2e749Claude2720 .landing-hero-install {
2721 display: inline-flex;
2722 align-items: stretch;
2723 gap: 0;
dc26881CC LABS App2724 margin: 0 auto;
5f2e749Claude2725 background: var(--bg-elevated);
2726 border: 1px solid var(--border-strong);
2727 border-radius: var(--r);
2728 box-shadow: var(--elev-1);
2729 overflow: hidden;
2730 max-width: 100%;
2731 font-family: var(--font-mono);
2732 }
2733 .landing-hero-install-code {
2734 display: inline-flex;
2735 align-items: center;
2736 gap: 10px;
2737 padding: 10px 14px;
2738 font-size: 13.5px;
2739 color: var(--text-strong);
2740 background: transparent;
2741 border: 0;
2742 white-space: nowrap;
2743 overflow-x: auto;
2744 }
2745 .landing-hero-install-prompt {
2746 color: var(--accent);
2747 user-select: none;
2748 }
2749 .landing-hero-install-copy {
2750 appearance: none;
2751 border: 0;
2752 border-left: 1px solid var(--border);
2753 background: transparent;
2754 color: var(--text-muted);
2755 font-family: var(--font-mono);
2756 font-size: 12px;
2757 font-weight: 600;
2758 letter-spacing: 0.06em;
2759 text-transform: uppercase;
2760 padding: 0 16px;
2761 cursor: pointer;
2762 transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
2763 }
2764 .landing-hero-install-copy:hover {
2765 background: var(--accent-gradient-faint);
2766 color: var(--accent);
2767 }
2768 .landing-hero-install-copy[data-copied="1"] {
2769 color: var(--green, #34d399);
2770 }
2771
b5d207bClaude2772 /* ---------- L10/U1 hero activity rail ----------
dc26881CC LABS App2773 U1 — tightened into a single horizontal strip. The 1px gradient
2774 rule on top is the same accent the headline uses, so the rail
2775 reads as part of the hero composition rather than a stray list. */
5f2e749Claude2776 .landing-hero-rail {
2777 list-style: none;
dc26881CC LABS App2778 padding: var(--space-4) 0 0;
2779 margin: 0 auto var(--space-6);
5f2e749Claude2780 display: flex;
2781 flex-wrap: wrap;
2782 justify-content: center;
dc26881CC LABS App2783 gap: var(--space-2) var(--space-6);
5f2e749Claude2784 font-family: var(--font-sans);
dc26881CC LABS App2785 font-size: 12px;
2786 color: var(--text-faint);
5f2e749Claude2787 max-width: 760px;
dc26881CC LABS App2788 position: relative;
2789 }
2790 .landing-hero-rail::before {
2791 content: '';
2792 position: absolute;
2793 top: 0;
2794 left: 50%;
2795 transform: translateX(-50%);
2796 width: 120px;
2797 height: 1px;
2798 background: var(--accent-gradient);
2799 opacity: 0.45;
2800 border-radius: 9999px;
5f2e749Claude2801 }
2802 .landing-hero-rail li {
2803 display: inline-flex;
dc26881CC LABS App2804 align-items: baseline;
2805 gap: var(--space-2);
5f2e749Claude2806 line-height: 1.4;
2807 }
2808 .landing-hero-rail strong {
2809 color: var(--text-strong);
2810 font-weight: 600;
2811 font-feature-settings: 'tnum';
dc26881CC LABS App2812 font-size: 14px;
2813 letter-spacing: -0.01em;
5f2e749Claude2814 }
dc26881CC LABS App2815 .landing-hero-rail-label {
2816 color: var(--text-muted);
2817 letter-spacing: 0.01em;
5f2e749Claude2818 }
dc26881CC LABS App2819 /* Backwards-compat: nothing references this any more but if a stale
2820 fragment lingers it's still hidden cleanly rather than orphaned. */
2821 .landing-hero-rail-check { display: none; }
5f2e749Claude2822
2823 /* ---------- L10 three-reasons section ---------- */
2824 .landing-reasons { margin-top: var(--s-12); }
2825 .landing-reasons-grid {
2826 display: grid;
2827 grid-template-columns: repeat(3, 1fr);
2828 gap: 16px;
2829 }
2830 .landing-reason {
2831 background: var(--bg-elevated);
2832 border: 1px solid var(--border);
2833 border-radius: var(--r-lg);
2834 padding: var(--s-7);
2835 display: flex;
2836 flex-direction: column;
2837 gap: var(--s-3);
2838 transition: border-color var(--t-base) var(--ease), transform var(--t-base) var(--ease);
2839 }
2840 .landing-reason:hover {
2841 border-color: var(--border-strong);
2842 transform: translateY(-2px);
2843 }
2844 .landing-reason-icon {
2845 display: inline-flex;
2846 align-items: center;
2847 justify-content: center;
2848 width: 40px;
2849 height: 40px;
2850 border-radius: var(--r);
2851 background: var(--accent-gradient-soft);
2852 color: var(--accent);
ba23fe2Claude2853 border: 1px solid var(--border);
5f2e749Claude2854 }
2855 .landing-reason-title {
2856 font-family: var(--font-display);
2857 font-size: 20px;
2858 font-weight: 600;
2859 letter-spacing: -0.02em;
2860 margin: 0;
2861 color: var(--text-strong);
2862 }
2863 .landing-reason-body {
2864 font-size: var(--t-sm);
2865 color: var(--text-muted);
2866 line-height: 1.55;
2867 margin: 0;
2868 }
2869 .landing-reasons-code {
2870 display: block;
2871 font-family: var(--font-mono);
2872 font-size: 12px;
2873 color: var(--text-strong);
2874 background: var(--bg);
2875 border: 1px solid var(--border);
2876 border-radius: var(--r);
2877 padding: 8px 12px;
2878 overflow-x: auto;
2879 white-space: nowrap;
2880 }
2881 .landing-reason-link {
2882 margin-top: auto;
2883 display: inline-flex;
2884 align-items: center;
2885 gap: 6px;
2886 color: var(--accent);
2887 font-size: var(--t-sm);
2888 font-weight: 500;
2889 text-decoration: none;
2890 }
2891 .landing-reason-link:hover { text-decoration: underline; }
2892 @media (max-width: 960px) {
2893 .landing-reasons-grid { grid-template-columns: 1fr; max-width: 520px; margin: 0 auto; }
2894 }
2895
2896 /* ---------- L10 "How is this different" pull-quote ---------- */
2897 .landing-pullquote-section {
2898 margin: var(--s-20) auto var(--s-12);
2899 max-width: 920px;
2900 padding: 0 var(--s-4);
2901 text-align: center;
2902 }
2903 .landing-pullquote {
2904 margin: 0;
2905 padding: var(--s-10) var(--s-7);
ba23fe2Claude2906 background: var(--bg-elevated);
5f2e749Claude2907 border: 1px solid var(--border-strong);
2908 border-radius: var(--r-xl);
2909 position: relative;
2910 overflow: hidden;
2911 }
2912 .landing-pullquote-eyebrow {
2913 font-family: var(--font-mono);
2914 font-size: 11px;
2915 font-weight: 600;
2916 letter-spacing: 0.16em;
2917 text-transform: uppercase;
2918 color: var(--accent);
2919 margin-bottom: var(--s-4);
2920 }
2921 .landing-pullquote-text {
2922 font-family: var(--font-display);
2923 font-size: clamp(20px, 2.4vw, 28px);
2924 line-height: 1.4;
2925 letter-spacing: -0.018em;
2926 color: var(--text-strong);
2927 margin: 0 auto;
2928 max-width: 760px;
2929 quotes: "\\201C" "\\201D";
2930 }
2931 .landing-pullquote-text::before { content: open-quote; color: var(--accent); margin-right: 4px; }
2932 .landing-pullquote-text::after { content: close-quote; color: var(--accent); margin-left: 4px; }
2933 .landing-pullquote-link {
2934 display: inline-flex;
2935 align-items: center;
2936 gap: 6px;
2937 margin-top: var(--s-6);
2938 color: var(--accent);
2939 font-size: var(--t-sm);
2940 font-weight: 500;
2941 text-decoration: none;
2942 }
2943 .landing-pullquote-link:hover { text-decoration: underline; }
2944
2945 /* ---------- L10 hero responsive overrides ---------- */
2946 @media (max-width: 640px) {
2947 .landing-hero-install { width: 100%; }
2948 .landing-hero-install-code { flex: 1; font-size: 12px; }
2949 .landing-hero-rail { flex-direction: column; align-items: flex-start; gap: 6px; padding: 0 var(--s-3); }
2950 .landing-hero-rail li { width: 100%; }
2951 }
534f04aClaude2952
2953 /* ============================================================ */
2954 /* Block M1 — Live-now demo feed */
2955 /* ============================================================ */
2956 .landing-livenow {
2957 margin: var(--s-8) 0 var(--s-6);
2958 padding: var(--s-6) 0 var(--s-4);
2959 }
2960 .landing-livenow-head {
2961 text-align: center;
2962 margin-bottom: var(--s-6);
2963 }
2964 .landing-livenow-eyebrow {
2965 display: inline-flex;
2966 align-items: center;
2967 gap: 8px;
2968 padding: 4px 12px;
2969 border-radius: var(--r-full);
2970 background: rgba(52,211,153,0.08);
2971 border: 1px solid rgba(52,211,153,0.25);
2972 color: var(--green);
2973 font-family: var(--font-mono, ui-monospace, monospace);
2974 font-size: 11px;
2975 letter-spacing: 0.06em;
2976 text-transform: uppercase;
2977 margin-bottom: var(--s-3);
2978 }
2979 .landing-livenow-pulse {
2980 width: 8px; height: 8px;
2981 border-radius: 50%;
2982 background: var(--green);
ba23fe2Claude2983 animation: heroPulse 2.4s ease-in-out infinite;
534f04aClaude2984 }
2985 .landing-livenow-title {
2986 font-size: 22px;
2987 line-height: 1.25;
2988 margin: 0 auto;
2989 max-width: 720px;
2990 color: var(--text-strong);
2991 font-weight: 600;
2992 letter-spacing: -0.01em;
2993 }
2994 .landing-livenow-sub {
2995 margin: var(--s-2) auto 0;
2996 color: var(--text-muted);
2997 font-size: 13px;
2998 max-width: 560px;
2999 }
3000 .landing-livenow-sub code {
3001 background: rgba(255,255,255,0.05);
3002 border: 1px solid var(--border);
3003 padding: 1px 6px;
3004 border-radius: 4px;
3005 font-size: 12px;
3006 color: var(--accent);
3007 }
3008
3009 .landing-livenow-grid {
3010 display: grid;
3011 grid-template-columns: repeat(2, minmax(0, 1fr));
3012 gap: var(--s-3);
3013 }
3014 @media (min-width: 980px) {
3015 .landing-livenow-grid {
3016 grid-template-columns: repeat(4, minmax(0, 1fr));
3017 }
3018 }
3019
3020 .landing-livecard {
3021 background: linear-gradient(180deg, rgba(255,255,255,0.025), rgba(255,255,255,0.005));
3022 border: 1px solid var(--border);
3023 border-radius: var(--r-md, 10px);
3024 padding: 14px 14px 12px;
3025 display: flex;
3026 flex-direction: column;
3027 min-height: 180px;
3028 position: relative;
3029 overflow: hidden;
3030 }
3031 .landing-livecard::before {
3032 content: '';
3033 position: absolute;
3034 inset: 0;
3035 background: var(--accent-gradient-faint);
3036 opacity: 0;
3037 transition: opacity 200ms var(--ease, ease);
3038 pointer-events: none;
3039 }
3040 .landing-livecard:hover::before { opacity: 1; }
3041
3042 .landing-livecard-head {
3043 display: flex;
3044 align-items: center;
3045 gap: 8px;
3046 margin-bottom: 10px;
3047 }
3048 .landing-livecard-dot {
3049 width: 7px; height: 7px;
3050 border-radius: 50%;
3051 background: var(--green);
ba23fe2Claude3052 animation: heroPulse 2.4s ease-in-out infinite;
534f04aClaude3053 flex-shrink: 0;
3054 }
3055 .landing-livecard-title {
3056 margin: 0;
3057 font-size: 12px;
3058 font-weight: 600;
3059 text-transform: uppercase;
3060 letter-spacing: 0.06em;
3061 color: var(--text-muted);
3062 }
3063
3064 .landing-livecard-bignum {
3065 display: flex;
3066 align-items: baseline;
3067 gap: 8px;
3068 margin: 4px 0 10px;
3069 }
3070 .landing-livecard-bignum-n {
3071 font-size: 30px;
3072 font-weight: 700;
3073 color: var(--text-strong);
3074 font-variant-numeric: tabular-nums;
3075 }
3076 .landing-livecard-bignum-label {
3077 font-size: 12px;
3078 color: var(--text-muted);
3079 }
3080
3081 .landing-livecard-list {
3082 list-style: none;
3083 margin: 0;
3084 padding: 0;
3085 font-size: 13px;
3086 line-height: 1.45;
3087 flex: 1;
3088 }
3089 .landing-livecard-row, .landing-livecard-feedrow {
3090 padding: 6px 0;
3091 border-bottom: 1px dashed rgba(255,255,255,0.05);
3092 transition: background-color 1s var(--ease, ease);
3093 border-radius: 4px;
3094 margin: 0 -4px;
3095 padding-left: 4px;
3096 padding-right: 4px;
3097 }
3098 .landing-livecard-row:last-child,
3099 .landing-livecard-feedrow:last-child { border-bottom: 0; }
3100 .landing-livecard-feedrow { padding: 4px; font-size: 12.5px; }
3101
3102 .landing-livecard-flash {
3103 background-color: rgba(52,211,153,0.18) !important;
3104 animation: landing-livecard-flash-fade 1.1s ease-out forwards;
3105 }
3106 @keyframes landing-livecard-flash-fade {
3107 0% { background-color: rgba(52,211,153,0.22); }
3108 100% { background-color: rgba(52,211,153,0); }
3109 }
3110
3111 .landing-livecard-link {
3112 color: var(--text-strong);
3113 text-decoration: none;
3114 display: inline-block;
3115 max-width: 100%;
3116 overflow: hidden;
3117 text-overflow: ellipsis;
3118 white-space: nowrap;
3119 }
3120 .landing-livecard-link:hover { color: var(--accent); }
3121 .landing-livecard-num {
3122 font-family: var(--font-mono, ui-monospace, monospace);
3123 color: var(--text-faint);
3124 font-weight: 500;
3125 font-size: 12px;
3126 }
3127 .landing-livecard-title-text { color: var(--text-strong); }
3128 .landing-livecard-snippet {
3129 color: var(--text-muted);
3130 font-style: italic;
3131 font-size: 12px;
3132 }
3133 .landing-livecard-meta {
3134 font-size: 11px;
3135 color: var(--text-faint);
3136 margin-top: 2px;
3137 font-family: var(--font-mono, ui-monospace, monospace);
3138 }
3139 .landing-livecard-repo {
3140 color: var(--accent-2);
3141 }
3142 .landing-livecard-rel {
3143 color: var(--text-muted);
3144 }
3145 .landing-livecard-empty {
3146 color: var(--text-faint);
3147 font-size: 12px;
3148 font-style: italic;
3149 padding: 8px 0;
3150 }
3151 .landing-livecard-kind {
3152 display: inline-block;
3153 padding: 1px 7px;
3154 border-radius: var(--r-full);
3155 font-family: var(--font-mono, ui-monospace, monospace);
3156 font-size: 10px;
3157 letter-spacing: 0.04em;
3158 text-transform: uppercase;
3159 font-weight: 600;
3160 }
3161 .landing-livecard-kind-auto_merge-merged,
3162 .landing-livecard-kind-auto-merge-merged {
3163 background: rgba(52,211,153,0.12);
3164 color: var(--green);
3165 border: 1px solid rgba(52,211,153,0.25);
3166 }
3167 .landing-livecard-kind-ai_build-dispatched,
3168 .landing-livecard-kind-ai-build-dispatched {
ba23fe2Claude3169 background: var(--accent-gradient-faint);
534f04aClaude3170 color: var(--accent);
ba23fe2Claude3171 border: 1px solid var(--border-strong);
534f04aClaude3172 }
3173 .landing-livecard-kind-ai_review-posted,
3174 .landing-livecard-kind-ai-review-posted {
ba23fe2Claude3175 background: var(--bg-surface);
534f04aClaude3176 color: var(--accent-2);
ba23fe2Claude3177 border: 1px solid var(--border);
534f04aClaude3178 }
3179
3180 .landing-livenow-cta {
3181 margin-top: var(--s-5);
3182 display: flex;
3183 flex-wrap: wrap;
3184 align-items: center;
3185 justify-content: center;
3186 gap: 10px;
3187 font-size: 13px;
3188 color: var(--text-muted);
3189 }
3190 .landing-livenow-cta-link {
3191 color: var(--accent);
3192 text-decoration: none;
3193 font-weight: 500;
3194 display: inline-flex;
3195 align-items: center;
3196 gap: 4px;
3197 }
3198 .landing-livenow-cta-link:hover { color: var(--accent-hover); }
3199 .landing-livenow-cta-sep { color: var(--text-faint); }
3200
3201 @media (prefers-reduced-motion: reduce) {
3202 .landing-livenow-pulse,
3203 .landing-livecard-dot { animation: none; }
3204 .landing-livecard-flash { animation: none; background-color: transparent !important; }
3205 }
52ad8b1Claude3206`;
3207
3208/**
3209 * Block L4 — count-up animation.
3210 *
3211 * Reads each `[data-counter-target]` and animates the in-DOM text from
3212 * 0 → target over ~1.2s when the element first scrolls into view.
3213 *
3214 * Render-once semantics: each tile already contains the final value as
3215 * HTML, so visitors with JS disabled — or anyone before the script
3216 * loads — sees the correct number. The script just animates the text.
3217 *
3218 * Falls back to the static value (no animation) when IntersectionObserver
3219 * isn't available, or when the user prefers reduced motion.
3220 */
3221const landingCountersJs = `
3222(function(){
3223 try {
3224 var els = document.querySelectorAll('[data-counter-target]');
3225 if (!els.length) return;
3226 var reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
3227 if (reduced || typeof IntersectionObserver !== 'function') return;
3228
3229 function animate(el) {
3230 var target = parseInt(el.getAttribute('data-counter-target') || '0', 10);
3231 if (!isFinite(target) || target <= 0) return;
3232 var prefix = el.getAttribute('data-counter-prefix') || '';
3233 var suffix = el.getAttribute('data-counter-suffix') || '';
3234 var duration = 1200;
3235 var start = performance.now();
3236 function frame(now) {
3237 var t = Math.min(1, (now - start) / duration);
3238 // ease-out cubic
3239 var eased = 1 - Math.pow(1 - t, 3);
3240 var v = Math.floor(eased * target);
3241 el.textContent = prefix + v.toLocaleString() + suffix;
3242 if (t < 1) requestAnimationFrame(frame);
3243 else el.textContent = prefix + target.toLocaleString() + suffix;
3244 }
3245 // Reset to zero before animating in.
3246 el.textContent = prefix + '0' + suffix;
3247 requestAnimationFrame(frame);
3248 }
3249
3250 var io = new IntersectionObserver(function(entries) {
3251 entries.forEach(function(entry){
3252 if (entry.isIntersecting) {
3253 animate(entry.target);
3254 io.unobserve(entry.target);
3255 }
3256 });
3257 }, { threshold: 0.4 });
3258 els.forEach(function(el){ io.observe(el); });
3259 } catch (_) { /* swallow — static numbers remain */ }
3260})();
2b821b7Claude3261`;
5f2e749Claude3262
3263/**
3264 * Block L10 — clipboard copy for the hero install snippet.
3265 *
3266 * Pure progressive enhancement. Without JS the user can still
3267 * triple-click + Cmd/Ctrl-C the snippet — the button is the
3268 * speed-bump, not the only path.
3269 */
3270const landingCopyJs = `
3271(function(){
3272 try {
3273 var btns = document.querySelectorAll('[data-copy-target]');
3274 if (!btns.length) return;
3275 btns.forEach(function(btn){
3276 btn.addEventListener('click', function(){
3277 var id = btn.getAttribute('data-copy-target') || '';
3278 var src = document.getElementById(id);
3279 if (!src) return;
3280 var text = src.textContent || '';
3281 var done = function(){
3282 var prev = btn.textContent;
3283 btn.textContent = 'Copied';
3284 btn.setAttribute('data-copied', '1');
3285 setTimeout(function(){
3286 btn.textContent = prev || 'Copy';
3287 btn.removeAttribute('data-copied');
3288 }, 1500);
3289 };
3290 if (navigator.clipboard && navigator.clipboard.writeText) {
3291 navigator.clipboard.writeText(text).then(done, function(){ done(); });
3292 } else {
3293 // Legacy fallback — temp textarea + execCommand.
3294 try {
3295 var ta = document.createElement('textarea');
3296 ta.value = text;
3297 ta.style.position = 'fixed';
3298 ta.style.opacity = '0';
3299 document.body.appendChild(ta);
3300 ta.select();
3301 document.execCommand('copy');
3302 document.body.removeChild(ta);
3303 done();
3304 } catch (_) {}
3305 }
3306 });
3307 });
3308 } catch (_) { /* swallow */ }
3309})();
3310`;
0c3eee5Claude3311
3312// ============================================================
3313// land2030Css — scoped CSS for the 2030 homepage prelude.
3314// Every selector is prefixed `.land-2030-*` so it can't bleed
3315// into the older `.landing-*` (L10/U1/Q1/M1) styles below.
3316// ============================================================
3317const land2030Css = `
3318 .land-2030-root {
3319 position: relative;
3320 max-width: 1240px;
3321 margin: 0 auto;
3322 padding: 0 16px var(--space-6, 32px);
3323 color: var(--text, #e6e6f0);
3324 }
3325
3326 /* ---------- Hero ---------- */
3327 .land-2030-hero {
3328 position: relative;
3329 padding: clamp(48px, 8vw, 120px) 8px clamp(40px, 6vw, 80px);
3330 text-align: center;
3331 overflow: hidden;
3332 }
3333 .land-2030-hairline {
3334 position: absolute;
3335 top: 0; left: 0; right: 0;
ba23fe2Claude3336 height: 1px;
3337 background: var(--border);
0c3eee5Claude3338 pointer-events: none;
3339 }
3340 .land-2030-orb {
3341 position: absolute;
3342 top: -10%;
3343 left: 50%;
3344 width: 720px;
ba23fe2Claude3345 height: 480px;
0c3eee5Claude3346 transform: translateX(-50%);
ba23fe2Claude3347 background: radial-gradient(ellipse, rgba(91,110,232,0.07), transparent 70%);
0c3eee5Claude3348 pointer-events: none;
3349 z-index: 0;
3350 }
3351 .land-2030-hero-inner {
3352 position: relative;
3353 z-index: 1;
3354 max-width: 1080px;
3355 margin: 0 auto;
3356 }
3357 .land-2030-eyebrow {
3358 display: inline-flex;
3359 align-items: center;
3360 gap: 8px;
3361 padding: 6px 12px;
3362 border-radius: 999px;
ba23fe2Claude3363 background: var(--bg-surface);
3364 color: var(--accent);
0c3eee5Claude3365 font-size: 12px;
3366 letter-spacing: 0.04em;
3367 text-transform: uppercase;
ba23fe2Claude3368 border: 1px solid var(--border);
0c3eee5Claude3369 margin-bottom: 24px;
3370 }
3371 .land-2030-eyebrow-mini {
3372 margin-bottom: 12px;
3373 }
3374 .land-2030-pulse {
3375 width: 8px; height: 8px;
3376 border-radius: 50%;
ba23fe2Claude3377 background: var(--accent);
0c3eee5Claude3378 animation: land2030-pulse 2s ease-in-out infinite;
3379 }
3380 @keyframes land2030-pulse {
3381 0%, 100% { opacity: 0.5; transform: scale(1); }
3382 50% { opacity: 1; transform: scale(1.25); }
3383 }
3384 .land-2030-display {
3385 font-size: clamp(60px, 9vw, 96px);
3386 font-family: var(--font-display, inherit);
3387 font-weight: 800;
3388 line-height: 1.02;
3389 letter-spacing: -0.035em;
3390 margin: 0 auto 24px;
3391 max-width: 1040px;
3392 }
ba23fe2Claude3393 .land-2030-display span { color: var(--accent); }
0c3eee5Claude3394 .land-2030-sub {
3395 font-size: clamp(17px, 1.6vw, 22px);
3396 color: var(--text-muted, #a0a0b8);
3397 max-width: 720px;
3398 margin: 0 auto 36px;
3399 line-height: 1.45;
3400 }
3401 .land-2030-cta-row {
3402 display: flex;
3403 flex-wrap: wrap;
3404 justify-content: center;
3405 gap: 12px;
3406 }
3407 .land-2030-cta-primary,
3408 .land-2030-cta-secondary {
3409 min-width: 200px;
3410 }
3411
3412 /* ---------- Section scaffold ---------- */
3413 .land-2030-section {
3414 margin: clamp(56px, 8vw, 96px) 0;
3415 position: relative;
3416 }
3417 .land-2030-section-dark {
3418 padding: 48px 32px;
ba23fe2Claude3419 background: var(--bg-elevated);
3420 border: 1px solid var(--border);
0c3eee5Claude3421 border-radius: 20px;
3422 }
3423 .land-2030-section-head {
3424 text-align: center;
3425 max-width: 760px;
3426 margin: 0 auto 40px;
3427 }
3428 .land-2030-h2 {
3429 font-size: clamp(28px, 4vw, 44px);
3430 font-family: var(--font-display, inherit);
3431 font-weight: 700;
3432 letter-spacing: -0.025em;
3433 line-height: 1.1;
3434 margin: 0 0 12px;
3435 }
3436 .land-2030-lede {
3437 font-size: 16px;
3438 color: var(--text-muted, #a0a0b8);
3439 line-height: 1.55;
3440 margin: 0;
3441 }
3442
3443 /* ---------- Closed loop diagram ---------- */
3444 .land-2030-loop-wrap {
3445 max-width: 1100px;
3446 margin: 0 auto;
3447 padding: 16px;
ba23fe2Claude3448 background: var(--bg-elevated);
3449 border: 1px solid var(--border);
0c3eee5Claude3450 border-radius: 20px;
3451 color: var(--text-muted, #a0a0b8);
3452 }
3453 .land-2030-loop-svg {
3454 display: block;
3455 width: 100%;
3456 height: auto;
3457 }
3458 .land-2030-loop-path {
3459 stroke-dasharray: 6 4;
3460 animation: land2030-loop-dash 30s linear infinite;
3461 }
3462 @keyframes land2030-loop-dash {
3463 from { stroke-dashoffset: 0; }
3464 to { stroke-dashoffset: -400; }
3465 }
3466 .land-2030-loop-text {
3467 font-size: 14px;
3468 font-weight: 600;
3469 letter-spacing: -0.005em;
3470 }
3471
3472 /* ---------- 6-card "unfair advantages" grid ---------- */
3473 .land-2030-card-grid {
3474 display: grid;
3475 grid-template-columns: repeat(3, minmax(0, 1fr));
3476 gap: 16px;
3477 max-width: 1180px;
3478 margin: 0 auto;
3479 }
3480 .land-2030-card {
3481 display: flex;
3482 flex-direction: column;
3483 gap: 10px;
3484 padding: 24px;
ba23fe2Claude3485 background: var(--bg-elevated);
3486 border: 1px solid var(--border);
0c3eee5Claude3487 border-radius: 16px;
3488 text-decoration: none;
3489 color: inherit;
ba23fe2Claude3490 transition: transform 200ms ease, border-color 200ms ease;
0c3eee5Claude3491 }
3492 .land-2030-card:hover {
3493 transform: translateY(-2px);
ba23fe2Claude3494 border-color: var(--border-strong);
0c3eee5Claude3495 }
3496 .land-2030-card-icon {
3497 width: 44px; height: 44px;
3498 display: inline-flex;
3499 align-items: center;
3500 justify-content: center;
3501 border-radius: 12px;
ba23fe2Claude3502 background: var(--bg-surface);
3503 color: var(--accent);
3504 border: 1px solid var(--border);
0c3eee5Claude3505 margin-bottom: 4px;
3506 }
3507 .land-2030-card-title {
3508 margin: 0;
3509 font-size: 18px;
3510 font-weight: 700;
3511 letter-spacing: -0.01em;
3512 }
3513 .land-2030-card-desc {
3514 margin: 0;
3515 font-size: 14.5px;
3516 color: var(--text-muted, #a0a0b8);
3517 line-height: 1.55;
3518 }
3519 .land-2030-card-cta {
3520 margin-top: auto;
ba23fe2Claude3521 color: var(--accent);
0c3eee5Claude3522 font-size: 14px;
3523 font-weight: 600;
3524 }
3525
3526 /* ---------- Global dashboards grid ---------- */
3527 .land-2030-dash-grid {
3528 display: grid;
3529 grid-template-columns: repeat(5, minmax(0, 1fr));
3530 gap: 12px;
3531 max-width: 1180px;
3532 margin: 0 auto;
3533 }
3534 .land-2030-dash {
3535 display: flex;
3536 flex-direction: column;
3537 gap: 6px;
3538 padding: 16px 18px;
ba23fe2Claude3539 background: var(--bg-elevated);
3540 border: 1px solid var(--border);
0c3eee5Claude3541 border-radius: 12px;
3542 text-decoration: none;
3543 color: inherit;
3544 transition: transform 180ms ease, border-color 180ms ease;
3545 }
3546 .land-2030-dash:hover {
3547 transform: translateY(-1px);
ba23fe2Claude3548 border-color: var(--border-strong);
0c3eee5Claude3549 }
3550 .land-2030-dash-name {
3551 font-family: var(--font-mono, ui-monospace, monospace);
3552 font-size: 14px;
ba23fe2Claude3553 color: var(--accent);
0c3eee5Claude3554 font-weight: 600;
3555 }
3556 .land-2030-dash-desc {
3557 font-size: 13px;
3558 color: var(--text-muted, #a0a0b8);
3559 line-height: 1.45;
3560 }
3561
3562 /* ---------- 18-feature grid + status pills ---------- */
3563 .land-2030-feat-grid {
3564 display: grid;
3565 grid-template-columns: repeat(3, minmax(0, 1fr));
3566 gap: 10px;
3567 max-width: 1180px;
3568 margin: 0 auto;
3569 }
3570 .land-2030-feat {
3571 display: flex;
3572 flex-direction: column;
3573 gap: 6px;
3574 padding: 14px 16px;
ba23fe2Claude3575 background: var(--bg-elevated);
3576 border: 1px solid var(--border);
0c3eee5Claude3577 border-radius: 10px;
3578 text-decoration: none;
3579 color: inherit;
3580 transition: border-color 160ms ease, background 160ms ease;
3581 }
3582 .land-2030-feat:hover {
ba23fe2Claude3583 border-color: var(--border-strong);
3584 background: var(--bg-surface);
0c3eee5Claude3585 }
3586 .land-2030-feat-head {
3587 display: flex;
3588 align-items: center;
3589 justify-content: space-between;
3590 gap: 10px;
3591 }
3592 .land-2030-feat-title {
3593 font-size: 14.5px;
3594 font-weight: 700;
3595 letter-spacing: -0.005em;
3596 }
3597 .land-2030-feat-desc {
3598 margin: 0;
3599 font-size: 13px;
3600 color: var(--text-muted, #a0a0b8);
3601 line-height: 1.5;
3602 }
3603 .land-2030-pill {
3604 font-size: 10.5px;
3605 letter-spacing: 0.04em;
3606 text-transform: uppercase;
3607 padding: 3px 8px;
3608 border-radius: 999px;
3609 border: 1px solid currentColor;
3610 font-weight: 700;
3611 line-height: 1;
3612 }
3613 .land-2030-pill-live { color: #5be0a9; border-color: rgba(91,224,169,0.5); background: rgba(91,224,169,0.10); }
3614 .land-2030-pill-beta { color: #ffd16b; border-color: rgba(255,209,107,0.5); background: rgba(255,209,107,0.10); }
3615 .land-2030-pill-soon { color: #a0a0b8; border-color: rgba(160,160,184,0.4); background: rgba(160,160,184,0.08); }
3616
3617 /* ---------- Developer surface ---------- */
3618 .land-2030-dx-grid {
3619 display: grid;
3620 grid-template-columns: repeat(3, minmax(0, 1fr));
3621 gap: 16px;
3622 max-width: 1180px;
3623 margin: 0 auto;
3624 }
3625 .land-2030-dx {
3626 padding: 18px;
ba23fe2Claude3627 background: var(--bg-elevated);
3628 border: 1px solid var(--border);
0c3eee5Claude3629 border-radius: 14px;
3630 }
3631 .land-2030-dx-title {
3632 margin: 0 0 10px;
3633 font-size: 15px;
3634 font-weight: 700;
3635 color: var(--text-strong, #fff);
3636 }
3637 .land-2030-code {
3638 margin: 0;
3639 padding: 12px 14px;
ba23fe2Claude3640 background: var(--bg);
3641 border: 1px solid var(--border);
0c3eee5Claude3642 border-radius: 10px;
3643 font-family: var(--font-mono, ui-monospace, monospace);
3644 font-size: 12.5px;
ba23fe2Claude3645 color: var(--text-muted);
0c3eee5Claude3646 line-height: 1.6;
3647 overflow-x: auto;
3648 }
3649 .land-2030-code code { color: inherit; background: transparent; padding: 0; }
3650 .land-2030-code-wide {
3651 font-size: 13px;
ba23fe2Claude3652 color: var(--text);
0c3eee5Claude3653 }
3654
3655 /* ---------- Agent multiplayer ---------- */
3656 .land-2030-agent-wrap {
3657 display: grid;
3658 grid-template-columns: 1.4fr 1fr;
3659 gap: 24px;
3660 max-width: 1080px;
3661 margin: 0 auto;
3662 align-items: center;
3663 }
3664 .land-2030-agent-stat { text-align: left; }
3665 .land-2030-agent-big {
3666 font-size: clamp(48px, 7vw, 80px);
3667 font-weight: 800;
3668 line-height: 1;
ba23fe2Claude3669 color: var(--text-strong);
0c3eee5Claude3670 letter-spacing: -0.03em;
3671 }
3672 .land-2030-agent-label {
3673 margin-top: 12px;
3674 color: var(--text-muted, #a0a0b8);
3675 font-size: 15px;
3676 line-height: 1.5;
3677 }
3678 .land-2030-agent-link {
3679 display: inline-block;
3680 margin-top: 16px;
ba23fe2Claude3681 color: var(--accent);
0c3eee5Claude3682 text-decoration: none;
3683 font-weight: 600;
3684 font-size: 14px;
3685 }
3686 .land-2030-agent-link:hover { text-decoration: underline; }
3687
3688 /* ---------- vs GitHub strip ---------- */
3689 .land-2030-vs {
3690 max-width: 880px;
3691 margin: 0 auto;
ba23fe2Claude3692 background: var(--bg-elevated);
3693 border: 1px solid var(--border);
0c3eee5Claude3694 border-radius: 14px;
3695 overflow: hidden;
3696 text-align: center;
3697 padding: 0 0 18px;
3698 }
3699 .land-2030-vs-row {
3700 display: grid;
3701 grid-template-columns: 1fr 1fr;
3702 gap: 0;
3703 padding: 16px 18px;
ba23fe2Claude3704 border-bottom: 1px solid var(--border);
0c3eee5Claude3705 font-size: 15px;
3706 }
3707 .land-2030-vs-row:last-of-type { border-bottom: 0; }
3708 .land-2030-vs-head {
3709 font-size: 12px;
3710 letter-spacing: 0.04em;
3711 text-transform: uppercase;
3712 color: var(--text-muted, #a0a0b8);
ba23fe2Claude3713 background: var(--bg-surface);
0c3eee5Claude3714 }
3715 .land-2030-vs-us {
ba23fe2Claude3716 color: var(--text-strong);
0c3eee5Claude3717 font-weight: 700;
3718 }
3719 .land-2030-vs-link {
3720 display: inline-block;
3721 margin-top: 12px;
ba23fe2Claude3722 color: var(--accent);
0c3eee5Claude3723 text-decoration: none;
3724 font-weight: 600;
3725 font-size: 14px;
3726 }
3727 .land-2030-vs-link:hover { text-decoration: underline; }
3728
3729 /* ---------- Responsive ---------- */
3730 @media (max-width: 960px) {
3731 .land-2030-card-grid,
3732 .land-2030-feat-grid,
3733 .land-2030-dx-grid,
3734 .land-2030-dash-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
3735 .land-2030-agent-wrap { grid-template-columns: 1fr; }
3736 }
3737 @media (max-width: 640px) {
3738 .land-2030-card-grid,
3739 .land-2030-feat-grid,
3740 .land-2030-dx-grid,
3741 .land-2030-dash-grid { grid-template-columns: 1fr; }
3742 .land-2030-section-dark { padding: 28px 16px; }
3743 .land-2030-vs-row { grid-template-columns: 1fr; gap: 4px; }
3744 }
3745 @media (max-width: 375px) {
3746 .land-2030-display { font-size: 44px; }
3747 .land-2030-cta-primary,
3748 .land-2030-cta-secondary { width: 100%; min-width: 0; }
3749 }
3750
3751 @media (prefers-reduced-motion: reduce) {
3752 .land-2030-loop-path,
3753 .land-2030-pulse { animation: none; }
3754 }
3755`;