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.tsxBlame3702 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" />
139 <div class="landing-hero-grid" />
2b821b7Claude140 </div>
958d26aClaude141
142 <div class="landing-hero-inner stagger">
143 <div class="eyebrow landing-hero-eyebrow">
144 <span class="landing-hero-pulse" />
145 v1 · pre-launch · {new Date().getFullYear()}
146 </div>
147
148 <h1 class="landing-hero-title display">
64aa989Claude149 The git platform built for AI-assisted teams.
958d26aClaude150 </h1>
151
152 <p class="landing-hero-sub">
64aa989Claude153 AI code review on every PR. Auto-repair for failed gates. Push to live on merge.
958d26aClaude154 </p>
155
adf5e18Claude156 {/* U1 — primary CTA row. "Migrate from GitHub" added as a
157 secondary CTA alongside sign-up to capture visitors who
158 already have GitHub repos and want a one-click move. */}
dc26881CC LABS App159 <div class="landing-hero-ctas" data-testid="hero-primary-ctas">
958d26aClaude160 <a href="/register" class="btn btn-primary btn-xl landing-cta-primary">
5f2e749Claude161 Sign up free
958d26aClaude162 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
163 </a>
adf5e18Claude164 {/* Migrate from GitHub — prominent secondary CTA */}
165 <a
166 href="/import"
167 class="btn btn-xl landing-cta-migrate"
168 data-testid="cta-migrate"
169 >
170 Migrate from GitHub
171 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
172 </a>
dc26881CC LABS App173 {/* BLOCK Q1 — one-click Claude Desktop install. */}
93fe97eClaude174 <a
175 href="/gluecron.dxt"
176 class="btn btn-xl landing-cta-dxt"
177 download
178 data-testid="cta-dxt"
179 >
180 Add to Claude Desktop
181 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
182 </a>
dc26881CC LABS App183 </div>
184
185 {/* U1 — tertiary text-link row.
186 Visually subordinate to the buttons above. Keeps the
187 "Try the live demo" + "Compare to GitHub" affordances
188 from L10 + the Q3 "try without signing up" /play link
189 without crowding the primary CTA row. */}
190 <div class="landing-hero-tertiary" data-testid="hero-tertiary-row">
191 <a href="/demo" class="landing-hero-tertiary-link" data-testid="cta-tertiary-demo">
5f2e749Claude192 Try the live demo
dc26881CC LABS App193 <span aria-hidden="true">{" →"}</span>
958d26aClaude194 </a>
dc26881CC LABS App195 <span class="landing-hero-tertiary-sep" aria-hidden="true">·</span>
196 <a href="/vs-github" class="landing-hero-tertiary-link" data-testid="cta-tertiary-vs">
52ad8b1Claude197 Compare to GitHub
dc26881CC LABS App198 <span aria-hidden="true">{" →"}</span>
52ad8b1Claude199 </a>
dc26881CC LABS App200 <span class="landing-hero-tertiary-sep" aria-hidden="true">·</span>
201 <a href="/play" class="landing-hero-tertiary-link" data-testid="cta-play">
202 Try it without signing up
cd4f63bTest User203 <span aria-hidden="true">{" →"}</span>
204 </a>
205 </div>
206
dc26881CC LABS App207 {/* U1 — power-users install snippet panel.
208 Moved BELOW the CTAs so it doesn't compete for the eye
209 with the primary "Sign up free" button. */}
210 <div class="landing-hero-install-wrap" aria-label="Power users install panel">
211 <div class="landing-hero-install-label">For power users</div>
212 <div class="landing-hero-install" aria-label="One-line install">
213 <code class="landing-hero-install-code">
214 <span class="landing-hero-install-prompt" aria-hidden="true">$</span>
215 <span id="landing-install-text">curl -sSL gluecron.com/install | bash</span>
216 </code>
217 <button
218 type="button"
219 class="landing-hero-install-copy"
220 data-copy-target="landing-install-text"
221 aria-label="Copy install command"
222 >
223 Copy
224 </button>
225 </div>
226 </div>
227
b5d207bClaude228 {/* U1 — tightened activity rail.
dc26881CC LABS App229 Same data as before, rendered as a single horizontal
230 rule with the gradient accent line on top. Numbers
231 smaller, copy still scannable. */}
5f2e749Claude232 {publicStats && (
3575261Claude233 <ul class="landing-hero-rail" aria-label="Gluecron live this week">
5f2e749Claude234 <li>
235 <strong>{publicStats.weeklyPrsAutoMerged.toLocaleString()}</strong>
dc26881CC LABS App236 <span class="landing-hero-rail-label">PRs auto-merged</span>
5f2e749Claude237 </li>
238 <li>
239 <strong>{publicStats.weeklyIssuesBuiltByAi.toLocaleString()}</strong>
dc26881CC LABS App240 <span class="landing-hero-rail-label">issues built by AI</span>
5f2e749Claude241 </li>
242 <li>
243 <strong>{publicStats.weeklyDeploysShipped.toLocaleString()}</strong>
3575261Claude244 <span class="landing-hero-rail-label">deploys shipped</span>
5f2e749Claude245 </li>
246 <li>
dc26881CC LABS App247 <strong>{`~${Math.round(publicStats.weeklyHoursSaved).toLocaleString()}`}</strong>
248 <span class="landing-hero-rail-label">hours saved by AI</span>
5f2e749Claude249 </li>
250 </ul>
251 )}
252
253 {/* L8 — free-tier reassurance link. Keeps anxiety low for the AI-curious. */}
254 <p class="landing-hero-freenote">
255 Free forever for the AI-curious.{" "}
256 <a href="/pricing" class="landing-hero-freenote-link">
257 See pricing &rarr;
258 </a>
259 </p>
260
958d26aClaude261 <p class="landing-hero-caption">
262 Already have a repo?
263 <span class="landing-hero-cmd">
264 <span class="kbd">git</span>
265 <span class="kbd">remote</span>
266 <span class="kbd">add</span>
267 <span class="kbd">gluecron</span>
268 <span class="landing-hero-arrow">{"→"}</span>
269 <span class="kbd">git push</span>
270 </span>
271 </p>
272
273 {hasStats && (
274 <p class="landing-stats">
275 {stats!.publicRepos !== undefined && stats!.publicRepos > 0 && (
276 <span>
277 <strong>{stats!.publicRepos.toLocaleString()}</strong>
278 {stats!.publicRepos === 1 ? " repo" : " repos"}
279 </span>
280 )}
281 {stats!.publicRepos !== undefined &&
282 stats!.publicRepos > 0 &&
283 stats!.users !== undefined &&
284 stats!.users > 0 && <span class="landing-stats-sep">·</span>}
285 {stats!.users !== undefined && stats!.users > 0 && (
286 <span>
287 <strong>{stats!.users.toLocaleString()}</strong>
288 {stats!.users === 1 ? " developer" : " developers"}
289 </span>
290 )}
291 </p>
292 )}
293 </div>
c963db5Claude294 </section>
c475ee6Claude295
534f04aClaude296 {/* ---------- Block M1 — Live-now demo feed ---------- */}
297 <LiveNowSection
298 queued={liveQueued}
299 merges={liveMerges}
300 reviews={liveReviews}
301 reviewCount={liveReviewCount}
302 feed={liveEntries}
303 />
304
52ad8b1Claude305 {/* ---------- L4 social-proof counters (animated count-up) ---------- */}
306 {tiles && (
307 <section class="landing-counters" aria-label="Gluecron live counters">
308 <div class="landing-counters-grid">
309 {tiles.map((t) => (
310 <div class="landing-counter">
311 <div
312 class="landing-counter-num"
313 data-counter-target={String(t.value)}
314 data-counter-suffix={t.suffix ?? ""}
315 data-counter-prefix={t.prefix ?? ""}
316 >
317 {t.prefix ?? ""}
318 {t.value.toLocaleString()}
319 {t.suffix ?? ""}
320 </div>
321 <div class="landing-counter-label">{t.label}</div>
322 </div>
323 ))}
324 </div>
325 <script dangerouslySetInnerHTML={{ __html: landingCountersJs }} />
326 </section>
327 )}
328
5f2e749Claude329 {/* ---------- L10 — Three reasons to switch ---------- */}
330 <section class="landing-section landing-reasons" aria-label="Three reasons to switch">
331 <div class="section-header">
332 <div class="eyebrow">Three reasons to switch</div>
333 <h2>Built so Claude can do the work.</h2>
334 </div>
335 <div class="landing-reasons-grid">
336 <ReasonCard
337 icon={
338 <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">
339 <path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z" />
340 </svg>
341 }
3575261Claude342 title="Ships in seconds, not tabs"
343 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."
344 link={{ href: "/sleep-mode", label: "See Sleep Mode" }}
5f2e749Claude345 />
346 <ReasonCard
347 icon={
348 <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">
349 <polyline points="4 17 10 11 4 5" />
350 <line x1="12" y1="19" x2="20" y2="19" />
351 </svg>
352 }
353 title="One command to migrate"
354 body="Drop a single curl into your shell. Gluecron rehosts your repo, your issues, your branches — no SaaS rip-and-replace project required."
355 extra={
356 <code class="landing-reasons-code">curl -sSL gluecron.com/install | bash</code>
357 }
358 link={{ href: "/import", label: "Or import from GitHub" }}
359 />
360 <ReasonCard
361 icon={
362 <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">
363 <polygon points="5 3 19 12 5 21 5 3" />
364 </svg>
365 }
366 title="Open the demo, watch it work"
3575261Claude367 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."
5f2e749Claude368 link={{ href: "/demo", label: "Open the live demo" }}
369 />
370 </div>
371 </section>
372
9ecf5a4Claude373 {/* ---------- Capability strip — uppercase tracked grid (vapron-style) ---------- */}
c963db5Claude374 <section class="landing-caps">
375 <div class="landing-caps-grid">
376 <span class="landing-cap">Claude-powered AI</span>
377 <span class="landing-cap">Spec-to-PR</span>
378 <span class="landing-cap">Auto-repair</span>
379 <span class="landing-cap">Real-time gates</span>
380 <span class="landing-cap">MCP-native</span>
381 <span class="landing-cap">Workflow runner</span>
382 <span class="landing-cap">Self-hostable</span>
383 <span class="landing-cap">Branch protection</span>
384 <span class="landing-cap">Bun + Hono</span>
385 <span class="landing-cap">Drizzle + Postgres</span>
386 <span class="landing-cap">JSX server-rendered</span>
387 <span class="landing-cap">Type-safe end to end</span>
c475ee6Claude388 </div>
958d26aClaude389 </section>
390
9ecf5a4Claude391 {/* ---------- Big stat row (vapron-style hero closer) ---------- */}
c963db5Claude392 <section class="landing-bigstats">
393 <div class="landing-bigstats-grid">
394 <div class="landing-bigstat">
395 <div class="landing-bigstat-num">Claude-powered</div>
396 <div class="landing-bigstat-label">The best AI, native</div>
397 </div>
398 <div class="landing-bigstat">
399 <div class="landing-bigstat-num">Self-hosted</div>
400 <div class="landing-bigstat-label">On your hardware</div>
401 </div>
402 <div class="landing-bigstat">
403 <div class="landing-bigstat-num">MCP-native</div>
404 <div class="landing-bigstat-label">Claude · Cursor · Code</div>
405 </div>
406 <div class="landing-bigstat">
407 <div class="landing-bigstat-num">Real-time</div>
408 <div class="landing-bigstat-label">SSE everywhere</div>
409 </div>
958d26aClaude410 </div>
4c47454Claude411 </section>
412
413 {/* ---------- Feature grid ---------- */}
958d26aClaude414 <section class="landing-section">
415 <div class="section-header">
416 <div class="eyebrow">The platform</div>
417 <h2>An IDE for your repo, not just a host.</h2>
418 <p>
419 Gluecron ships the surfaces GitHub charges extra for, and the
420 ones it never built. AI is a teammate with its own commits, not
421 a sidebar.
2b821b7Claude422 </p>
423 </div>
4c47454Claude424
958d26aClaude425 <div class="landing-features stagger">
426 <FeatureCard
427 icon={
428 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
429 <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" />
430 </svg>
431 }
432 title="AI as a teammate"
433 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."
434 />
435 <FeatureCard
436 icon={
437 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
438 <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" />
439 <path d="M9 12l2 2 4-4" />
440 </svg>
441 }
442 title="Quality gate that learns"
443 desc="GateTest scans every push. Auto-repair fixes regressions before you see them. Required checks block bad PRs from merging. Your software self-corrects."
444 />
445 <FeatureCard
446 icon={
447 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
448 <path d="M13 2L4 14h7l-1 8 9-12h-7z" />
449 </svg>
450 }
451 title="Real-time everything"
452 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."
453 />
454 <FeatureCard
455 icon={
456 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
457 <rect x="3" y="3" width="18" height="18" rx="3" />
458 <path d="M3 9h18M9 21V9" />
459 </svg>
460 }
461 title="Workflow runner"
462 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."
463 />
464 <FeatureCard
465 icon={
466 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
467 <circle cx="12" cy="12" r="9" />
468 <path d="M3 12h18M12 3a14 14 0 010 18M12 3a14 14 0 000 18" />
469 </svg>
470 }
471 title="MCP-native"
472 desc="Claude, Cursor, Code — they speak Model Context Protocol. Gluecron exposes search, file read, issues, codebase explain as MCP tools by default."
473 />
474 <FeatureCard
475 icon={
476 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
477 <path d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
478 <path d="M12 7v5l3 2" />
479 </svg>
480 }
481 title="Yours, on your hardware"
482 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."
483 />
2b821b7Claude484 </div>
958d26aClaude485 </section>
2b821b7Claude486
958d26aClaude487 {/* ---------- Workflow walkthrough ---------- */}
488 <section class="landing-section landing-walk">
489 <div class="section-header">
490 <div class="eyebrow">How it works</div>
491 <h2>Push code. Watch it ship.</h2>
492 <p>
493 Every push triggers the same pipeline whether the commit came
494 from you, from CI, or from an AI agent.
4c47454Claude495 </p>
496 </div>
958d26aClaude497
498 <div class="landing-walk-grid">
499 <WalkStep n="01" title="Push" desc="git push to gluecron — Smart-HTTP, SSH, or via the web editor." />
500 <WalkStep n="02" title="Gate" desc="GateTest runs. Secret scanner runs. AI security review posts inline comments." />
501 <WalkStep n="03" title="Repair" desc="If a gate fails, auto-repair tries to fix it. New commit gets re-gated." />
9ecf5a4Claude502 <WalkStep n="04" title="Ship" desc="Green push to default branch fires deploy webhook. Vapron, Fly, your prod." />
958d26aClaude503 </div>
4c47454Claude504 </section>
2b821b7Claude505
4c47454Claude506 {/* ---------- Terminal block ---------- */}
958d26aClaude507 <section class="landing-section landing-terminal-section">
508 <div class="landing-terminal-wrap">
509 <div class="landing-terminal" role="img" aria-label="Example git push to gluecron with passing gates">
510 <div class="landing-terminal-chrome">
511 <span class="landing-terminal-dot landing-terminal-dot-r" />
512 <span class="landing-terminal-dot landing-terminal-dot-y" />
513 <span class="landing-terminal-dot landing-terminal-dot-g" />
514 <span class="landing-terminal-title">~/your-repo &mdash; zsh</span>
515 </div>
516 <div class="landing-terminal-body">
517 <div class="landing-term-line">
518 <span class="landing-term-prompt">$</span>
519 <span>git remote add gluecron https://gluecron.com/you/your-repo.git</span>
520 </div>
521 <div class="landing-term-line">
522 <span class="landing-term-prompt">$</span>
523 <span>git push -u gluecron main</span>
524 </div>
525 <div class="landing-term-line landing-term-out">
526 <span class="landing-term-meta">remote:</span>
527 <span>Resolving deltas… 100% (24/24)</span>
528 </div>
529 <div class="landing-term-line landing-term-out landing-term-ok-line">
530 <span class="landing-term-ok">{"✓"}</span>
531 <span>pushed to gluecron.com/you/your-repo</span>
532 </div>
533 <div class="landing-term-line landing-term-out landing-term-ok-line">
534 <span class="landing-term-ok">{"✓"}</span>
535 <span>GateTest passed (12 rules, 0 violations)</span>
536 </div>
537 <div class="landing-term-line landing-term-out landing-term-ok-line">
538 <span class="landing-term-ok">{"✓"}</span>
539 <span>AI review posted (2 suggestions, 0 blockers)</span>
540 </div>
541 <div class="landing-term-line landing-term-out landing-term-ok-line">
542 <span class="landing-term-ok">{"✓"}</span>
543 <span>deployed to your-repo.gluecron.com <span class="landing-term-meta">(4.1s)</span></span>
544 </div>
545 <div class="landing-term-line landing-term-cursor">
546 <span class="landing-term-prompt">$</span>
547 <span class="landing-term-blink">▍</span>
548 </div>
549 </div>
4c47454Claude550 </div>
958d26aClaude551 </div>
552 </section>
553
554 {/* ---------- Comparison ---------- */}
555 <section class="landing-section">
556 <div class="section-header">
557 <div class="eyebrow">vs the incumbent</div>
558 <h2>Everything GitHub charges for. And the parts they didn't build.</h2>
559 </div>
560
561 <div class="landing-compare">
562 <CompareRow feature="Git hosting + Smart-HTTP push" them="✓" us="✓" />
563 <CompareRow feature="Issues, PRs, code review" them="✓" us="✓" />
564 <CompareRow feature="Workflow runner (Actions-equivalent)" them="paid minutes" us="self-hosted, unmetered" highlight />
565 <CompareRow feature="AI code review on every PR" them="Copilot subscription" us="built in" highlight />
566 <CompareRow feature="Spec-to-PR (NL feature → draft PR)" them="—" us="✓" highlight />
567 <CompareRow feature="Auto-repair on failed gates" them="—" us="✓" highlight />
568 <CompareRow feature="Real-time SSE for logs + PRs" them="polling" us="streaming" highlight />
569 <CompareRow feature="MCP server (Claude / Cursor)" them="—" us="✓" highlight />
570 <CompareRow feature="Self-host on your own infra" them="enterprise tier" us="single binary" highlight />
571 <CompareRow feature="Pre-receive policy enforcement" them="rulesets (GHE)" us="✓" />
572 </div>
573 </section>
574
575 {/* ---------- Pricing teaser ---------- */}
576 <section class="landing-section">
577 <div class="section-header">
578 <div class="eyebrow">Pricing</div>
579 <h2>Free to start. Honest at scale.</h2>
580 <p>
581 Self-hosting is free forever. Hosted plans price the AI calls,
582 not the seats.
583 </p>
584 </div>
585
586 <div class="landing-pricing">
587 <PricingCard
588 tier="Free"
589 price="$0"
590 cadence="forever"
591 desc="For personal projects + open source. Public + private repos, full AI suite, fair quotas."
592 features={["Unlimited public repos", "3 private repos", "5K AI calls / mo", "Community support"]}
593 cta="Start free"
594 href="/register"
595 />
596 <PricingCard
597 tier="Pro"
598 price="$12"
599 cadence="per user / mo"
600 desc="For working developers. Lifts every quota, adds priority routing, no Gluecron branding on deploys."
601 features={["Unlimited private repos", "100K AI calls / mo", "Priority queue", "Custom domains"]}
602 cta="Go Pro"
603 href="/settings/billing"
604 highlight
605 />
606 <PricingCard
607 tier="Team"
608 price="Talk to us"
609 cadence="custom"
610 desc="For orgs running production on Gluecron. SSO, audit retention, enterprise SLA, on-prem."
611 features={["SSO + SCIM", "On-prem deploy", "Dedicated capacity", "24/7 incident response"]}
612 cta="Contact"
613 href="mailto:hello@gluecron.com"
614 />
615 </div>
616 </section>
617
5f2e749Claude618 {/* ---------- L10 — "How is this different?" pull-quote ---------- */}
619 <section class="landing-pullquote-section" aria-label="How is this different from GitHub?">
620 <figure class="landing-pullquote">
621 <div class="landing-pullquote-eyebrow">How is this different from GitHub?</div>
622 <blockquote class="landing-pullquote-text">
623 Every other host bolts AI on as a sidecar. Gluecron is the first
624 git host where Claude is a first-class developer. Built to be
625 operated by AI agents, not just augmented by them.
626 </blockquote>
627 <a href="/vs-github" class="landing-pullquote-link">
628 See the full comparison
629 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
630 </a>
631 </figure>
632 </section>
633
958d26aClaude634 {/* ---------- Closing CTA ---------- */}
635 <section class="landing-cta-section">
636 <div class="landing-cta-card">
637 <div class="landing-cta-bg" aria-hidden="true" />
638 <div class="eyebrow">Ready when you are</div>
639 <h2 class="landing-cta-title">
640 Stop maintaining the platform.<br />
8cfb00eClaude641 Start shipping the product.
958d26aClaude642 </h2>
643 <p class="landing-cta-sub">
644 Free to start, self-hosted-friendly, MCP-native. Migrate from
645 GitHub in one click.
646 </p>
647 <div class="landing-cta-buttons">
648 <a href="/register" class="btn btn-primary btn-xl">
649 Create your account
650 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
651 </a>
652 <a href="/import" class="btn btn-ghost btn-xl">
653 Migrate a repo
654 </a>
4c47454Claude655 </div>
9a8b8deClaude656 <div style="margin-top:var(--space-3);font-size:13.5px;color:var(--text-muted)">
657 Migrating from GitHub?{" "}
658 <a href="/migrate" style="color:var(--accent)">
659 Import your entire org in one click &rarr;
660 </a>
661 </div>
4c47454Claude662 </div>
663 </section>
5f2e749Claude664
665 {/* L10 — clipboard copy script for the hero install snippet. */}
666 <script dangerouslySetInnerHTML={{ __html: landingCopyJs }} />
4c47454Claude667 </div>
2b821b7Claude668 </>
669 );
670};
671
958d26aClaude672const FeatureCard: FC<{ icon: any; title: string; desc: string }> = ({
673 icon,
674 title,
675 desc,
676}) => (
677 <div class="landing-feature">
678 <div class="landing-feature-icon" aria-hidden="true">
679 {icon}
680 </div>
681 <h3 class="landing-feature-title">{title}</h3>
682 <p class="landing-feature-desc">{desc}</p>
683 </div>
684);
685
5f2e749Claude686// Block L10 — "Three reasons to switch" column.
687const ReasonCard: FC<{
688 icon: any;
689 title: string;
690 body: string;
691 link: { href: string; label: string };
692 extra?: any;
693}> = ({ icon, title, body, link, extra }) => (
694 <div class="landing-reason">
695 <div class="landing-reason-icon" aria-hidden="true">
696 {icon}
697 </div>
698 <h3 class="landing-reason-title">{title}</h3>
699 <p class="landing-reason-body">{body}</p>
700 {extra}
701 <a href={link.href} class="landing-reason-link">
702 {link.label}
703 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
704 </a>
705 </div>
706);
707
958d26aClaude708const WalkStep: FC<{ n: string; title: string; desc: string }> = ({
709 n,
710 title,
711 desc,
712}) => (
713 <div class="landing-walk-step">
714 <div class="landing-walk-num">{n}</div>
715 <h3 class="landing-walk-title">{title}</h3>
716 <p class="landing-walk-desc">{desc}</p>
717 </div>
718);
719
720const CompareRow: FC<{
721 feature: string;
722 them: string;
723 us: string;
724 highlight?: boolean;
725}> = ({ feature, them, us, highlight }) => (
726 <div class={`landing-compare-row${highlight ? " landing-compare-hl" : ""}`}>
727 <div class="landing-compare-feature">{feature}</div>
728 <div class="landing-compare-them">{them}</div>
729 <div class="landing-compare-us">{us === "✓" ? "✓" : us}</div>
730 </div>
731);
732
733const PricingCard: FC<{
734 tier: string;
735 price: string;
736 cadence: string;
737 desc: string;
738 features: string[];
739 cta: string;
740 href: string;
741 highlight?: boolean;
742}> = ({ tier, price, cadence, desc, features, cta, href, highlight }) => (
743 <div class={`landing-price-card${highlight ? " landing-price-hl" : ""}`}>
744 {highlight && <div class="landing-price-badge">Most popular</div>}
745 <div class="landing-price-tier">{tier}</div>
746 <div class="landing-price-amount">
747 <span class="landing-price-num">{price}</span>
748 <span class="landing-price-cad">{cadence}</span>
749 </div>
750 <p class="landing-price-desc">{desc}</p>
751 <ul class="landing-price-features">
752 {features.map((f) => (
753 <li>
754 <span class="landing-price-check" aria-hidden="true">{"✓"}</span>
755 {f}
756 </li>
757 ))}
758 </ul>
759 <a
760 href={href}
761 class={`btn ${highlight ? "btn-primary" : "btn-secondary"} btn-block landing-price-cta`}
762 >
763 {cta}
764 </a>
765 </div>
766);
767
52ad8b1Claude768// ─────────────────────────────────────────────────────────────────
769// Block L4 — social-proof tile builder.
770//
771// Pure: takes the cached PublicStats payload and emits the six
772// landing-page tiles in render order. Exported so tests / future
773// surfaces (dashboard, /about, …) can share the exact same copy.
774// ─────────────────────────────────────────────────────────────────
775
776export interface SocialProofTile {
777 label: string;
778 value: number;
779 prefix?: string;
780 suffix?: string;
781}
782
783export function buildSocialProofTiles(s: PublicStats): SocialProofTile[] {
784 return [
785 { label: "Public repos", value: s.totalPublicRepos },
786 { label: "Developers", value: s.totalUsers },
787 {
788 label: "PRs auto-merged this week",
789 value: s.weeklyPrsAutoMerged,
790 },
791 {
792 label: "Issues built by AI this week",
793 value: s.weeklyIssuesBuiltByAi,
794 },
795 {
796 label: "Deploys shipped this week",
797 value: s.weeklyDeploysShipped,
798 },
799 {
800 label: "Hours saved this week",
801 // Round to whole hours for the tile — the precise 0.1 figure
802 // lives on the dashboard widget; the marketing surface keeps
803 // the number scannable.
804 value: Math.round(s.weeklyHoursSaved),
805 prefix: "~",
806 suffix: "h",
807 },
808 ];
809}
810
534f04aClaude811// ─────────────────────────────────────────────────────────────────
812// Block M1 — Live-now demo feed.
813//
814// A 4-tile row inserted between the hero and the L4 counters tile
815// section, surfacing real autopilot activity from the seeded `demo`
816// owner's repos:
817// 1. Issues queued for AI build (ai:build label, open)
818// 2. PRs auto-merged in the last 24h
819// 3. AI reviews posted today (count + latest 3)
820// 4. Combined activity feed (last 10)
821//
822// SSR-renders an initial snapshot, then re-fetches every 30s via the
823// L3 JSON endpoints so the page feels alive without a websocket.
824// Pure presentational; the route layer owns the DB reads.
825// ─────────────────────────────────────────────────────────────────
826
827/**
828 * Render an ISO timestamp (or Date) as a coarse "about N units ago"
829 * string. Tolerates strings, Dates, NaN, and future timestamps.
830 *
831 * Exported for unit testing.
832 */
833export function relativeTimeFromNow(
834 value: string | Date | number | null | undefined,
835 now: number = Date.now()
836): string {
837 if (value === null || value === undefined) return "just now";
838 let t: number;
839 if (value instanceof Date) {
840 t = value.getTime();
841 } else if (typeof value === "number") {
842 t = value;
843 } else {
844 t = new Date(value).getTime();
845 }
846 if (!Number.isFinite(t)) return "just now";
847 const delta = now - t;
848 // Future timestamps (clock skew) — treat as "just now" rather than
849 // surfacing a confusing negative.
850 if (delta < 0) return "just now";
851 const s = Math.floor(delta / 1000);
852 if (s < 60) return "just now";
853 const m = Math.floor(s / 60);
854 if (m < 60) return `about ${m} minute${m === 1 ? "" : "s"} ago`;
855 const h = Math.floor(m / 60);
856 if (h < 24) return `about ${h} hour${h === 1 ? "" : "s"} ago`;
857 const d = Math.floor(h / 24);
858 return `about ${d} day${d === 1 ? "" : "s"} ago`;
859}
860
861function feedEntryId(e: LandingLiveFeedEntry): string {
862 const at = e.at instanceof Date ? e.at.toISOString() : String(e.at);
863 return `${e.kind}|${e.repo}|${e.ref.type}|${e.ref.number}|${at}`;
864}
865
866function feedEntryLabel(kind: LandingLiveFeedEntry["kind"]): string {
867 switch (kind) {
868 case "auto_merge.merged":
869 return "auto-merged";
870 case "ai_build.dispatched":
871 return "AI-build queued";
872 case "ai_review.posted":
873 return "AI review posted";
874 }
875}
876
877interface LiveNowSectionProps {
878 queued: LandingLiveFeedQueued[];
879 merges: LandingLiveFeedMerge[];
880 reviews: LandingLiveFeedReview[];
881 reviewCount: number;
882 feed: LandingLiveFeedEntry[];
883}
884
885const LiveNowSection: FC<LiveNowSectionProps> = ({
886 queued,
887 merges,
888 reviews,
889 reviewCount,
890 feed,
891}) => {
892 return (
893 <section class="landing-livenow" aria-labelledby="landing-livenow-h">
894 <div class="landing-livenow-head">
895 <div class="landing-livenow-eyebrow">
896 <span class="landing-livenow-pulse" aria-hidden="true" />
897 Live now
898 </div>
899 <h2 id="landing-livenow-h" class="landing-livenow-title">
900 Claude is working on demo repos as you read this.
901 </h2>
902 <p class="landing-livenow-sub">
903 Every card below is real data from the public{" "}
904 <code>{DEMO_USERNAME}/*</code> repos. Refreshes every 30 seconds.
905 </p>
906 </div>
907
908 <div class="landing-livenow-grid" data-livenow-grid>
909 {/* Card 1 — queued issues */}
910 <article class="landing-livecard" aria-labelledby="lc-queued-h">
911 <header class="landing-livecard-head">
912 <span class="landing-livecard-dot" aria-hidden="true" />
913 <h3 id="lc-queued-h" class="landing-livecard-title">
914 Issues queued for AI
915 </h3>
916 </header>
917 <ul class="landing-livecard-list" data-livecard="queued">
918 {queued.length === 0 ? (
919 <li class="landing-livecard-empty">
920 No queued AI builds — quiet right now.
921 </li>
922 ) : (
923 queued.slice(0, 3).map((i) => (
924 <li
925 class="landing-livecard-row"
926 data-row-id={`queued|${i.repo}|${i.number}`}
927 >
928 <a
929 class="landing-livecard-link"
930 href={`/${DEMO_USERNAME}/${i.repo}/issues/${i.number}`}
931 >
932 <span class="landing-livecard-num">#{i.number}</span>{" "}
933 <span class="landing-livecard-title-text">{i.title}</span>
934 </a>
935 <div class="landing-livecard-meta">
936 <span class="landing-livecard-repo">{i.repo}</span>
937 </div>
938 </li>
939 ))
940 )}
941 </ul>
942 </article>
943
944 {/* Card 2 — recently merged */}
945 <article class="landing-livecard" aria-labelledby="lc-merges-h">
946 <header class="landing-livecard-head">
947 <span class="landing-livecard-dot" aria-hidden="true" />
948 <h3 id="lc-merges-h" class="landing-livecard-title">
949 Recently merged by AI
950 </h3>
951 </header>
952 <ul class="landing-livecard-list" data-livecard="merges">
953 {merges.length === 0 ? (
954 <li class="landing-livecard-empty">
955 No auto-merges in the last 24h.
956 </li>
957 ) : (
958 merges.slice(0, 3).map((m) => (
959 <li
960 class="landing-livecard-row"
961 data-row-id={`merges|${m.repo}|${m.number}`}
962 >
963 <a
964 class="landing-livecard-link"
965 href={`/${DEMO_USERNAME}/${m.repo}/pulls/${m.number}`}
966 >
967 <span class="landing-livecard-num">#{m.number}</span>{" "}
968 <span class="landing-livecard-title-text">{m.title}</span>
969 </a>
970 <div class="landing-livecard-meta">
971 AI merged in{" "}
972 <span class="landing-livecard-repo">{m.repo}</span>{" "}
973 <span
974 class="landing-livecard-rel"
975 data-rel={
976 m.mergedAt instanceof Date
977 ? m.mergedAt.toISOString()
978 : String(m.mergedAt)
979 }
980 >
981 {relativeTimeFromNow(m.mergedAt)}
982 </span>
983 </div>
984 </li>
985 ))
986 )}
987 </ul>
988 </article>
989
990 {/* Card 3 — AI reviews */}
991 <article class="landing-livecard" aria-labelledby="lc-reviews-h">
992 <header class="landing-livecard-head">
993 <span class="landing-livecard-dot" aria-hidden="true" />
994 <h3 id="lc-reviews-h" class="landing-livecard-title">
995 AI reviews posted
996 </h3>
997 </header>
998 <div class="landing-livecard-bignum">
999 <span
1000 class="landing-livecard-bignum-n"
1001 data-livecard-count="reviews"
1002 data-tick-target={String(reviewCount)}
1003 >
1004 {reviewCount.toLocaleString()}
1005 </span>
1006 <span class="landing-livecard-bignum-label">reviews today</span>
1007 </div>
1008 <ul class="landing-livecard-list" data-livecard="reviews">
1009 {reviews.length === 0 ? (
1010 <li class="landing-livecard-empty">
1011 No AI reviews in the last 24h.
1012 </li>
1013 ) : (
1014 reviews.slice(0, 3).map((r) => (
1015 <li
1016 class="landing-livecard-row"
1017 data-row-id={`reviews|${r.repo}|${r.prNumber}`}
1018 >
1019 <a
1020 class="landing-livecard-link"
1021 href={`/${DEMO_USERNAME}/${r.repo}/pulls/${r.prNumber}`}
1022 >
1023 <span class="landing-livecard-num">#{r.prNumber}</span>{" "}
1024 <span class="landing-livecard-snippet">
1025 {r.commentSnippet}
1026 </span>
1027 </a>
1028 <div class="landing-livecard-meta">
1029 <span class="landing-livecard-repo">{r.repo}</span>
1030 </div>
1031 </li>
1032 ))
1033 )}
1034 </ul>
1035 </article>
1036
1037 {/* Card 4 — activity feed */}
1038 <article class="landing-livecard" aria-labelledby="lc-feed-h">
1039 <header class="landing-livecard-head">
1040 <span class="landing-livecard-dot" aria-hidden="true" />
1041 <h3 id="lc-feed-h" class="landing-livecard-title">
1042 Activity feed
1043 </h3>
1044 </header>
1045 <ul class="landing-livecard-list landing-livecard-feed" data-livecard="feed">
1046 {feed.length === 0 ? (
1047 <li class="landing-livecard-empty">
1048 Quiet right now — check back in a minute.
1049 </li>
1050 ) : (
1051 feed.slice(0, 10).map((e) => {
1052 const path = e.ref.type === "pr" ? "pulls" : "issues";
1053 const id = feedEntryId(e);
1054 return (
1055 <li class="landing-livecard-feedrow" data-row-id={id}>
1056 <span
1057 class={`landing-livecard-kind landing-livecard-kind-${e.kind.replace(/\./g, "-")}`}
1058 >
1059 {feedEntryLabel(e.kind)}
1060 </span>{" "}
1061 <a
1062 class="landing-livecard-link"
1063 href={`/${DEMO_USERNAME}/${e.repo}/${path}/${e.ref.number}`}
1064 >
1065 {e.repo} #{e.ref.number}
1066 </a>{" "}
1067 <span
1068 class="landing-livecard-rel"
1069 data-rel={
1070 e.at instanceof Date ? e.at.toISOString() : String(e.at)
1071 }
1072 >
1073 {relativeTimeFromNow(e.at)}
1074 </span>
1075 </li>
1076 );
1077 })
1078 )}
1079 </ul>
1080 </article>
1081 </div>
1082
1083 <div class="landing-livenow-cta">
1084 <span class="landing-livenow-cta-text">
1085 Want this for your repos?
1086 </span>
1087 <a class="landing-livenow-cta-link" href="/register">
1088 Sign up free
1089 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
1090 </a>
1091 <span class="landing-livenow-cta-sep" aria-hidden="true">·</span>
1092 <a class="landing-livenow-cta-link" href="/demo">
1093 Try the live demo
1094 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
1095 </a>
1096 </div>
1097
1098 <script dangerouslySetInnerHTML={{ __html: liveNowJs }} />
1099 </section>
1100 );
1101};
1102
1103// Inline poller. Plain JS so we don't ship a separate bundle. Hits the
1104// four L3 JSON endpoints every 30s, re-renders the four cards, ticks
1105// the big number, refreshes relative timestamps, flashes new rows.
1106const liveNowJs = `
1107(function(){
1108try{
1109 var DEMO=${JSON.stringify(DEMO_USERNAME)};
1110 var INTERVAL=30000;
1111 function esc(s){return String(s==null?'':s).replace(/[&<>"']/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c];});}
1112 function rel(v){
1113 if(v==null) return 'just now';
1114 var t=(v instanceof Date)?v.getTime():(typeof v==='number'?v:new Date(v).getTime());
1115 if(!isFinite(t)) return 'just now';
1116 var d=Date.now()-t;
1117 if(d<0) return 'just now';
1118 var s=Math.floor(d/1000);
1119 if(s<60) return 'just now';
1120 var m=Math.floor(s/60);
1121 if(m<60) return 'about '+m+' minute'+(m===1?'':'s')+' ago';
1122 var h=Math.floor(m/60);
1123 if(h<24) return 'about '+h+' hour'+(h===1?'':'s')+' ago';
1124 var dd=Math.floor(h/24);
1125 return 'about '+dd+' day'+(dd===1?'':'s')+' ago';
1126 }
1127 function tickNumber(el,target){
1128 if(!el) return;
1129 var start=parseInt(el.getAttribute('data-tick-current')||'0',10)||0;
1130 if(start===target){el.textContent=target.toLocaleString();el.setAttribute('data-tick-current',String(target));return;}
1131 var dur=800,t0=performance.now();
1132 function step(now){
1133 var p=Math.min(1,(now-t0)/dur);
1134 var eased=1-Math.pow(1-p,3);
1135 var v=Math.round(start+(target-start)*eased);
1136 el.textContent=v.toLocaleString();
1137 if(p<1) requestAnimationFrame(step); else el.setAttribute('data-tick-current',String(target));
1138 }
1139 requestAnimationFrame(step);
1140 }
1141 function flashRow(li){
1142 if(!li) return;
1143 li.classList.add('landing-livecard-flash');
1144 setTimeout(function(){li.classList.remove('landing-livecard-flash');},1100);
1145 }
1146 function diffMount(ul,newHtml,newIds){
1147 if(!ul) return;
1148 var prev={};
1149 var nodes=ul.querySelectorAll('[data-row-id]');
1150 for(var i=0;i<nodes.length;i++){prev[nodes[i].getAttribute('data-row-id')]=true;}
1151 ul.innerHTML=newHtml;
1152 var fresh=ul.querySelectorAll('[data-row-id]');
1153 for(var j=0;j<fresh.length;j++){
1154 var id=fresh[j].getAttribute('data-row-id');
1155 if(id && !prev[id]) flashRow(fresh[j]);
1156 }
1157 }
1158 function pollQueued(){
1159 return fetch('/api/v2/demo/queued',{credentials:'omit'}).then(function(r){return r.json();}).then(function(d){
1160 var ul=document.querySelector('[data-livecard="queued"]');if(!ul) return;
1161 var items=(d&&d.items)||[];
1162 if(items.length===0){ul.innerHTML='<li class="landing-livecard-empty">No queued AI builds — quiet right now.</li>';return;}
1163 var ids=[];
1164 var html=items.slice(0,3).map(function(i){
1165 var id='queued|'+i.repo+'|'+i.number;ids.push(id);
1166 return '<li class="landing-livecard-row" data-row-id="'+esc(id)+'">'+
1167 '<a class="landing-livecard-link" href="/'+esc(DEMO)+'/'+esc(i.repo)+'/issues/'+i.number+'">'+
1168 '<span class="landing-livecard-num">#'+i.number+'</span> '+
1169 '<span class="landing-livecard-title-text">'+esc(i.title)+'</span></a>'+
1170 '<div class="landing-livecard-meta"><span class="landing-livecard-repo">'+esc(i.repo)+'</span></div></li>';
1171 }).join('');
1172 diffMount(ul,html,ids);
1173 }).catch(function(){});
1174 }
1175 function pollMerges(){
1176 return fetch('/api/v2/demo/merges',{credentials:'omit'}).then(function(r){return r.json();}).then(function(d){
1177 var ul=document.querySelector('[data-livecard="merges"]');if(!ul) return;
1178 var items=(d&&d.items)||[];
1179 if(items.length===0){ul.innerHTML='<li class="landing-livecard-empty">No auto-merges in the last 24h.</li>';return;}
1180 var ids=[];
1181 var html=items.slice(0,3).map(function(m){
1182 var id='merges|'+m.repo+'|'+m.number;ids.push(id);
1183 return '<li class="landing-livecard-row" data-row-id="'+esc(id)+'">'+
1184 '<a class="landing-livecard-link" href="/'+esc(DEMO)+'/'+esc(m.repo)+'/pulls/'+m.number+'">'+
1185 '<span class="landing-livecard-num">#'+m.number+'</span> '+
1186 '<span class="landing-livecard-title-text">'+esc(m.title)+'</span></a>'+
1187 '<div class="landing-livecard-meta">AI merged in <span class="landing-livecard-repo">'+esc(m.repo)+'</span> '+
1188 '<span class="landing-livecard-rel" data-rel="'+esc(m.mergedAt)+'">'+esc(rel(m.mergedAt))+'</span></div></li>';
1189 }).join('');
1190 diffMount(ul,html,ids);
1191 }).catch(function(){});
1192 }
1193 function pollReviews(){
1194 return fetch('/api/v2/demo/reviews',{credentials:'omit'}).then(function(r){return r.json();}).then(function(d){
1195 var ul=document.querySelector('[data-livecard="reviews"]');if(!ul) return;
1196 var bigEl=document.querySelector('[data-livecard-count="reviews"]');
1197 var n=(d&&typeof d.count==='number')?d.count:0;
1198 if(bigEl) tickNumber(bigEl,n);
1199 var items=(d&&d.items)||[];
1200 if(items.length===0){ul.innerHTML='<li class="landing-livecard-empty">No AI reviews in the last 24h.</li>';return;}
1201 var ids=[];
1202 var html=items.slice(0,3).map(function(r){
1203 var id='reviews|'+r.repo+'|'+r.prNumber;ids.push(id);
1204 return '<li class="landing-livecard-row" data-row-id="'+esc(id)+'">'+
1205 '<a class="landing-livecard-link" href="/'+esc(DEMO)+'/'+esc(r.repo)+'/pulls/'+r.prNumber+'">'+
1206 '<span class="landing-livecard-num">#'+r.prNumber+'</span> '+
1207 '<span class="landing-livecard-snippet">'+esc(r.commentSnippet)+'</span></a>'+
1208 '<div class="landing-livecard-meta"><span class="landing-livecard-repo">'+esc(r.repo)+'</span></div></li>';
1209 }).join('');
1210 diffMount(ul,html,ids);
1211 }).catch(function(){});
1212 }
1213 function pollFeed(){
1214 return fetch('/api/v2/demo/activity',{credentials:'omit'}).then(function(r){return r.json();}).then(function(d){
1215 var ul=document.querySelector('[data-livecard="feed"]');if(!ul) return;
1216 var entries=(d&&d.entries)||[];
1217 if(entries.length===0){ul.innerHTML='<li class="landing-livecard-empty">Quiet right now — check back in a minute.</li>';return;}
1218 var ids=[];
1219 var html=entries.slice(0,10).map(function(e){
1220 var path=(e.ref&&e.ref.type==='pr')?'pulls':'issues';
1221 var num=(e.ref&&e.ref.number)||0;
1222 var label=e.kind==='auto_merge.merged'?'auto-merged':(e.kind==='ai_build.dispatched'?'AI-build queued':'AI review posted');
1223 var kindCls=String(e.kind||'').replace(/\\./g,'-');
1224 var id=e.kind+'|'+e.repo+'|'+(e.ref&&e.ref.type)+'|'+num+'|'+e.at;ids.push(id);
1225 return '<li class="landing-livecard-feedrow" data-row-id="'+esc(id)+'">'+
1226 '<span class="landing-livecard-kind landing-livecard-kind-'+esc(kindCls)+'">'+esc(label)+'</span> '+
1227 '<a class="landing-livecard-link" href="/'+esc(DEMO)+'/'+esc(e.repo)+'/'+path+'/'+num+'">'+esc(e.repo)+' #'+num+'</a> '+
1228 '<span class="landing-livecard-rel" data-rel="'+esc(e.at)+'">'+esc(rel(e.at))+'</span></li>';
1229 }).join('');
1230 diffMount(ul,html,ids);
1231 }).catch(function(){});
1232 }
1233 function refreshRel(){
1234 var spans=document.querySelectorAll('.landing-livecard-rel[data-rel]');
1235 for(var i=0;i<spans.length;i++){
1236 spans[i].textContent=rel(spans[i].getAttribute('data-rel'));
1237 }
1238 }
1239 function tickAll(){pollQueued();pollMerges();pollReviews();pollFeed();}
1240 // Initial counter tick (count-up from 0) on first paint.
1241 var bigEl0=document.querySelector('[data-livecard-count="reviews"]');
1242 if(bigEl0){
1243 var target=parseInt(bigEl0.getAttribute('data-tick-target')||'0',10)||0;
1244 bigEl0.textContent='0';
1245 tickNumber(bigEl0,target);
1246 }
1247 refreshRel();
1248 setInterval(tickAll,INTERVAL);
1249 setInterval(refreshRel,INTERVAL);
1250 document.addEventListener('visibilitychange',function(){
1251 if(document.visibilityState==='visible'){tickAll();refreshRel();}
1252 });
1253}catch(_){}})();
1254`.trim();
1255
0c3eee5Claude1256// ============================================================
1257// Land2030 — 2030 homepage prelude (closed-loop showcase).
1258// ============================================================
1259//
1260// Renders BEFORE the existing LandingHero so every L10/U1/Q1/M1
1261// regression assertion keeps passing. CSS is scoped under
1262// `.land-2030-*` to avoid colliding with the older `.landing-*`
1263// styles. Inline SVG only — no extra deps.
1264
1265const ClosedLoopDiagram: FC = () => (
1266 <svg
1267 viewBox="0 0 1100 280"
1268 class="land-2030-loop-svg"
1269 role="img"
1270 aria-label="Closed loop: Spec to Code to AI Review to Tests to Merge to Deploy to Monitor to Patch"
1271 >
1272 <defs>
1273 <linearGradient id="land2030LoopLine" x1="0" y1="0" x2="1" y2="0">
ba23fe2Claude1274 <stop offset="0%" stop-color="#5b6ee8" stop-opacity="0.0" />
1275 <stop offset="20%" stop-color="#5b6ee8" stop-opacity="0.5" />
1276 <stop offset="80%" stop-color="#5b6ee8" stop-opacity="0.5" />
1277 <stop offset="100%" stop-color="#5b6ee8" stop-opacity="0.0" />
0c3eee5Claude1278 </linearGradient>
1279 <radialGradient id="land2030LoopNode" cx="0.5" cy="0.5" r="0.5">
ba23fe2Claude1280 <stop offset="0%" stop-color="#5b6ee8" stop-opacity="0.95" />
1281 <stop offset="100%" stop-color="#5b6ee8" stop-opacity="0.60" />
0c3eee5Claude1282 </radialGradient>
1283 </defs>
1284 {/* connecting curve */}
1285 <path
1286 d="M 60 140 C 220 40, 880 40, 1040 140 C 880 240, 220 240, 60 140 Z"
1287 fill="none"
1288 stroke="url(#land2030LoopLine)"
1289 stroke-width="2"
1290 class="land-2030-loop-path"
1291 />
1292 {[
1293 { x: 70, y: 140, label: "Spec" },
1294 { x: 215, y: 70, label: "Code" },
1295 { x: 410, y: 50, label: "AI Review" },
1296 { x: 605, y: 50, label: "Tests" },
1297 { x: 800, y: 70, label: "Merge" },
1298 { x: 950, y: 140, label: "Deploy" },
1299 { x: 800, y: 210, label: "Monitor" },
1300 { x: 410, y: 230, label: "Patch" },
1301 ].map((n) => (
1302 <g class="land-2030-loop-node">
1303 <circle cx={n.x} cy={n.y} r="22" fill="url(#land2030LoopNode)" />
1304 <circle
1305 cx={n.x}
1306 cy={n.y}
1307 r="22"
1308 fill="none"
1309 stroke="rgba(255,255,255,0.18)"
1310 stroke-width="1"
1311 />
1312 <text
1313 x={n.x}
1314 y={n.y + (n.y > 140 ? 48 : -32)}
1315 text-anchor="middle"
1316 class="land-2030-loop-text"
1317 fill="currentColor"
1318 >
1319 {n.label}
1320 </text>
1321 </g>
1322 ))}
1323 </svg>
1324);
1325
1326interface Land2030CardProps {
1327 icon: any;
1328 title: string;
1329 desc: string;
1330 href: string;
1331 cta?: string;
1332}
1333const Land2030Card: FC<Land2030CardProps> = ({ icon, title, desc, href, cta }) => (
1334 <a href={href} class="land-2030-card">
1335 <div class="land-2030-card-icon" aria-hidden="true">
1336 {icon}
1337 </div>
1338 <h3 class="land-2030-card-title">{title}</h3>
1339 <p class="land-2030-card-desc">{desc}</p>
1340 <span class="land-2030-card-cta">
1341 {cta ?? "Try it"}
1342 <span aria-hidden="true">{" →"}</span>
1343 </span>
1344 </a>
1345);
1346
1347interface Land2030FeatureItem {
1348 title: string;
1349 desc: string;
1350 href: string;
1351 status: "live" | "beta" | "soon";
1352}
1353const LAND_2030_FEATURES: Land2030FeatureItem[] = [
1354 { title: "Spec-to-PR", desc: "Write English. Ship code.", href: "/specs", status: "live" },
1355 { title: "Voice-to-PR", desc: "Talk. Ship code.", href: "/voice-to-pr", status: "live" },
1356 { title: "Repo chat", desc: "Rubber-duck with a semantic index of your repo.", href: "/ask", status: "live" },
1357 { title: "AI CI healer", desc: "Broken CI? Claude opens a fix PR.", href: "/inbox", status: "live" },
1358 { title: "AI patch generator", desc: "Security finding → patch PR, signed by Claude.", href: "/code-scanning", status: "live" },
1359 { title: "AI proactive monitor", desc: "Claude opens issues unprompted when it spots smells.", href: "/standups", status: "live" },
1360 { title: "AI commit messages", desc: "`gluecron commit` writes a great message for the staged diff.", href: "/help", status: "live" },
1361 { title: "AI release notes", desc: "Claude reads merged PRs and writes the changelog.", href: "/ai-changelog", status: "live" },
1362 { title: "Multi-repo refactor", desc: "One English request → coordinated PRs across N repos.", href: "/refactors", status: "live" },
1363 { title: "Migration assistant", desc: "Major-version upgrades drafted PR-by-PR.", href: "/migration-assistant", status: "live" },
1364 { title: "AI test generator", desc: "Every PR auto-gets tests for the new diff.", href: "/ai-tests", status: "beta" },
1365 { title: "PR slash commands", desc: "/merge, /rebase, /explain, /test — Claude runs them.", href: "/help", status: "live" },
1366 { title: "Live co-editing on PRs", desc: "Figma-style cursors on PR descriptions and reviews.", href: "/pulls", status: "beta" },
1367 { title: "Branch preview URLs", desc: "Every push → a sharable preview URL.", href: "/previews", status: "live" },
1368 { title: "Agent multiplayer", desc: "Per-agent sessions, budgets, and branch namespacing.", href: "/settings/agents", status: "live" },
1369 { title: "Continuous semantic index", desc: "Push-time embeddings keep search and chat fresh.", href: "/semantic-search", status: "live" },
1370 { title: "VS Code extension", desc: "Inbox, PRs, and repo chat — in your editor.", href: "/help", status: "soon" },
1371 { title: "Slack / Discord bot", desc: "Mentions, reviews, deploys — in your team chat.", href: "/help", status: "soon" },
1372];
1373
1374const Land2030: FC = () => (
1375 <>
1376 <style dangerouslySetInnerHTML={{ __html: land2030Css }} />
1377 <div class="land-2030-root">
1378 {/* ---------- 2030 HERO ---------- */}
1379 <section class="land-2030-hero" aria-label="Gluecron 2030">
1380 <div class="land-2030-hairline" aria-hidden="true" />
1381 <div class="land-2030-orb" aria-hidden="true" />
1382 <div class="land-2030-hero-inner">
1383 <div class="land-2030-eyebrow">
1384 <span class="land-2030-pulse" aria-hidden="true" />
64aa989Claude1385 Gluecron — built for AI-assisted teams
0c3eee5Claude1386 </div>
1387 <h1 class="land-2030-display">
1388 <span class="land-2030-grad-1">Write</span>{" "}
1389 <span class="land-2030-grad-2">English.</span>{" "}
1390 <span class="land-2030-grad-3">Ship</span>{" "}
1391 <span class="land-2030-grad-4">code.</span>{" "}
1392 <span class="land-2030-grad-5">Gluecron.</span>
1393 </h1>
1394 <p class="land-2030-sub">
1395 The git platform built for the era when AI ships most of the code.
1396 </p>
1397 <div class="land-2030-cta-row">
1398 <a href="/register" class="btn btn-primary btn-xl land-2030-cta-primary">
1399 Start free
1400 <span aria-hidden="true">{" →"}</span>
1401 </a>
1402 <a href="#land-2030-loop" class="btn btn-xl land-2030-cta-secondary">
1403 Watch the loop
1404 <span aria-hidden="true">{" ↓"}</span>
1405 </a>
1406 </div>
1407 </div>
1408 </section>
1409
1410 {/* ---------- THE CLOSED LOOP ---------- */}
1411 <section id="land-2030-loop" class="land-2030-section">
1412 <div class="land-2030-section-head">
1413 <div class="land-2030-eyebrow land-2030-eyebrow-mini">The closed loop</div>
1414 <h2 class="land-2030-h2">One platform. No glue code.</h2>
1415 <p class="land-2030-lede">
1416 Spec → Code → AI Review → Tests → Merge → Deploy → Monitor → Patch.
1417 All on Gluecron. No GitHub + Copilot + Vercel + Sentry stitching
1418 needed.
1419 </p>
1420 </div>
1421 <div class="land-2030-loop-wrap">
1422 <ClosedLoopDiagram />
1423 </div>
1424 </section>
1425
1426 {/* ---------- THE 6 THINGS NOBODY ELSE CAN DO ---------- */}
1427 <section class="land-2030-section">
1428 <div class="land-2030-section-head">
1429 <div class="land-2030-eyebrow land-2030-eyebrow-mini">Unfair advantages</div>
1430 <h2 class="land-2030-h2">What Gluecron does that nobody else can.</h2>
1431 </div>
1432 <div class="land-2030-card-grid">
1433 <Land2030Card
1434 href="/voice-to-pr"
1435 title="Voice-to-PR"
1436 desc="Speak the change you want. Claude opens the PR. Works on your commute."
1437 icon={
1438 <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>
1439 }
1440 />
1441 <Land2030Card
1442 href="/specs"
1443 title="Spec-to-PR"
1444 desc="Write the spec in plain English. Claude implements it, opens a PR, and asks for review."
1445 icon={
1446 <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>
1447 }
1448 />
1449 <Land2030Card
1450 href="/workflows"
1451 title="AI CI self-healing"
1452 desc="Tests go red? Claude reads the log, finds the cause, and pushes the fix to your PR branch."
1453 icon={
1454 <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>
1455 }
1456 />
1457 <Land2030Card
1458 href="/refactors"
1459 title="Multi-repo refactor agent"
1460 desc="One English request → coordinated PRs across every repo that uses the symbol."
1461 icon={
1462 <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>
1463 }
1464 />
1465 <Land2030Card
1466 href="/ask"
1467 title="Repo chat with semantic search"
1468 desc="Continuous push-time embeddings. Ask the repo anything; it cites real files and commits."
1469 icon={
1470 <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>
1471 }
1472 />
1473 <Land2030Card
1474 href="/pulls"
1475 title="Per-PR live co-editing"
1476 desc="Figma-style cursors and presence on PR descriptions and reviews. Goodbye stale tabs."
1477 icon={
1478 <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>
1479 }
1480 />
1481 </div>
1482 </section>
1483
1484 {/* ---------- GLOBAL DASHBOARDS ---------- */}
1485 <section class="land-2030-section">
1486 <div class="land-2030-section-head">
1487 <div class="land-2030-eyebrow land-2030-eyebrow-mini">Mission control</div>
1488 <h2 class="land-2030-h2">Every signal, one inbox.</h2>
1489 <p class="land-2030-lede">
1490 Five global dashboards that span every repo you touch — no more
1491 tab-juggling across orgs.
1492 </p>
1493 </div>
1494 <div class="land-2030-dash-grid">
1495 <a href="/pulls" class="land-2030-dash">
1496 <span class="land-2030-dash-name">/pulls</span>
1497 <span class="land-2030-dash-desc">PR command center across every repo you can touch.</span>
1498 </a>
1499 <a href="/issues" class="land-2030-dash">
1500 <span class="land-2030-dash-name">/issues</span>
1501 <span class="land-2030-dash-desc">Global issue dashboard. Triage from one screen.</span>
1502 </a>
1503 <a href="/inbox" class="land-2030-dash">
1504 <span class="land-2030-dash-name">/inbox</span>
1505 <span class="land-2030-dash-desc">Unified mentions, reviews, CI, and AI events.</span>
1506 </a>
1507 <a href="/activity" class="land-2030-dash">
1508 <span class="land-2030-dash-name">/activity</span>
1509 <span class="land-2030-dash-desc">A timeline of everything that moved on your repos.</span>
1510 </a>
1511 <a href="/standups" class="land-2030-dash">
1512 <span class="land-2030-dash-name">/standups</span>
1513 <span class="land-2030-dash-desc">Daily AI-generated brief of what shipped and what's stuck.</span>
1514 </a>
1515 </div>
1516 </section>
1517
1518 {/* ---------- FULL AI FEATURE GRID ---------- */}
1519 <section class="land-2030-section">
1520 <div class="land-2030-section-head">
1521 <div class="land-2030-eyebrow land-2030-eyebrow-mini">Closed-loop AI</div>
1522 <h2 class="land-2030-h2">18 AI features. One platform.</h2>
1523 <p class="land-2030-lede">
1524 Most of these don't exist anywhere else. None of them require a
1525 second SaaS subscription.
1526 </p>
1527 </div>
1528 <div class="land-2030-feat-grid">
1529 {LAND_2030_FEATURES.map((f) => (
1530 <a href={f.href} class="land-2030-feat">
1531 <div class="land-2030-feat-head">
1532 <span class="land-2030-feat-title">{f.title}</span>
1533 <span class={`land-2030-pill land-2030-pill-${f.status}`}>
1534 {f.status === "live" ? "live" : f.status === "beta" ? "beta" : "soon"}
1535 </span>
1536 </div>
1537 <p class="land-2030-feat-desc">{f.desc}</p>
1538 </a>
1539 ))}
1540 </div>
1541 </section>
1542
1543 {/* ---------- DEVELOPER EXPERIENCE ---------- */}
1544 <section class="land-2030-section">
1545 <div class="land-2030-section-head">
1546 <div class="land-2030-eyebrow land-2030-eyebrow-mini">Developer surface</div>
1547 <h2 class="land-2030-h2">Built where you already are.</h2>
1548 </div>
1549 <div class="land-2030-dx-grid">
1550 <div class="land-2030-dx">
1551 <h3 class="land-2030-dx-title">gluecron CLI</h3>
1552 <pre class="land-2030-code"><code>$ gluecron spec "add CSV export to /api/orders"
1553{"→ Drafting PR…"}
1554{"→ Opened #482 with 3 commits"}
1555{"→ AI review queued"}</code></pre>
1556 </div>
1557 <div class="land-2030-dx">
1558 <h3 class="land-2030-dx-title">PR slash commands</h3>
1559 <pre class="land-2030-code"><code>/merge — squash + merge when checks pass
1560/rebase — rebase onto base, push --force-with-lease
1561/explain — Claude explains the diff in plain English
1562/test — Claude writes tests for the new code</code></pre>
1563 </div>
1564 <div class="land-2030-dx">
1565 <h3 class="land-2030-dx-title">Branch preview URLs</h3>
1566 <pre class="land-2030-code"><code>{"→ git push gluecron HEAD"}
1567{"→ preview: https://pr-482.preview.gluecron.com"}
1568{"→ commented on PR #482"}</code></pre>
1569 </div>
1570 </div>
1571 </section>
1572
1573 {/* ---------- BUILT FOR AGENTS ---------- */}
1574 <section class="land-2030-section land-2030-section-dark">
1575 <div class="land-2030-section-head">
1576 <div class="land-2030-eyebrow land-2030-eyebrow-mini">Agent era</div>
1577 <h2 class="land-2030-h2">Built for agents, not just humans.</h2>
1578 <p class="land-2030-lede">
1579 Per-agent tokens, per-agent budgets, per-agent branch namespaces,
1580 and a lease primitive so 50 agents don't trample one repo.
1581 </p>
1582 </div>
1583 <div class="land-2030-agent-wrap">
1584 <pre class="land-2030-code land-2030-code-wide"><code>{'# agent gets a scoped token + lease before writing'}
1585{'curl -H "Authorization: Bearer agt_3p9x…" \\\\'}
1586{' -X POST https://gluecron.com/api/v2/leases \\\\'}
1587{' -d \'{"repo":"acme/api","branch":"agent/jules/checkout-fix","ttl":300}\''}
1588{''}
1589{'{ "lease_id": "lse_8a2f", "expires_at": "2030-05-25T14:05:11Z" }'}</code></pre>
1590 <div class="land-2030-agent-stat">
1591 <div class="land-2030-agent-big">10,000</div>
1592 <div class="land-2030-agent-label">
1593 agents pushing to your repo per day. Welcome to 2030.
1594 </div>
1595 <a href="/docs/build-agent-integration" class="land-2030-agent-link">
1596 Build an agent integration{" →"}
1597 </a>
1598 </div>
1599 </div>
1600 </section>
1601
1602 {/* ---------- VS GITHUB ---------- */}
1603 <section class="land-2030-section">
1604 <div class="land-2030-section-head">
1605 <div class="land-2030-eyebrow land-2030-eyebrow-mini">vs GitHub</div>
1606 <h2 class="land-2030-h2">One platform replaces five.</h2>
1607 </div>
1608 <div class="land-2030-vs">
1609 <div class="land-2030-vs-row land-2030-vs-head">
1610 <div>Today</div>
1611 <div>On Gluecron</div>
1612 </div>
1613 <div class="land-2030-vs-row">
1614 <div>GitHub + Copilot + Vercel + Sentry + Linear</div>
1615 <div class="land-2030-vs-us">Gluecron</div>
1616 </div>
1617 <a href="/vs-github" class="land-2030-vs-link">
1618 See the full comparison{" →"}
1619 </a>
1620 </div>
1621 </section>
1622 </div>
1623 </>
1624);
1625
4c47454Claude1626// Backwards-compatible default — web.tsx imports `LandingPage`.
a819f96Claude1627// Single landing surface. The bolted-on <Land2030 /> prelude was rendering a
1628// SECOND full page above LandingHero — two stacked <h1> documents as you
1629// scrolled. Render exactly one page; the 2030 reboot replaces this wholesale.
4c47454Claude1630export const LandingPage: FC<LandingPageProps> = (props) => (
a819f96Claude1631 <LandingHero {...props} />
2b821b7Claude1632);
1633
4c47454Claude1634export default LandingPage;
1635
2b821b7Claude1636const landingCss = `
958d26aClaude1637 /* ============================================================ */
1638 /* Landing — Editorial-Technical 2026.05 */
1639 /* ============================================================ */
4c47454Claude1640 .landing-root {
1641 position: relative;
958d26aClaude1642 max-width: 1180px;
4c47454Claude1643 margin: 0 auto;
1644 padding: 0 16px;
958d26aClaude1645 }
1646 .landing-root > section { position: relative; }
1647
1648 /* ---------- Hero ---------- */
1649 .landing-hero {
1650 position: relative;
c475ee6Claude1651 padding: var(--s-16) 0 var(--s-20);
958d26aClaude1652 text-align: center;
4c47454Claude1653 overflow: hidden;
1654 }
c475ee6Claude1655 .landing-hero-blob-1 {
1656 }
1657 .landing-hero-blob-2 {
1658 }
1659
1660 /* ---------- Hero product visual: live AI PR review card ---------- */
1661 .landing-hero-visual {
1662 position: relative;
1663 max-width: 760px;
1664 margin: var(--s-12) auto 0;
1665 padding: 0 16px;
1666 perspective: 1400px;
1667 z-index: 2;
1668 opacity: 0;
1669 animation: hero-visual-in 700ms var(--ease-out-expo, cubic-bezier(0.19, 1, 0.22, 1)) 400ms forwards;
1670 }
1671 @keyframes hero-visual-in {
1672 from { opacity: 0; transform: translateY(20px); }
1673 to { opacity: 1; transform: translateY(0); }
1674 }
1675 .hero-pr-card {
1676 position: relative;
ba23fe2Claude1677 background: var(--bg-elevated);
c475ee6Claude1678 border: 1px solid var(--border-strong);
1679 border-radius: var(--r-xl);
1680 overflow: hidden;
1681 text-align: left;
ba23fe2Claude1682 box-shadow: 0 24px 64px -16px rgba(0,0,0,0.45), 0 0 0 1px var(--border-strong);
c475ee6Claude1683 transform: rotateX(2deg) rotateY(-2deg);
1684 transition: transform 600ms var(--ease, ease);
1685 }
1686 .landing-hero-visual:hover .hero-pr-card {
1687 transform: rotateX(0deg) rotateY(0deg);
1688 }
1689 .hero-pr-header {
1690 display: flex;
1691 align-items: center;
1692 gap: 12px;
1693 padding: 14px 18px;
ba23fe2Claude1694 border-bottom: 1px solid var(--border);
1695 background: var(--bg-secondary);
c475ee6Claude1696 font-size: 13px;
1697 }
1698 .hero-pr-dot {
1699 width: 10px; height: 10px;
1700 border-radius: 50%;
1701 background: var(--green);
1702 flex-shrink: 0;
1703 }
1704 .hero-pr-title {
1705 color: var(--text-strong);
1706 font-weight: 600;
1707 flex: 1;
1708 overflow: hidden;
1709 text-overflow: ellipsis;
1710 white-space: nowrap;
1711 }
1712 .hero-pr-num {
1713 color: var(--text-faint);
1714 font-family: var(--font-mono);
1715 font-weight: 500;
1716 margin-right: 8px;
1717 }
1718 .hero-pr-status {
1719 display: inline-flex;
1720 align-items: center;
1721 gap: 6px;
1722 padding: 3px 10px;
1723 border-radius: var(--r-full);
1724 background: var(--accent-gradient-faint);
ba23fe2Claude1725 border: 1px solid var(--border);
c475ee6Claude1726 color: var(--accent);
1727 font-family: var(--font-mono);
1728 font-size: 11px;
1729 letter-spacing: 0.04em;
1730 flex-shrink: 0;
1731 }
1732 .hero-pr-status-pulse {
1733 width: 6px; height: 6px;
1734 border-radius: 50%;
1735 background: var(--accent);
ba23fe2Claude1736 animation: heroPulse 2s ease-in-out infinite;
c475ee6Claude1737 }
1738
1739 .hero-pr-body {
1740 padding: 0;
1741 }
1742 .hero-pr-file {
1743 display: flex;
1744 align-items: center;
1745 gap: 10px;
1746 padding: 10px 18px;
1747 border-bottom: 1px solid rgba(255,255,255,0.05);
1748 font-family: var(--font-mono);
1749 font-size: 12px;
1750 background: rgba(255,255,255,0.012);
1751 }
1752 .hero-pr-file-icon { color: var(--accent-2); }
1753 .hero-pr-file-name { color: var(--text); flex: 1; }
1754 .hero-pr-file-stats { display: inline-flex; gap: 8px; }
1755 .hero-pr-add { color: var(--green); font-weight: 600; }
1756 .hero-pr-del { color: var(--red); font-weight: 600; }
1757
1758 .hero-pr-diff {
1759 padding: 12px 18px;
1760 font-family: var(--font-mono);
1761 font-feature-settings: var(--mono-feat, 'calt');
1762 font-size: 12.5px;
1763 line-height: 1.7;
1764 color: rgba(237,237,242,0.85);
1765 overflow-x: auto;
1766 }
1767 .hero-pr-hunk {
ba23fe2Claude1768 color: var(--accent);
1769 background: var(--accent-gradient-faint);
c475ee6Claude1770 padding: 2px 8px;
1771 margin: 0 -8px 4px;
1772 border-radius: 4px;
1773 }
1774 .hero-pr-line-add {
1775 background: rgba(52,211,153,0.08);
1776 color: rgba(167,243,208,0.95);
1777 padding: 0 8px;
1778 margin: 0 -8px;
1779 border-left: 2px solid var(--green);
1780 padding-left: 8px;
1781 }
1782
1783 .hero-pr-comment {
1784 margin: 14px 18px;
1785 padding: 14px 16px;
ba23fe2Claude1786 background: var(--bg-secondary);
1787 border: 1px solid var(--border);
c475ee6Claude1788 border-radius: var(--r-md);
1789 }
1790 .hero-pr-bot-row {
1791 display: flex;
1792 align-items: center;
1793 gap: 8px;
1794 margin-bottom: 8px;
1795 font-size: 12px;
1796 }
1797 .hero-pr-bot-avatar {
1798 width: 22px; height: 22px;
1799 display: inline-flex;
1800 align-items: center;
1801 justify-content: center;
1802 border-radius: 50%;
1803 background: var(--accent-gradient);
1804 font-size: 11px;
1805 }
1806 .hero-pr-bot-name {
1807 color: var(--text-strong);
1808 font-weight: 600;
1809 }
1810 .hero-pr-bot-meta {
1811 color: var(--text-faint);
1812 font-family: var(--font-mono);
1813 }
1814 .hero-pr-bot-text {
1815 color: var(--text);
1816 font-size: 13px;
1817 line-height: 1.55;
1818 margin: 0;
1819 }
1820 .hero-pr-bot-text code {
1821 background: rgba(255,255,255,0.06);
1822 border: 1px solid rgba(255,255,255,0.10);
1823 padding: 1px 6px;
1824 border-radius: 4px;
1825 font-size: 11.5px;
1826 color: var(--accent);
1827 }
1828 .hero-pr-bot-link { color: var(--accent-2); text-decoration: underline; text-decoration-style: dotted; }
1829
1830 .hero-pr-gates {
1831 display: flex;
1832 flex-wrap: wrap;
1833 gap: 6px;
1834 padding: 12px 18px 16px;
1835 border-top: 1px solid rgba(255,255,255,0.06);
1836 background: rgba(255,255,255,0.012);
1837 }
1838 .hero-pr-gate {
1839 display: inline-flex;
1840 align-items: center;
1841 gap: 6px;
1842 padding: 4px 10px;
1843 border-radius: var(--r-full);
1844 font-family: var(--font-mono);
1845 font-size: 11px;
1846 border: 1px solid;
1847 }
1848 .hero-pr-gate-pass {
1849 color: var(--green);
1850 background: rgba(52,211,153,0.08);
1851 border-color: rgba(52,211,153,0.30);
1852 }
1853 .hero-pr-gate-running {
1854 color: var(--accent);
1855 background: var(--accent-gradient-faint);
ba23fe2Claude1856 border-color: var(--border-strong);
c475ee6Claude1857 }
1858 .hero-pr-gate-spin {
1859 width: 9px; height: 9px;
ba23fe2Claude1860 border: 1.5px solid var(--border);
c475ee6Claude1861 border-top-color: var(--accent);
1862 border-radius: 50%;
1863 }
1864
1865 /* Floating accent badges around the card */
1866 .hero-float {
1867 position: absolute;
1868 display: inline-flex;
1869 align-items: center;
1870 gap: 6px;
1871 padding: 6px 12px;
ba23fe2Claude1872 background: var(--bg-elevated);
1873 border: 1px solid var(--border-strong);
c475ee6Claude1874 border-radius: var(--r-full);
1875 font-family: var(--font-mono);
1876 font-size: 11px;
1877 color: var(--text);
ba23fe2Claude1878 box-shadow: var(--elev-2);
c475ee6Claude1879 backdrop-filter: blur(8px);
1880 -webkit-backdrop-filter: blur(8px);
1881 }
1882 .hero-float-icon { color: var(--accent); }
1883 .hero-float-1 {
1884 top: -14px;
1885 left: -8px;
1886 }
1887 .hero-float-2 {
1888 bottom: -14px;
1889 right: -8px;
1890 }
1891
1892 @media (max-width: 720px) {
1893 .landing-hero-visual { padding: 0 8px; }
1894 .hero-pr-card { transform: none; }
1895 .hero-pr-title { font-size: 12px; }
1896 .hero-pr-diff { font-size: 11px; line-height: 1.6; }
1897 .hero-float { display: none; }
1898 }
ba23fe2Claude1899 /* Hero background — single restrained radial glow, no blobs or grids.
1900 Professional tools (GitHub, Linear, Stripe) use clean backgrounds. */
958d26aClaude1901 .landing-hero-bg {
1902 position: absolute;
ba23fe2Claude1903 inset: 0;
958d26aClaude1904 pointer-events: none;
1905 z-index: 0;
4c47454Claude1906 }
ba23fe2Claude1907 .landing-hero-blob { display: none; }
1908 .landing-hero-blob-1 { display: none; }
1909 .landing-hero-blob-2 { display: none; }
1910 .landing-hero-grid { display: none; }
4c47454Claude1911
958d26aClaude1912 .landing-hero-inner {
1913 position: relative;
1914 z-index: 1;
1915 max-width: 960px;
2b821b7Claude1916 margin: 0 auto;
1917 }
dc26881CC LABS App1918 /* U1 — every block below the headline obeys a single rhythm. */
958d26aClaude1919 .landing-hero-eyebrow {
dc26881CC LABS App1920 margin: 0 auto var(--space-6);
958d26aClaude1921 color: var(--accent);
1922 }
1923 .landing-hero-eyebrow::before { display: none; }
1924 .landing-hero-pulse {
1925 width: 7px;
1926 height: 7px;
1927 border-radius: 50%;
ba23fe2Claude1928 background: var(--green);
958d26aClaude1929 flex-shrink: 0;
8cfb00eClaude1930 opacity: 0.8;
958d26aClaude1931 }
1932
2b821b7Claude1933 .landing-hero-title {
3a5755eClaude1934 /* 2026 polish — bigger, bolder, tighter. Inter Tight at 800 weight
1935 with -0.03em tracking is the modern "AI-startup hero" look that
1936 Vercel/Linear/Cursor all use. clamp() scales gracefully on mobile. */
1937 font-size: clamp(40px, 7vw, 84px);
1938 line-height: 1.02;
1939 letter-spacing: -0.032em;
1940 font-weight: 800;
1941 font-family: var(--font-display);
dc26881CC LABS App1942 margin: 0 0 var(--space-6);
958d26aClaude1943 color: var(--text-strong);
2b821b7Claude1944 }
c963db5Claude1945 .landing-hero-title .gradient-text {
8cfb00eClaude1946 color: var(--text-strong);
c963db5Claude1947 }
958d26aClaude1948
2b821b7Claude1949 .landing-hero-sub {
3a5755eClaude1950 font-size: clamp(16px, 1.8vw, 22px);
2b821b7Claude1951 color: var(--text-muted);
958d26aClaude1952 max-width: 680px;
dc26881CC LABS App1953 margin: 0 auto var(--space-6);
3a5755eClaude1954 line-height: 1.5;
1955 letter-spacing: -0.008em;
1956 font-weight: 400;
2b821b7Claude1957 }
4c47454Claude1958
2b821b7Claude1959 .landing-hero-ctas {
1960 display: flex;
dc26881CC LABS App1961 gap: var(--space-3);
2b821b7Claude1962 justify-content: center;
1963 flex-wrap: wrap;
dc26881CC LABS App1964 margin-top: 0;
1965 margin-bottom: var(--space-4);
2b821b7Claude1966 }
958d26aClaude1967 .landing-cta-arrow {
1968 transition: transform var(--t-base) var(--ease-spring);
1969 display: inline-block;
2b821b7Claude1970 }
cd4f63bTest User1971
dc26881CC LABS App1972 /* U1 — tertiary text-link row.
1973 Sits directly under the 2-button primary CTA row. Smaller, muted,
1974 so it reads as "by the way" rather than competing for the eye. */
1975 .landing-hero-tertiary {
1976 margin-top: 0;
1977 margin-bottom: var(--space-6);
cd4f63bTest User1978 text-align: center;
dc26881CC LABS App1979 display: inline-flex;
1980 flex-wrap: wrap;
1981 gap: var(--space-2) var(--space-3);
1982 justify-content: center;
1983 align-items: baseline;
1984 width: 100%;
cd4f63bTest User1985 font-size: 13px;
1986 color: var(--text-muted);
dc26881CC LABS App1987 }
1988 .landing-hero-tertiary-link {
1989 color: var(--text-muted);
cd4f63bTest User1990 text-decoration: none;
1991 border-bottom: 1px dashed transparent;
1992 padding-bottom: 1px;
dc26881CC LABS App1993 transition: color var(--t-fast) var(--ease),
1994 border-color var(--t-fast) var(--ease);
cd4f63bTest User1995 }
dc26881CC LABS App1996 .landing-hero-tertiary-link:hover {
1997 color: var(--text-strong);
cd4f63bTest User1998 border-bottom-color: var(--text-muted);
1999 }
dc26881CC LABS App2000 .landing-hero-tertiary-sep {
2001 color: var(--text-faint);
2002 user-select: none;
2003 }
958d26aClaude2004 .btn:hover .landing-cta-arrow,
2005 .landing-cta-primary:hover .landing-cta-arrow {
2006 transform: translateX(4px);
8e9f1d9Claude2007 }
2b821b7Claude2008
93fe97eClaude2009 /* BLOCK Q1 — flagship "Add to Claude Desktop" CTA.
2010 Gradient-bordered + accent text so it reads as a peer of the primary
bc7654bCC LABS App2011 Sign-up CTA, not a third secondary. Theme-aware: inner fill uses
2012 --bg-elevated so it's white on light and dark on dark, never the
2013 jarring near-black on white we shipped first time. Subtle elevation
2014 on hover; static when the visitor opts out of motion. */
93fe97eClaude2015 .landing-cta-dxt {
2016 position: relative;
bc7654bCC LABS App2017 background: var(--bg-elevated);
2018 color: var(--text-strong);
93fe97eClaude2019 border: 1px solid transparent;
2020 background-image:
bc7654bCC LABS App2021 linear-gradient(var(--bg-elevated), var(--bg-elevated)),
ba23fe2Claude2022 linear-gradient(var(--border-strong), var(--border-strong));
93fe97eClaude2023 background-origin: border-box;
2024 background-clip: padding-box, border-box;
2025 transition: transform var(--t-base, 180ms) var(--ease-spring, ease),
2026 box-shadow var(--t-base, 180ms) var(--ease-spring, ease);
2027 }
2028 .landing-cta-dxt:hover {
2029 transform: translateY(-2px);
ba23fe2Claude2030 box-shadow: var(--elev-2);
93fe97eClaude2031 }
2032 @media (prefers-reduced-motion: reduce) {
2033 .landing-cta-dxt,
2034 .landing-cta-dxt:hover {
2035 transform: none;
2036 transition: none;
2037 }
2038 }
2039
adf5e18Claude2040 /* "Migrate from GitHub" CTA — secondary, but strong enough to stand
2041 alongside the primary. Uses a subtle amber/violet mix so it reads as
2042 action-oriented without competing with the green primary CTA. */
2043 .landing-cta-migrate {
2044 position: relative;
2045 background: var(--bg-elevated);
2046 color: var(--text-strong);
2047 border: 1px solid var(--border-strong);
2048 transition: border-color var(--t-base, 180ms) var(--ease, ease),
2049 transform var(--t-base, 180ms) var(--ease-spring, ease),
2050 box-shadow var(--t-base, 180ms) var(--ease, ease);
2051 }
2052 .landing-cta-migrate:hover {
ba23fe2Claude2053 border-color: var(--border-strong);
adf5e18Claude2054 transform: translateY(-2px);
ba23fe2Claude2055 box-shadow: var(--elev-2);
adf5e18Claude2056 text-decoration: none;
2057 color: var(--text-strong);
2058 }
2059 @media (prefers-reduced-motion: reduce) {
2060 .landing-cta-migrate,
2061 .landing-cta-migrate:hover {
2062 transform: none;
2063 transition: none;
2064 }
2065 }
2066
dc26881CC LABS App2067 /* L8 — free-tier reassurance link beneath the CTA row.
2068 U1 — rhythm snapped to var(--space-6). */
5f2e749Claude2069 .landing-hero-freenote {
dc26881CC LABS App2070 margin: 0 auto var(--space-6);
5f2e749Claude2071 font-size: var(--t-sm);
2072 color: var(--text-muted);
2073 text-align: center;
2074 }
2075 .landing-hero-freenote-link {
2076 color: var(--accent);
2077 text-decoration: none;
2078 font-weight: 500;
ba23fe2Claude2079 border-bottom: 1px dotted var(--border);
5f2e749Claude2080 transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
2081 }
2082 .landing-hero-freenote-link:hover {
2083 color: var(--text-strong);
2084 border-bottom-color: var(--accent);
2085 }
2086
4c47454Claude2087 .landing-hero-caption {
dc26881CC LABS App2088 margin: 0 auto var(--space-6);
958d26aClaude2089 font-size: var(--t-sm);
4c47454Claude2090 color: var(--text-muted);
958d26aClaude2091 display: flex;
2092 flex-wrap: wrap;
2093 align-items: center;
2094 justify-content: center;
dc26881CC LABS App2095 gap: var(--space-3);
2b821b7Claude2096 }
958d26aClaude2097 .landing-hero-cmd {
2098 display: inline-flex;
2099 align-items: center;
2100 gap: 4px;
2101 padding: 6px 12px;
2102 background: var(--bg-elevated);
4c47454Claude2103 border: 1px solid var(--border);
958d26aClaude2104 border-radius: var(--r-full);
2105 box-shadow: var(--elev-1);
2106 }
2107 .landing-hero-cmd .kbd {
2108 border: 0;
2109 background: transparent;
2110 padding: 0 4px;
2111 color: var(--text-muted);
2112 font-size: 12px;
2113 }
2114 .landing-hero-cmd .kbd:nth-last-of-type(1) { color: var(--accent); }
2115 .landing-hero-arrow {
2116 color: var(--text-faint);
2117 font-size: 13px;
2118 margin: 0 2px;
2b821b7Claude2119 }
4c47454Claude2120
2121 .landing-stats {
dc26881CC LABS App2122 margin: 0 auto;
958d26aClaude2123 font-family: var(--font-mono);
2124 font-size: 12px;
2125 color: var(--text-muted);
2126 display: flex;
2127 align-items: center;
2128 justify-content: center;
dc26881CC LABS App2129 gap: var(--space-2);
958d26aClaude2130 flex-wrap: wrap;
2131 letter-spacing: 0.02em;
2132 }
2133 .landing-stats strong {
2134 color: var(--text-strong);
2135 font-weight: 600;
2136 font-feature-settings: 'tnum';
2137 }
2138 .landing-stats-sep { opacity: 0.4; }
2139
9ecf5a4Claude2140 /* ---------- Capability grid (vapron-style uppercase tracked) ---------- */
c963db5Claude2141 .landing-caps {
2142 margin: var(--s-12) auto var(--s-16);
2143 max-width: 1080px;
2144 padding: var(--s-7) var(--s-4);
958d26aClaude2145 border-top: 1px solid var(--border-subtle);
2146 border-bottom: 1px solid var(--border-subtle);
c963db5Claude2147 }
2148 .landing-caps-grid {
2149 display: grid;
2150 grid-template-columns: repeat(4, 1fr);
2151 gap: 24px 16px;
958d26aClaude2152 text-align: center;
2153 }
c963db5Claude2154 .landing-cap {
958d26aClaude2155 font-family: var(--font-mono);
2156 font-size: 11px;
c963db5Claude2157 font-weight: 600;
958d26aClaude2158 text-transform: uppercase;
c963db5Claude2159 letter-spacing: 0.16em;
2160 color: var(--text-muted);
2161 transition: color var(--t-fast) var(--ease);
958d26aClaude2162 }
c963db5Claude2163 .landing-cap:hover { color: var(--text-strong); }
2164 @media (max-width: 800px) {
2165 .landing-caps-grid { grid-template-columns: repeat(2, 1fr); gap: 18px 12px; }
2166 }
2167 @media (max-width: 480px) {
2168 .landing-caps-grid { grid-template-columns: 1fr; }
2169 }
2170
9ecf5a4Claude2171 /* ---------- Big stat row (vapron-style hero closer) ---------- */
c963db5Claude2172 .landing-bigstats {
2173 margin: var(--s-10) auto var(--s-20);
2174 max-width: 1180px;
2175 padding: 0 var(--s-4);
2176 }
2177 .landing-bigstats-grid {
2178 display: grid;
2179 grid-template-columns: repeat(4, 1fr);
2180 gap: 32px;
2181 text-align: left;
958d26aClaude2182 }
c963db5Claude2183 .landing-bigstat {
2184 padding: var(--s-2) 0;
2185 }
2186 .landing-bigstat-num {
958d26aClaude2187 font-family: var(--font-display);
c963db5Claude2188 font-size: clamp(28px, 3.5vw, 44px);
2189 line-height: 1.05;
2190 letter-spacing: -0.03em;
2191 font-weight: 700;
2192 color: var(--text-strong);
2193 margin-bottom: var(--s-2);
2194 }
2195 .landing-bigstat-label {
2196 font-family: var(--font-mono);
2197 font-size: 11px;
958d26aClaude2198 font-weight: 500;
c963db5Claude2199 text-transform: uppercase;
2200 letter-spacing: 0.14em;
2201 color: var(--text-faint);
2b821b7Claude2202 }
c963db5Claude2203 @media (max-width: 800px) {
2204 .landing-bigstats-grid { grid-template-columns: repeat(2, 1fr); gap: 28px 16px; }
2205 }
2206 @media (max-width: 480px) {
2207 .landing-bigstats-grid { grid-template-columns: 1fr; gap: 24px; }
958d26aClaude2208 }
2209
2210 /* ---------- Section base ---------- */
2211 .landing-section { margin: var(--s-20) auto; }
2b821b7Claude2212
4c47454Claude2213 /* ---------- Feature grid ---------- */
2214 .landing-features {
2b821b7Claude2215 display: grid;
2216 grid-template-columns: repeat(3, 1fr);
958d26aClaude2217 gap: 16px;
2b821b7Claude2218 }
2219 .landing-feature {
958d26aClaude2220 background: var(--bg-elevated);
2b821b7Claude2221 border: 1px solid var(--border);
958d26aClaude2222 border-radius: var(--r-lg);
2223 padding: var(--s-7);
2224 position: relative;
2225 overflow: hidden;
2226 isolation: isolate;
2227 transition:
2228 transform var(--t-base) var(--ease-out-quart),
2229 border-color var(--t-base) var(--ease),
2230 box-shadow var(--t-base) var(--ease);
2231 }
2232 .landing-feature::before {
2233 content: '';
2234 position: absolute;
2235 inset: 0;
ba23fe2Claude2236 background: var(--bg-surface);
958d26aClaude2237 opacity: 0;
2238 transition: opacity var(--t-base) var(--ease);
2239 z-index: -1;
4c47454Claude2240 }
2241 .landing-feature:hover {
958d26aClaude2242 transform: translateY(-3px);
2243 border-color: var(--border-strong);
2244 box-shadow: var(--elev-2);
2b821b7Claude2245 }
958d26aClaude2246 .landing-feature:hover::before { opacity: 1; }
2b821b7Claude2247 .landing-feature-icon {
4c47454Claude2248 display: inline-flex;
2249 align-items: center;
2250 justify-content: center;
958d26aClaude2251 width: 40px;
2252 height: 40px;
2253 border-radius: var(--r);
2254 background: var(--accent-gradient-soft);
2255 color: var(--accent);
2256 margin-bottom: var(--s-4);
ba23fe2Claude2257 border: 1px solid var(--border);
2b821b7Claude2258 }
2259 .landing-feature-title {
958d26aClaude2260 font-family: var(--font-display);
2261 font-size: 19px;
2262 font-weight: 600;
2263 letter-spacing: -0.018em;
2264 margin: 0 0 var(--s-2);
2265 color: var(--text-strong);
2b821b7Claude2266 }
2267 .landing-feature-desc {
958d26aClaude2268 font-size: var(--t-sm);
2269 color: var(--text-muted);
2270 line-height: 1.6;
2271 margin: 0;
2272 }
2273
2274 /* ---------- Walkthrough ---------- */
2275 .landing-walk-grid {
2276 display: grid;
2277 grid-template-columns: repeat(4, 1fr);
2278 gap: 16px;
2279 counter-reset: walk;
2280 }
2281 .landing-walk-step {
2282 position: relative;
2283 padding: var(--s-7) var(--s-6) var(--s-6);
2284 background: var(--bg-elevated);
2285 border: 1px solid var(--border);
2286 border-radius: var(--r-lg);
2287 }
2288 .landing-walk-step::after {
2289 content: '';
2290 position: absolute;
2291 top: 50%;
2292 right: -12px;
2293 width: 12px;
2294 height: 1px;
2295 background: var(--border-strong);
2296 }
2297 .landing-walk-step:last-child::after { display: none; }
2298 .landing-walk-num {
2299 display: inline-block;
2300 font-family: var(--font-mono);
2301 font-size: 11px;
2302 color: var(--accent);
2303 background: var(--accent-gradient-faint);
ba23fe2Claude2304 border: 1px solid var(--border);
958d26aClaude2305 padding: 3px 8px;
2306 border-radius: var(--r-full);
2307 letter-spacing: 0.06em;
2308 margin-bottom: var(--s-3);
2309 }
2310 .landing-walk-title {
2311 font-family: var(--font-display);
2312 font-size: 22px;
2313 font-weight: 600;
2314 letter-spacing: -0.022em;
2315 margin: 0 0 var(--s-2);
2316 color: var(--text-strong);
2317 }
2318 .landing-walk-desc {
2319 font-size: var(--t-sm);
2b821b7Claude2320 color: var(--text-muted);
2321 line-height: 1.55;
2322 margin: 0;
2323 }
2324
958d26aClaude2325 /* ---------- Terminal ---------- */
2326 .landing-terminal-section { margin-top: var(--s-16); }
4c47454Claude2327 .landing-terminal-wrap {
2328 display: flex;
2b821b7Claude2329 justify-content: center;
2330 }
4c47454Claude2331 .landing-terminal {
2332 width: 100%;
958d26aClaude2333 max-width: 820px;
2334 background: linear-gradient(180deg, #0a0b12 0%, #06070c 100%);
2335 border: 1px solid var(--border-strong);
2336 border-radius: var(--r-lg);
2337 overflow: hidden;
ba23fe2Claude2338 box-shadow: var(--elev-3);
4c47454Claude2339 text-align: left;
2b821b7Claude2340 }
958d26aClaude2341 :root[data-theme='light'] .landing-terminal {
2342 background: linear-gradient(180deg, #0f111a 0%, #06070c 100%);
2343 }
2344 .landing-terminal-chrome {
2345 display: flex;
2346 align-items: center;
2347 gap: 7px;
2348 padding: 11px 14px;
2349 background: rgba(255,255,255,0.025);
2350 border-bottom: 1px solid rgba(255,255,255,0.06);
2351 position: relative;
2352 }
2353 .landing-terminal-dot {
2354 width: 11px;
2355 height: 11px;
2356 border-radius: 50%;
2357 flex-shrink: 0;
2358 }
2359 .landing-terminal-dot-r { background: #ff5f57; }
2360 .landing-terminal-dot-y { background: #febc2e; }
2361 .landing-terminal-dot-g { background: #28c840; }
2362 .landing-terminal-title {
2363 position: absolute;
2364 left: 50%;
2365 transform: translateX(-50%);
2366 font-family: var(--font-mono);
2367 font-size: 11px;
2368 color: rgba(237,237,242,0.55);
2369 letter-spacing: 0.01em;
2370 }
2371 .landing-terminal-body {
2372 padding: var(--s-6) var(--s-7);
2373 font-family: var(--font-mono);
2374 font-feature-settings: var(--mono-feat);
2375 font-size: 13.5px;
2376 line-height: 1.85;
2377 color: rgba(237,237,242,0.92);
2378 }
4c47454Claude2379 .landing-term-line {
2380 display: flex;
2381 gap: 10px;
2382 white-space: pre-wrap;
2383 word-break: break-all;
2b821b7Claude2384 }
958d26aClaude2385 .landing-term-out { color: rgba(237,237,242,0.7); }
ba23fe2Claude2386 .landing-term-prompt { color: var(--accent); user-select: none; flex-shrink: 0; }
958d26aClaude2387 .landing-term-meta { color: rgba(237,237,242,0.45); }
2388 .landing-term-ok { color: var(--green); user-select: none; flex-shrink: 0; }
2389 .landing-term-ok-line { color: rgba(237,237,242,0.92); }
2390 .landing-term-cursor { margin-top: 4px; }
2391 .landing-term-blink {
2392 animation: blink 1.05s steps(2) infinite;
2393 color: var(--accent);
2394 }
2395 @keyframes blink { 50% { opacity: 0; } }
2396
2397 /* ---------- Comparison ---------- */
2398 .landing-compare {
2399 max-width: 920px;
2400 margin: 0 auto;
2401 border: 1px solid var(--border);
2402 border-radius: var(--r-lg);
2403 overflow: hidden;
2404 background: var(--bg-elevated);
2405 }
2406 .landing-compare-row {
2407 display: grid;
2408 grid-template-columns: 1fr 180px 180px;
2409 align-items: center;
2410 padding: 14px 20px;
2411 border-bottom: 1px solid var(--border-subtle);
2412 font-size: var(--t-sm);
2413 transition: background var(--t-fast) var(--ease);
2414 }
2415 .landing-compare-row:last-child { border-bottom: none; }
2416 .landing-compare-row:hover { background: var(--bg-hover); }
2417 .landing-compare-feature {
2418 color: var(--text-strong);
2419 font-weight: 500;
2420 }
2421 .landing-compare-them, .landing-compare-us {
2422 text-align: center;
2423 font-family: var(--font-mono);
2424 font-size: 12px;
2425 color: var(--text-muted);
2426 }
2427 .landing-compare-us { color: var(--green); font-weight: 500; }
2428 .landing-compare-hl .landing-compare-us {
2429 color: var(--accent);
2430 font-weight: 600;
2b821b7Claude2431 }
958d26aClaude2432 .landing-compare-hl .landing-compare-feature::after {
2433 content: 'NEW';
2434 margin-left: 8px;
2435 padding: 1px 6px;
2436 border-radius: 4px;
2437 background: var(--accent-gradient-faint);
2438 color: var(--accent);
2439 font-family: var(--font-mono);
2440 font-size: 9px;
2441 letter-spacing: 0.1em;
2442 font-weight: 600;
2443 vertical-align: 1px;
2444 }
2445 @media (max-width: 720px) {
2446 .landing-compare-row { grid-template-columns: 1fr 80px 80px; padding: 12px 14px; }
2447 .landing-compare-hl .landing-compare-feature::after { display: none; }
2448 }
2449
2450 /* ---------- Pricing ---------- */
2451 .landing-pricing {
2452 display: grid;
2453 grid-template-columns: repeat(3, 1fr);
2454 gap: 16px;
2455 max-width: 1080px;
2456 margin: 0 auto;
2457 align-items: stretch;
2458 }
2459 .landing-price-card {
2460 position: relative;
2461 background: var(--bg-elevated);
2462 border: 1px solid var(--border);
2463 border-radius: var(--r-lg);
2464 padding: var(--s-7);
2465 display: flex;
2466 flex-direction: column;
2467 gap: var(--s-4);
2468 transition: border-color var(--t-base) var(--ease), transform var(--t-base) var(--ease);
2469 }
2470 .landing-price-card:hover {
2471 border-color: var(--border-strong);
2472 transform: translateY(-2px);
2473 }
2474 .landing-price-hl {
ba23fe2Claude2475 border-color: var(--border-strong);
2476 box-shadow: var(--elev-2);
2477 background: var(--bg-elevated);
958d26aClaude2478 }
ba23fe2Claude2479 .landing-price-hl:hover { border-color: var(--accent); }
958d26aClaude2480 .landing-price-badge {
2481 position: absolute;
2482 top: -10px;
2483 left: 50%;
2484 transform: translateX(-50%);
2485 padding: 3px 12px;
2486 background: var(--accent-gradient);
2487 color: #fff;
2488 font-family: var(--font-mono);
2489 font-size: 10px;
2490 letter-spacing: 0.1em;
2491 text-transform: uppercase;
2492 font-weight: 600;
2493 border-radius: var(--r-full);
ba23fe2Claude2494 box-shadow: var(--elev-1);
958d26aClaude2495 }
2496 .landing-price-tier {
2497 font-family: var(--font-mono);
2498 font-size: 11px;
2499 text-transform: uppercase;
2500 letter-spacing: 0.16em;
2501 color: var(--text-muted);
2502 }
2503 .landing-price-amount {
2504 display: flex;
2505 align-items: baseline;
2506 gap: 8px;
2507 }
2508 .landing-price-num {
2509 font-family: var(--font-display);
2510 font-size: 40px;
2511 font-weight: 600;
2512 letter-spacing: -0.03em;
2513 color: var(--text-strong);
2514 }
2515 .landing-price-cad {
2516 font-size: var(--t-sm);
2517 color: var(--text-faint);
2518 }
2519 .landing-price-desc {
2520 font-size: var(--t-sm);
2521 color: var(--text-muted);
2522 line-height: 1.55;
2523 margin: 0;
2524 }
2525 .landing-price-features {
2526 list-style: none;
2527 padding: 0;
2528 margin: 0;
2529 display: flex;
2530 flex-direction: column;
2531 gap: 8px;
2532 font-size: var(--t-sm);
2533 color: var(--text);
2534 }
2535 .landing-price-features li {
2536 display: flex;
2537 align-items: center;
2538 gap: 9px;
2539 }
2540 .landing-price-check {
2541 color: var(--accent);
2542 font-weight: 600;
4c47454Claude2543 flex-shrink: 0;
2b821b7Claude2544 }
958d26aClaude2545 .landing-price-cta { margin-top: auto; }
2546
2547 /* ---------- Closing CTA ---------- */
2548 .landing-cta-section { margin: var(--s-20) auto var(--s-16); }
2549 .landing-cta-card {
2550 position: relative;
2551 text-align: center;
2552 padding: var(--s-16) var(--s-7);
2553 border: 1px solid var(--border-strong);
2554 border-radius: var(--r-2xl);
2555 background: var(--bg-elevated);
2556 overflow: hidden;
2557 isolation: isolate;
2558 }
2559 .landing-cta-bg {
2560 position: absolute;
2561 inset: 0;
2562 z-index: -1;
64aa989Claude2563 background: transparent;
958d26aClaude2564 }
2565 .landing-cta-card .eyebrow { justify-content: center; }
2566 .landing-cta-title {
2567 font-family: var(--font-display);
2568 font-size: clamp(28px, 4.4vw, 56px);
2569 line-height: 1.05;
2570 letter-spacing: -0.03em;
2571 font-weight: 600;
2572 margin: var(--s-3) 0 var(--s-4);
2573 color: var(--text-strong);
2574 }
2575 .landing-cta-sub {
2576 font-size: var(--t-md);
2577 color: var(--text-muted);
2578 max-width: 560px;
2579 margin: 0 auto var(--s-8);
2580 line-height: 1.55;
2581 }
2582 .landing-cta-buttons {
2583 display: flex;
2584 gap: 12px;
2585 justify-content: center;
2586 flex-wrap: wrap;
2587 }
2b821b7Claude2588
2589 /* ---------- Responsive ---------- */
958d26aClaude2590 @media (max-width: 960px) {
2591 .landing-features { grid-template-columns: repeat(2, 1fr); }
2592 .landing-walk-grid { grid-template-columns: repeat(2, 1fr); }
2593 .landing-walk-step::after { display: none; }
2594 .landing-pricing { grid-template-columns: 1fr; max-width: 480px; }
2595 }
2596 @media (max-width: 640px) {
2597 .landing-hero { padding: var(--s-14) 0 var(--s-10); }
2598 .landing-hero-cmd { flex-wrap: wrap; justify-content: center; }
2599 .landing-hero-ctas { flex-direction: column; align-items: stretch; }
2600 .landing-hero-ctas .btn { width: 100%; justify-content: center; }
2601 .landing-features { grid-template-columns: 1fr; }
2602 .landing-walk-grid { grid-template-columns: 1fr; }
2603 .landing-section { margin: var(--s-12) auto; }
2604 .landing-cta-card { padding: var(--s-10) var(--s-5); }
2605 .landing-cta-buttons .btn { width: 100%; justify-content: center; }
2b821b7Claude2606 }
52ad8b1Claude2607
2608 /* ---------- L4 social-proof counters ---------- */
2609 .landing-counters {
2610 margin: var(--s-10) auto var(--s-12);
2611 max-width: 1180px;
2612 padding: 0 var(--s-4);
2613 }
2614 .landing-counters-grid {
2615 display: grid;
2616 grid-template-columns: repeat(6, 1fr);
2617 gap: 20px;
2618 text-align: left;
2619 }
2620 .landing-counter {
2621 padding: var(--s-3) 0;
2622 border-top: 1px solid var(--border-subtle);
2623 }
2624 .landing-counter-num {
2625 font-family: var(--font-display);
2626 font-size: clamp(24px, 3vw, 38px);
2627 line-height: 1.05;
2628 letter-spacing: -0.03em;
2629 font-weight: 700;
2630 margin-bottom: 6px;
2631 font-feature-settings: 'tnum';
ba23fe2Claude2632 color: var(--text-strong);
52ad8b1Claude2633 }
2634 .landing-counter-label {
2635 font-family: var(--font-mono);
2636 font-size: 10.5px;
2637 font-weight: 500;
2638 text-transform: uppercase;
2639 letter-spacing: 0.12em;
2640 color: var(--text-faint);
2641 line-height: 1.4;
2642 }
2643 @media (max-width: 960px) {
2644 .landing-counters-grid { grid-template-columns: repeat(3, 1fr); gap: 20px 16px; }
2645 }
2646 @media (max-width: 540px) {
2647 .landing-counters-grid { grid-template-columns: repeat(2, 1fr); gap: 18px 12px; }
2648 }
5f2e749Claude2649
dc26881CC LABS App2650 /* ---------- L10/U1 hero install snippet ----------
2651 U1: wrapped in a labelled "power users" panel and re-located
2652 beneath the CTA + tertiary rows so it no longer competes with
2653 the primary calls to action. */
2654 .landing-hero-install-wrap {
2655 margin: 0 auto var(--space-6);
2656 text-align: center;
2657 }
2658 .landing-hero-install-label {
2659 display: inline-block;
2660 margin-bottom: var(--space-2);
2661 font-family: var(--font-mono);
2662 font-size: 11px;
2663 letter-spacing: 0.08em;
2664 text-transform: uppercase;
2665 color: var(--text-faint);
2666 }
5f2e749Claude2667 .landing-hero-install {
2668 display: inline-flex;
2669 align-items: stretch;
2670 gap: 0;
dc26881CC LABS App2671 margin: 0 auto;
5f2e749Claude2672 background: var(--bg-elevated);
2673 border: 1px solid var(--border-strong);
2674 border-radius: var(--r);
2675 box-shadow: var(--elev-1);
2676 overflow: hidden;
2677 max-width: 100%;
2678 font-family: var(--font-mono);
2679 }
2680 .landing-hero-install-code {
2681 display: inline-flex;
2682 align-items: center;
2683 gap: 10px;
2684 padding: 10px 14px;
2685 font-size: 13.5px;
2686 color: var(--text-strong);
2687 background: transparent;
2688 border: 0;
2689 white-space: nowrap;
2690 overflow-x: auto;
2691 }
2692 .landing-hero-install-prompt {
2693 color: var(--accent);
2694 user-select: none;
2695 }
2696 .landing-hero-install-copy {
2697 appearance: none;
2698 border: 0;
2699 border-left: 1px solid var(--border);
2700 background: transparent;
2701 color: var(--text-muted);
2702 font-family: var(--font-mono);
2703 font-size: 12px;
2704 font-weight: 600;
2705 letter-spacing: 0.06em;
2706 text-transform: uppercase;
2707 padding: 0 16px;
2708 cursor: pointer;
2709 transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
2710 }
2711 .landing-hero-install-copy:hover {
2712 background: var(--accent-gradient-faint);
2713 color: var(--accent);
2714 }
2715 .landing-hero-install-copy[data-copied="1"] {
2716 color: var(--green, #34d399);
2717 }
2718
b5d207bClaude2719 /* ---------- L10/U1 hero activity rail ----------
dc26881CC LABS App2720 U1 — tightened into a single horizontal strip. The 1px gradient
2721 rule on top is the same accent the headline uses, so the rail
2722 reads as part of the hero composition rather than a stray list. */
5f2e749Claude2723 .landing-hero-rail {
2724 list-style: none;
dc26881CC LABS App2725 padding: var(--space-4) 0 0;
2726 margin: 0 auto var(--space-6);
5f2e749Claude2727 display: flex;
2728 flex-wrap: wrap;
2729 justify-content: center;
dc26881CC LABS App2730 gap: var(--space-2) var(--space-6);
5f2e749Claude2731 font-family: var(--font-sans);
dc26881CC LABS App2732 font-size: 12px;
2733 color: var(--text-faint);
5f2e749Claude2734 max-width: 760px;
dc26881CC LABS App2735 position: relative;
2736 }
2737 .landing-hero-rail::before {
2738 content: '';
2739 position: absolute;
2740 top: 0;
2741 left: 50%;
2742 transform: translateX(-50%);
2743 width: 120px;
2744 height: 1px;
2745 background: var(--accent-gradient);
2746 opacity: 0.45;
2747 border-radius: 9999px;
5f2e749Claude2748 }
2749 .landing-hero-rail li {
2750 display: inline-flex;
dc26881CC LABS App2751 align-items: baseline;
2752 gap: var(--space-2);
5f2e749Claude2753 line-height: 1.4;
2754 }
2755 .landing-hero-rail strong {
2756 color: var(--text-strong);
2757 font-weight: 600;
2758 font-feature-settings: 'tnum';
dc26881CC LABS App2759 font-size: 14px;
2760 letter-spacing: -0.01em;
5f2e749Claude2761 }
dc26881CC LABS App2762 .landing-hero-rail-label {
2763 color: var(--text-muted);
2764 letter-spacing: 0.01em;
5f2e749Claude2765 }
dc26881CC LABS App2766 /* Backwards-compat: nothing references this any more but if a stale
2767 fragment lingers it's still hidden cleanly rather than orphaned. */
2768 .landing-hero-rail-check { display: none; }
5f2e749Claude2769
2770 /* ---------- L10 three-reasons section ---------- */
2771 .landing-reasons { margin-top: var(--s-12); }
2772 .landing-reasons-grid {
2773 display: grid;
2774 grid-template-columns: repeat(3, 1fr);
2775 gap: 16px;
2776 }
2777 .landing-reason {
2778 background: var(--bg-elevated);
2779 border: 1px solid var(--border);
2780 border-radius: var(--r-lg);
2781 padding: var(--s-7);
2782 display: flex;
2783 flex-direction: column;
2784 gap: var(--s-3);
2785 transition: border-color var(--t-base) var(--ease), transform var(--t-base) var(--ease);
2786 }
2787 .landing-reason:hover {
2788 border-color: var(--border-strong);
2789 transform: translateY(-2px);
2790 }
2791 .landing-reason-icon {
2792 display: inline-flex;
2793 align-items: center;
2794 justify-content: center;
2795 width: 40px;
2796 height: 40px;
2797 border-radius: var(--r);
2798 background: var(--accent-gradient-soft);
2799 color: var(--accent);
ba23fe2Claude2800 border: 1px solid var(--border);
5f2e749Claude2801 }
2802 .landing-reason-title {
2803 font-family: var(--font-display);
2804 font-size: 20px;
2805 font-weight: 600;
2806 letter-spacing: -0.02em;
2807 margin: 0;
2808 color: var(--text-strong);
2809 }
2810 .landing-reason-body {
2811 font-size: var(--t-sm);
2812 color: var(--text-muted);
2813 line-height: 1.55;
2814 margin: 0;
2815 }
2816 .landing-reasons-code {
2817 display: block;
2818 font-family: var(--font-mono);
2819 font-size: 12px;
2820 color: var(--text-strong);
2821 background: var(--bg);
2822 border: 1px solid var(--border);
2823 border-radius: var(--r);
2824 padding: 8px 12px;
2825 overflow-x: auto;
2826 white-space: nowrap;
2827 }
2828 .landing-reason-link {
2829 margin-top: auto;
2830 display: inline-flex;
2831 align-items: center;
2832 gap: 6px;
2833 color: var(--accent);
2834 font-size: var(--t-sm);
2835 font-weight: 500;
2836 text-decoration: none;
2837 }
2838 .landing-reason-link:hover { text-decoration: underline; }
2839 @media (max-width: 960px) {
2840 .landing-reasons-grid { grid-template-columns: 1fr; max-width: 520px; margin: 0 auto; }
2841 }
2842
2843 /* ---------- L10 "How is this different" pull-quote ---------- */
2844 .landing-pullquote-section {
2845 margin: var(--s-20) auto var(--s-12);
2846 max-width: 920px;
2847 padding: 0 var(--s-4);
2848 text-align: center;
2849 }
2850 .landing-pullquote {
2851 margin: 0;
2852 padding: var(--s-10) var(--s-7);
ba23fe2Claude2853 background: var(--bg-elevated);
5f2e749Claude2854 border: 1px solid var(--border-strong);
2855 border-radius: var(--r-xl);
2856 position: relative;
2857 overflow: hidden;
2858 }
2859 .landing-pullquote-eyebrow {
2860 font-family: var(--font-mono);
2861 font-size: 11px;
2862 font-weight: 600;
2863 letter-spacing: 0.16em;
2864 text-transform: uppercase;
2865 color: var(--accent);
2866 margin-bottom: var(--s-4);
2867 }
2868 .landing-pullquote-text {
2869 font-family: var(--font-display);
2870 font-size: clamp(20px, 2.4vw, 28px);
2871 line-height: 1.4;
2872 letter-spacing: -0.018em;
2873 color: var(--text-strong);
2874 margin: 0 auto;
2875 max-width: 760px;
2876 quotes: "\\201C" "\\201D";
2877 }
2878 .landing-pullquote-text::before { content: open-quote; color: var(--accent); margin-right: 4px; }
2879 .landing-pullquote-text::after { content: close-quote; color: var(--accent); margin-left: 4px; }
2880 .landing-pullquote-link {
2881 display: inline-flex;
2882 align-items: center;
2883 gap: 6px;
2884 margin-top: var(--s-6);
2885 color: var(--accent);
2886 font-size: var(--t-sm);
2887 font-weight: 500;
2888 text-decoration: none;
2889 }
2890 .landing-pullquote-link:hover { text-decoration: underline; }
2891
2892 /* ---------- L10 hero responsive overrides ---------- */
2893 @media (max-width: 640px) {
2894 .landing-hero-install { width: 100%; }
2895 .landing-hero-install-code { flex: 1; font-size: 12px; }
2896 .landing-hero-rail { flex-direction: column; align-items: flex-start; gap: 6px; padding: 0 var(--s-3); }
2897 .landing-hero-rail li { width: 100%; }
2898 }
534f04aClaude2899
2900 /* ============================================================ */
2901 /* Block M1 — Live-now demo feed */
2902 /* ============================================================ */
2903 .landing-livenow {
2904 margin: var(--s-8) 0 var(--s-6);
2905 padding: var(--s-6) 0 var(--s-4);
2906 }
2907 .landing-livenow-head {
2908 text-align: center;
2909 margin-bottom: var(--s-6);
2910 }
2911 .landing-livenow-eyebrow {
2912 display: inline-flex;
2913 align-items: center;
2914 gap: 8px;
2915 padding: 4px 12px;
2916 border-radius: var(--r-full);
2917 background: rgba(52,211,153,0.08);
2918 border: 1px solid rgba(52,211,153,0.25);
2919 color: var(--green);
2920 font-family: var(--font-mono, ui-monospace, monospace);
2921 font-size: 11px;
2922 letter-spacing: 0.06em;
2923 text-transform: uppercase;
2924 margin-bottom: var(--s-3);
2925 }
2926 .landing-livenow-pulse {
2927 width: 8px; height: 8px;
2928 border-radius: 50%;
2929 background: var(--green);
ba23fe2Claude2930 animation: heroPulse 2.4s ease-in-out infinite;
534f04aClaude2931 }
2932 .landing-livenow-title {
2933 font-size: 22px;
2934 line-height: 1.25;
2935 margin: 0 auto;
2936 max-width: 720px;
2937 color: var(--text-strong);
2938 font-weight: 600;
2939 letter-spacing: -0.01em;
2940 }
2941 .landing-livenow-sub {
2942 margin: var(--s-2) auto 0;
2943 color: var(--text-muted);
2944 font-size: 13px;
2945 max-width: 560px;
2946 }
2947 .landing-livenow-sub code {
2948 background: rgba(255,255,255,0.05);
2949 border: 1px solid var(--border);
2950 padding: 1px 6px;
2951 border-radius: 4px;
2952 font-size: 12px;
2953 color: var(--accent);
2954 }
2955
2956 .landing-livenow-grid {
2957 display: grid;
2958 grid-template-columns: repeat(2, minmax(0, 1fr));
2959 gap: var(--s-3);
2960 }
2961 @media (min-width: 980px) {
2962 .landing-livenow-grid {
2963 grid-template-columns: repeat(4, minmax(0, 1fr));
2964 }
2965 }
2966
2967 .landing-livecard {
2968 background: linear-gradient(180deg, rgba(255,255,255,0.025), rgba(255,255,255,0.005));
2969 border: 1px solid var(--border);
2970 border-radius: var(--r-md, 10px);
2971 padding: 14px 14px 12px;
2972 display: flex;
2973 flex-direction: column;
2974 min-height: 180px;
2975 position: relative;
2976 overflow: hidden;
2977 }
2978 .landing-livecard::before {
2979 content: '';
2980 position: absolute;
2981 inset: 0;
2982 background: var(--accent-gradient-faint);
2983 opacity: 0;
2984 transition: opacity 200ms var(--ease, ease);
2985 pointer-events: none;
2986 }
2987 .landing-livecard:hover::before { opacity: 1; }
2988
2989 .landing-livecard-head {
2990 display: flex;
2991 align-items: center;
2992 gap: 8px;
2993 margin-bottom: 10px;
2994 }
2995 .landing-livecard-dot {
2996 width: 7px; height: 7px;
2997 border-radius: 50%;
2998 background: var(--green);
ba23fe2Claude2999 animation: heroPulse 2.4s ease-in-out infinite;
534f04aClaude3000 flex-shrink: 0;
3001 }
3002 .landing-livecard-title {
3003 margin: 0;
3004 font-size: 12px;
3005 font-weight: 600;
3006 text-transform: uppercase;
3007 letter-spacing: 0.06em;
3008 color: var(--text-muted);
3009 }
3010
3011 .landing-livecard-bignum {
3012 display: flex;
3013 align-items: baseline;
3014 gap: 8px;
3015 margin: 4px 0 10px;
3016 }
3017 .landing-livecard-bignum-n {
3018 font-size: 30px;
3019 font-weight: 700;
3020 color: var(--text-strong);
3021 font-variant-numeric: tabular-nums;
3022 }
3023 .landing-livecard-bignum-label {
3024 font-size: 12px;
3025 color: var(--text-muted);
3026 }
3027
3028 .landing-livecard-list {
3029 list-style: none;
3030 margin: 0;
3031 padding: 0;
3032 font-size: 13px;
3033 line-height: 1.45;
3034 flex: 1;
3035 }
3036 .landing-livecard-row, .landing-livecard-feedrow {
3037 padding: 6px 0;
3038 border-bottom: 1px dashed rgba(255,255,255,0.05);
3039 transition: background-color 1s var(--ease, ease);
3040 border-radius: 4px;
3041 margin: 0 -4px;
3042 padding-left: 4px;
3043 padding-right: 4px;
3044 }
3045 .landing-livecard-row:last-child,
3046 .landing-livecard-feedrow:last-child { border-bottom: 0; }
3047 .landing-livecard-feedrow { padding: 4px; font-size: 12.5px; }
3048
3049 .landing-livecard-flash {
3050 background-color: rgba(52,211,153,0.18) !important;
3051 animation: landing-livecard-flash-fade 1.1s ease-out forwards;
3052 }
3053 @keyframes landing-livecard-flash-fade {
3054 0% { background-color: rgba(52,211,153,0.22); }
3055 100% { background-color: rgba(52,211,153,0); }
3056 }
3057
3058 .landing-livecard-link {
3059 color: var(--text-strong);
3060 text-decoration: none;
3061 display: inline-block;
3062 max-width: 100%;
3063 overflow: hidden;
3064 text-overflow: ellipsis;
3065 white-space: nowrap;
3066 }
3067 .landing-livecard-link:hover { color: var(--accent); }
3068 .landing-livecard-num {
3069 font-family: var(--font-mono, ui-monospace, monospace);
3070 color: var(--text-faint);
3071 font-weight: 500;
3072 font-size: 12px;
3073 }
3074 .landing-livecard-title-text { color: var(--text-strong); }
3075 .landing-livecard-snippet {
3076 color: var(--text-muted);
3077 font-style: italic;
3078 font-size: 12px;
3079 }
3080 .landing-livecard-meta {
3081 font-size: 11px;
3082 color: var(--text-faint);
3083 margin-top: 2px;
3084 font-family: var(--font-mono, ui-monospace, monospace);
3085 }
3086 .landing-livecard-repo {
3087 color: var(--accent-2);
3088 }
3089 .landing-livecard-rel {
3090 color: var(--text-muted);
3091 }
3092 .landing-livecard-empty {
3093 color: var(--text-faint);
3094 font-size: 12px;
3095 font-style: italic;
3096 padding: 8px 0;
3097 }
3098 .landing-livecard-kind {
3099 display: inline-block;
3100 padding: 1px 7px;
3101 border-radius: var(--r-full);
3102 font-family: var(--font-mono, ui-monospace, monospace);
3103 font-size: 10px;
3104 letter-spacing: 0.04em;
3105 text-transform: uppercase;
3106 font-weight: 600;
3107 }
3108 .landing-livecard-kind-auto_merge-merged,
3109 .landing-livecard-kind-auto-merge-merged {
3110 background: rgba(52,211,153,0.12);
3111 color: var(--green);
3112 border: 1px solid rgba(52,211,153,0.25);
3113 }
3114 .landing-livecard-kind-ai_build-dispatched,
3115 .landing-livecard-kind-ai-build-dispatched {
ba23fe2Claude3116 background: var(--accent-gradient-faint);
534f04aClaude3117 color: var(--accent);
ba23fe2Claude3118 border: 1px solid var(--border-strong);
534f04aClaude3119 }
3120 .landing-livecard-kind-ai_review-posted,
3121 .landing-livecard-kind-ai-review-posted {
ba23fe2Claude3122 background: var(--bg-surface);
534f04aClaude3123 color: var(--accent-2);
ba23fe2Claude3124 border: 1px solid var(--border);
534f04aClaude3125 }
3126
3127 .landing-livenow-cta {
3128 margin-top: var(--s-5);
3129 display: flex;
3130 flex-wrap: wrap;
3131 align-items: center;
3132 justify-content: center;
3133 gap: 10px;
3134 font-size: 13px;
3135 color: var(--text-muted);
3136 }
3137 .landing-livenow-cta-link {
3138 color: var(--accent);
3139 text-decoration: none;
3140 font-weight: 500;
3141 display: inline-flex;
3142 align-items: center;
3143 gap: 4px;
3144 }
3145 .landing-livenow-cta-link:hover { color: var(--accent-hover); }
3146 .landing-livenow-cta-sep { color: var(--text-faint); }
3147
3148 @media (prefers-reduced-motion: reduce) {
3149 .landing-livenow-pulse,
3150 .landing-livecard-dot { animation: none; }
3151 .landing-livecard-flash { animation: none; background-color: transparent !important; }
3152 }
52ad8b1Claude3153`;
3154
3155/**
3156 * Block L4 — count-up animation.
3157 *
3158 * Reads each `[data-counter-target]` and animates the in-DOM text from
3159 * 0 → target over ~1.2s when the element first scrolls into view.
3160 *
3161 * Render-once semantics: each tile already contains the final value as
3162 * HTML, so visitors with JS disabled — or anyone before the script
3163 * loads — sees the correct number. The script just animates the text.
3164 *
3165 * Falls back to the static value (no animation) when IntersectionObserver
3166 * isn't available, or when the user prefers reduced motion.
3167 */
3168const landingCountersJs = `
3169(function(){
3170 try {
3171 var els = document.querySelectorAll('[data-counter-target]');
3172 if (!els.length) return;
3173 var reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
3174 if (reduced || typeof IntersectionObserver !== 'function') return;
3175
3176 function animate(el) {
3177 var target = parseInt(el.getAttribute('data-counter-target') || '0', 10);
3178 if (!isFinite(target) || target <= 0) return;
3179 var prefix = el.getAttribute('data-counter-prefix') || '';
3180 var suffix = el.getAttribute('data-counter-suffix') || '';
3181 var duration = 1200;
3182 var start = performance.now();
3183 function frame(now) {
3184 var t = Math.min(1, (now - start) / duration);
3185 // ease-out cubic
3186 var eased = 1 - Math.pow(1 - t, 3);
3187 var v = Math.floor(eased * target);
3188 el.textContent = prefix + v.toLocaleString() + suffix;
3189 if (t < 1) requestAnimationFrame(frame);
3190 else el.textContent = prefix + target.toLocaleString() + suffix;
3191 }
3192 // Reset to zero before animating in.
3193 el.textContent = prefix + '0' + suffix;
3194 requestAnimationFrame(frame);
3195 }
3196
3197 var io = new IntersectionObserver(function(entries) {
3198 entries.forEach(function(entry){
3199 if (entry.isIntersecting) {
3200 animate(entry.target);
3201 io.unobserve(entry.target);
3202 }
3203 });
3204 }, { threshold: 0.4 });
3205 els.forEach(function(el){ io.observe(el); });
3206 } catch (_) { /* swallow — static numbers remain */ }
3207})();
2b821b7Claude3208`;
5f2e749Claude3209
3210/**
3211 * Block L10 — clipboard copy for the hero install snippet.
3212 *
3213 * Pure progressive enhancement. Without JS the user can still
3214 * triple-click + Cmd/Ctrl-C the snippet — the button is the
3215 * speed-bump, not the only path.
3216 */
3217const landingCopyJs = `
3218(function(){
3219 try {
3220 var btns = document.querySelectorAll('[data-copy-target]');
3221 if (!btns.length) return;
3222 btns.forEach(function(btn){
3223 btn.addEventListener('click', function(){
3224 var id = btn.getAttribute('data-copy-target') || '';
3225 var src = document.getElementById(id);
3226 if (!src) return;
3227 var text = src.textContent || '';
3228 var done = function(){
3229 var prev = btn.textContent;
3230 btn.textContent = 'Copied';
3231 btn.setAttribute('data-copied', '1');
3232 setTimeout(function(){
3233 btn.textContent = prev || 'Copy';
3234 btn.removeAttribute('data-copied');
3235 }, 1500);
3236 };
3237 if (navigator.clipboard && navigator.clipboard.writeText) {
3238 navigator.clipboard.writeText(text).then(done, function(){ done(); });
3239 } else {
3240 // Legacy fallback — temp textarea + execCommand.
3241 try {
3242 var ta = document.createElement('textarea');
3243 ta.value = text;
3244 ta.style.position = 'fixed';
3245 ta.style.opacity = '0';
3246 document.body.appendChild(ta);
3247 ta.select();
3248 document.execCommand('copy');
3249 document.body.removeChild(ta);
3250 done();
3251 } catch (_) {}
3252 }
3253 });
3254 });
3255 } catch (_) { /* swallow */ }
3256})();
3257`;
0c3eee5Claude3258
3259// ============================================================
3260// land2030Css — scoped CSS for the 2030 homepage prelude.
3261// Every selector is prefixed `.land-2030-*` so it can't bleed
3262// into the older `.landing-*` (L10/U1/Q1/M1) styles below.
3263// ============================================================
3264const land2030Css = `
3265 .land-2030-root {
3266 position: relative;
3267 max-width: 1240px;
3268 margin: 0 auto;
3269 padding: 0 16px var(--space-6, 32px);
3270 color: var(--text, #e6e6f0);
3271 }
3272
3273 /* ---------- Hero ---------- */
3274 .land-2030-hero {
3275 position: relative;
3276 padding: clamp(48px, 8vw, 120px) 8px clamp(40px, 6vw, 80px);
3277 text-align: center;
3278 overflow: hidden;
3279 }
3280 .land-2030-hairline {
3281 position: absolute;
3282 top: 0; left: 0; right: 0;
ba23fe2Claude3283 height: 1px;
3284 background: var(--border);
0c3eee5Claude3285 pointer-events: none;
3286 }
3287 .land-2030-orb {
3288 position: absolute;
3289 top: -10%;
3290 left: 50%;
3291 width: 720px;
ba23fe2Claude3292 height: 480px;
0c3eee5Claude3293 transform: translateX(-50%);
ba23fe2Claude3294 background: radial-gradient(ellipse, rgba(91,110,232,0.07), transparent 70%);
0c3eee5Claude3295 pointer-events: none;
3296 z-index: 0;
3297 }
3298 .land-2030-hero-inner {
3299 position: relative;
3300 z-index: 1;
3301 max-width: 1080px;
3302 margin: 0 auto;
3303 }
3304 .land-2030-eyebrow {
3305 display: inline-flex;
3306 align-items: center;
3307 gap: 8px;
3308 padding: 6px 12px;
3309 border-radius: 999px;
ba23fe2Claude3310 background: var(--bg-surface);
3311 color: var(--accent);
0c3eee5Claude3312 font-size: 12px;
3313 letter-spacing: 0.04em;
3314 text-transform: uppercase;
ba23fe2Claude3315 border: 1px solid var(--border);
0c3eee5Claude3316 margin-bottom: 24px;
3317 }
3318 .land-2030-eyebrow-mini {
3319 margin-bottom: 12px;
3320 }
3321 .land-2030-pulse {
3322 width: 8px; height: 8px;
3323 border-radius: 50%;
ba23fe2Claude3324 background: var(--accent);
0c3eee5Claude3325 animation: land2030-pulse 2s ease-in-out infinite;
3326 }
3327 @keyframes land2030-pulse {
3328 0%, 100% { opacity: 0.5; transform: scale(1); }
3329 50% { opacity: 1; transform: scale(1.25); }
3330 }
3331 .land-2030-display {
3332 font-size: clamp(60px, 9vw, 96px);
3333 font-family: var(--font-display, inherit);
3334 font-weight: 800;
3335 line-height: 1.02;
3336 letter-spacing: -0.035em;
3337 margin: 0 auto 24px;
3338 max-width: 1040px;
3339 }
ba23fe2Claude3340 .land-2030-display span { color: var(--accent); }
0c3eee5Claude3341 .land-2030-sub {
3342 font-size: clamp(17px, 1.6vw, 22px);
3343 color: var(--text-muted, #a0a0b8);
3344 max-width: 720px;
3345 margin: 0 auto 36px;
3346 line-height: 1.45;
3347 }
3348 .land-2030-cta-row {
3349 display: flex;
3350 flex-wrap: wrap;
3351 justify-content: center;
3352 gap: 12px;
3353 }
3354 .land-2030-cta-primary,
3355 .land-2030-cta-secondary {
3356 min-width: 200px;
3357 }
3358
3359 /* ---------- Section scaffold ---------- */
3360 .land-2030-section {
3361 margin: clamp(56px, 8vw, 96px) 0;
3362 position: relative;
3363 }
3364 .land-2030-section-dark {
3365 padding: 48px 32px;
ba23fe2Claude3366 background: var(--bg-elevated);
3367 border: 1px solid var(--border);
0c3eee5Claude3368 border-radius: 20px;
3369 }
3370 .land-2030-section-head {
3371 text-align: center;
3372 max-width: 760px;
3373 margin: 0 auto 40px;
3374 }
3375 .land-2030-h2 {
3376 font-size: clamp(28px, 4vw, 44px);
3377 font-family: var(--font-display, inherit);
3378 font-weight: 700;
3379 letter-spacing: -0.025em;
3380 line-height: 1.1;
3381 margin: 0 0 12px;
3382 }
3383 .land-2030-lede {
3384 font-size: 16px;
3385 color: var(--text-muted, #a0a0b8);
3386 line-height: 1.55;
3387 margin: 0;
3388 }
3389
3390 /* ---------- Closed loop diagram ---------- */
3391 .land-2030-loop-wrap {
3392 max-width: 1100px;
3393 margin: 0 auto;
3394 padding: 16px;
ba23fe2Claude3395 background: var(--bg-elevated);
3396 border: 1px solid var(--border);
0c3eee5Claude3397 border-radius: 20px;
3398 color: var(--text-muted, #a0a0b8);
3399 }
3400 .land-2030-loop-svg {
3401 display: block;
3402 width: 100%;
3403 height: auto;
3404 }
3405 .land-2030-loop-path {
3406 stroke-dasharray: 6 4;
3407 animation: land2030-loop-dash 30s linear infinite;
3408 }
3409 @keyframes land2030-loop-dash {
3410 from { stroke-dashoffset: 0; }
3411 to { stroke-dashoffset: -400; }
3412 }
3413 .land-2030-loop-text {
3414 font-size: 14px;
3415 font-weight: 600;
3416 letter-spacing: -0.005em;
3417 }
3418
3419 /* ---------- 6-card "unfair advantages" grid ---------- */
3420 .land-2030-card-grid {
3421 display: grid;
3422 grid-template-columns: repeat(3, minmax(0, 1fr));
3423 gap: 16px;
3424 max-width: 1180px;
3425 margin: 0 auto;
3426 }
3427 .land-2030-card {
3428 display: flex;
3429 flex-direction: column;
3430 gap: 10px;
3431 padding: 24px;
ba23fe2Claude3432 background: var(--bg-elevated);
3433 border: 1px solid var(--border);
0c3eee5Claude3434 border-radius: 16px;
3435 text-decoration: none;
3436 color: inherit;
ba23fe2Claude3437 transition: transform 200ms ease, border-color 200ms ease;
0c3eee5Claude3438 }
3439 .land-2030-card:hover {
3440 transform: translateY(-2px);
ba23fe2Claude3441 border-color: var(--border-strong);
0c3eee5Claude3442 }
3443 .land-2030-card-icon {
3444 width: 44px; height: 44px;
3445 display: inline-flex;
3446 align-items: center;
3447 justify-content: center;
3448 border-radius: 12px;
ba23fe2Claude3449 background: var(--bg-surface);
3450 color: var(--accent);
3451 border: 1px solid var(--border);
0c3eee5Claude3452 margin-bottom: 4px;
3453 }
3454 .land-2030-card-title {
3455 margin: 0;
3456 font-size: 18px;
3457 font-weight: 700;
3458 letter-spacing: -0.01em;
3459 }
3460 .land-2030-card-desc {
3461 margin: 0;
3462 font-size: 14.5px;
3463 color: var(--text-muted, #a0a0b8);
3464 line-height: 1.55;
3465 }
3466 .land-2030-card-cta {
3467 margin-top: auto;
ba23fe2Claude3468 color: var(--accent);
0c3eee5Claude3469 font-size: 14px;
3470 font-weight: 600;
3471 }
3472
3473 /* ---------- Global dashboards grid ---------- */
3474 .land-2030-dash-grid {
3475 display: grid;
3476 grid-template-columns: repeat(5, minmax(0, 1fr));
3477 gap: 12px;
3478 max-width: 1180px;
3479 margin: 0 auto;
3480 }
3481 .land-2030-dash {
3482 display: flex;
3483 flex-direction: column;
3484 gap: 6px;
3485 padding: 16px 18px;
ba23fe2Claude3486 background: var(--bg-elevated);
3487 border: 1px solid var(--border);
0c3eee5Claude3488 border-radius: 12px;
3489 text-decoration: none;
3490 color: inherit;
3491 transition: transform 180ms ease, border-color 180ms ease;
3492 }
3493 .land-2030-dash:hover {
3494 transform: translateY(-1px);
ba23fe2Claude3495 border-color: var(--border-strong);
0c3eee5Claude3496 }
3497 .land-2030-dash-name {
3498 font-family: var(--font-mono, ui-monospace, monospace);
3499 font-size: 14px;
ba23fe2Claude3500 color: var(--accent);
0c3eee5Claude3501 font-weight: 600;
3502 }
3503 .land-2030-dash-desc {
3504 font-size: 13px;
3505 color: var(--text-muted, #a0a0b8);
3506 line-height: 1.45;
3507 }
3508
3509 /* ---------- 18-feature grid + status pills ---------- */
3510 .land-2030-feat-grid {
3511 display: grid;
3512 grid-template-columns: repeat(3, minmax(0, 1fr));
3513 gap: 10px;
3514 max-width: 1180px;
3515 margin: 0 auto;
3516 }
3517 .land-2030-feat {
3518 display: flex;
3519 flex-direction: column;
3520 gap: 6px;
3521 padding: 14px 16px;
ba23fe2Claude3522 background: var(--bg-elevated);
3523 border: 1px solid var(--border);
0c3eee5Claude3524 border-radius: 10px;
3525 text-decoration: none;
3526 color: inherit;
3527 transition: border-color 160ms ease, background 160ms ease;
3528 }
3529 .land-2030-feat:hover {
ba23fe2Claude3530 border-color: var(--border-strong);
3531 background: var(--bg-surface);
0c3eee5Claude3532 }
3533 .land-2030-feat-head {
3534 display: flex;
3535 align-items: center;
3536 justify-content: space-between;
3537 gap: 10px;
3538 }
3539 .land-2030-feat-title {
3540 font-size: 14.5px;
3541 font-weight: 700;
3542 letter-spacing: -0.005em;
3543 }
3544 .land-2030-feat-desc {
3545 margin: 0;
3546 font-size: 13px;
3547 color: var(--text-muted, #a0a0b8);
3548 line-height: 1.5;
3549 }
3550 .land-2030-pill {
3551 font-size: 10.5px;
3552 letter-spacing: 0.04em;
3553 text-transform: uppercase;
3554 padding: 3px 8px;
3555 border-radius: 999px;
3556 border: 1px solid currentColor;
3557 font-weight: 700;
3558 line-height: 1;
3559 }
3560 .land-2030-pill-live { color: #5be0a9; border-color: rgba(91,224,169,0.5); background: rgba(91,224,169,0.10); }
3561 .land-2030-pill-beta { color: #ffd16b; border-color: rgba(255,209,107,0.5); background: rgba(255,209,107,0.10); }
3562 .land-2030-pill-soon { color: #a0a0b8; border-color: rgba(160,160,184,0.4); background: rgba(160,160,184,0.08); }
3563
3564 /* ---------- Developer surface ---------- */
3565 .land-2030-dx-grid {
3566 display: grid;
3567 grid-template-columns: repeat(3, minmax(0, 1fr));
3568 gap: 16px;
3569 max-width: 1180px;
3570 margin: 0 auto;
3571 }
3572 .land-2030-dx {
3573 padding: 18px;
ba23fe2Claude3574 background: var(--bg-elevated);
3575 border: 1px solid var(--border);
0c3eee5Claude3576 border-radius: 14px;
3577 }
3578 .land-2030-dx-title {
3579 margin: 0 0 10px;
3580 font-size: 15px;
3581 font-weight: 700;
3582 color: var(--text-strong, #fff);
3583 }
3584 .land-2030-code {
3585 margin: 0;
3586 padding: 12px 14px;
ba23fe2Claude3587 background: var(--bg);
3588 border: 1px solid var(--border);
0c3eee5Claude3589 border-radius: 10px;
3590 font-family: var(--font-mono, ui-monospace, monospace);
3591 font-size: 12.5px;
ba23fe2Claude3592 color: var(--text-muted);
0c3eee5Claude3593 line-height: 1.6;
3594 overflow-x: auto;
3595 }
3596 .land-2030-code code { color: inherit; background: transparent; padding: 0; }
3597 .land-2030-code-wide {
3598 font-size: 13px;
ba23fe2Claude3599 color: var(--text);
0c3eee5Claude3600 }
3601
3602 /* ---------- Agent multiplayer ---------- */
3603 .land-2030-agent-wrap {
3604 display: grid;
3605 grid-template-columns: 1.4fr 1fr;
3606 gap: 24px;
3607 max-width: 1080px;
3608 margin: 0 auto;
3609 align-items: center;
3610 }
3611 .land-2030-agent-stat { text-align: left; }
3612 .land-2030-agent-big {
3613 font-size: clamp(48px, 7vw, 80px);
3614 font-weight: 800;
3615 line-height: 1;
ba23fe2Claude3616 color: var(--text-strong);
0c3eee5Claude3617 letter-spacing: -0.03em;
3618 }
3619 .land-2030-agent-label {
3620 margin-top: 12px;
3621 color: var(--text-muted, #a0a0b8);
3622 font-size: 15px;
3623 line-height: 1.5;
3624 }
3625 .land-2030-agent-link {
3626 display: inline-block;
3627 margin-top: 16px;
ba23fe2Claude3628 color: var(--accent);
0c3eee5Claude3629 text-decoration: none;
3630 font-weight: 600;
3631 font-size: 14px;
3632 }
3633 .land-2030-agent-link:hover { text-decoration: underline; }
3634
3635 /* ---------- vs GitHub strip ---------- */
3636 .land-2030-vs {
3637 max-width: 880px;
3638 margin: 0 auto;
ba23fe2Claude3639 background: var(--bg-elevated);
3640 border: 1px solid var(--border);
0c3eee5Claude3641 border-radius: 14px;
3642 overflow: hidden;
3643 text-align: center;
3644 padding: 0 0 18px;
3645 }
3646 .land-2030-vs-row {
3647 display: grid;
3648 grid-template-columns: 1fr 1fr;
3649 gap: 0;
3650 padding: 16px 18px;
ba23fe2Claude3651 border-bottom: 1px solid var(--border);
0c3eee5Claude3652 font-size: 15px;
3653 }
3654 .land-2030-vs-row:last-of-type { border-bottom: 0; }
3655 .land-2030-vs-head {
3656 font-size: 12px;
3657 letter-spacing: 0.04em;
3658 text-transform: uppercase;
3659 color: var(--text-muted, #a0a0b8);
ba23fe2Claude3660 background: var(--bg-surface);
0c3eee5Claude3661 }
3662 .land-2030-vs-us {
ba23fe2Claude3663 color: var(--text-strong);
0c3eee5Claude3664 font-weight: 700;
3665 }
3666 .land-2030-vs-link {
3667 display: inline-block;
3668 margin-top: 12px;
ba23fe2Claude3669 color: var(--accent);
0c3eee5Claude3670 text-decoration: none;
3671 font-weight: 600;
3672 font-size: 14px;
3673 }
3674 .land-2030-vs-link:hover { text-decoration: underline; }
3675
3676 /* ---------- Responsive ---------- */
3677 @media (max-width: 960px) {
3678 .land-2030-card-grid,
3679 .land-2030-feat-grid,
3680 .land-2030-dx-grid,
3681 .land-2030-dash-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
3682 .land-2030-agent-wrap { grid-template-columns: 1fr; }
3683 }
3684 @media (max-width: 640px) {
3685 .land-2030-card-grid,
3686 .land-2030-feat-grid,
3687 .land-2030-dx-grid,
3688 .land-2030-dash-grid { grid-template-columns: 1fr; }
3689 .land-2030-section-dark { padding: 28px 16px; }
3690 .land-2030-vs-row { grid-template-columns: 1fr; gap: 4px; }
3691 }
3692 @media (max-width: 375px) {
3693 .land-2030-display { font-size: 44px; }
3694 .land-2030-cta-primary,
3695 .land-2030-cta-secondary { width: 100%; min-width: 0; }
3696 }
3697
3698 @media (prefers-reduced-motion: reduce) {
3699 .land-2030-loop-path,
3700 .land-2030-pulse { animation: none; }
3701 }
3702`;