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.tsxBlame2031 lines · 1 contributor
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),
11 * and a four-line "what just happened" rail driven off the L4 publicStats
12 * 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:
16 * - "Three reasons to switch" (Sleep Mode / Migrate / Demo)
17 * - "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";
2b821b7Claude25
26export interface LandingPageProps {
27 stats?: {
28 publicRepos?: number;
29 users?: number;
30 };
52ad8b1Claude31 /**
32 * Block L4 — full public-stats payload (lifetime + trailing-7-day
33 * AI-highlight counters). When present, the hero renders an animated
34 * six-tile social-proof row beneath the eyebrow.
35 */
36 publicStats?: PublicStats | null;
2b821b7Claude37}
38
52ad8b1Claude39export const LandingHero: FC<LandingPageProps> = ({ stats, publicStats } = {}) => {
8e9f1d9Claude40 const hasStats =
41 stats &&
42 ((stats.publicRepos !== undefined && stats.publicRepos > 0) ||
43 (stats.users !== undefined && stats.users > 0));
4c47454Claude44
52ad8b1Claude45 // Block L4 — six-tile social proof row. Rendered only when the
46 // cached public-stats payload is available; absent → fall back to
47 // the small text-only `landing-stats` row.
48 const tiles = publicStats
49 ? buildSocialProofTiles(publicStats)
50 : null;
51
2b821b7Claude52 return (
53 <>
fa880f2Claude54 <style dangerouslySetInnerHTML={{ __html: landingCss }} />
2b821b7Claude55
4c47454Claude56 <div class="landing-root">
57 {/* ---------- Hero ---------- */}
58 <section class="landing-hero">
958d26aClaude59 <div class="landing-hero-bg" aria-hidden="true">
60 <div class="landing-hero-blob landing-hero-blob-1" />
61 <div class="landing-hero-blob landing-hero-blob-2" />
62 <div class="landing-hero-grid" />
2b821b7Claude63 </div>
958d26aClaude64
65 <div class="landing-hero-inner stagger">
66 <div class="eyebrow landing-hero-eyebrow">
67 <span class="landing-hero-pulse" />
68 v1 · pre-launch · {new Date().getFullYear()}
69 </div>
70
71 <h1 class="landing-hero-title display">
5f2e749Claude72 <span class="gradient-text">The git host built around Claude.</span>
958d26aClaude73 </h1>
74
75 <p class="landing-hero-sub">
5f2e749Claude76 Label an issue. Walk away. Wake up to a merged PR.
958d26aClaude77 </p>
78
5f2e749Claude79 {/* L10 — one-line install snippet with copy button. */}
80 <div class="landing-hero-install" aria-label="One-line install">
81 <code class="landing-hero-install-code">
82 <span class="landing-hero-install-prompt" aria-hidden="true">$</span>
83 <span id="landing-install-text">curl -sSL gluecron.com/install | bash</span>
84 </code>
85 <button
86 type="button"
87 class="landing-hero-install-copy"
88 data-copy-target="landing-install-text"
89 aria-label="Copy install command"
90 >
91 Copy
92 </button>
93 </div>
94
958d26aClaude95 <div class="landing-hero-ctas">
96 <a href="/register" class="btn btn-primary btn-xl landing-cta-primary">
5f2e749Claude97 Sign up free
958d26aClaude98 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
99 </a>
5f2e749Claude100 <a href="/demo" class="btn btn-secondary btn-xl">
101 Try the live demo
102 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
958d26aClaude103 </a>
52ad8b1Claude104 <a href="/vs-github" class="btn btn-ghost btn-xl">
105 Compare to GitHub
5f2e749Claude106 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
52ad8b1Claude107 </a>
958d26aClaude108 </div>
109
5f2e749Claude110 {/* L10 — "what just happened" rail. Mini, secondary,
111 separate from the BIG L4 counters tile section below. */}
112 {publicStats && (
113 <ul class="landing-hero-rail" aria-label="What just happened on Gluecron">
114 <li>
115 <span class="landing-hero-rail-check" aria-hidden="true">{"✓"}</span>
116 <strong>{publicStats.weeklyPrsAutoMerged.toLocaleString()}</strong>
117 {" PRs auto-merged this week"}
118 </li>
119 <li>
120 <span class="landing-hero-rail-check" aria-hidden="true">{"✓"}</span>
121 <strong>{publicStats.weeklyIssuesBuiltByAi.toLocaleString()}</strong>
122 {" issues built by AI"}
123 </li>
124 <li>
125 <span class="landing-hero-rail-check" aria-hidden="true">{"✓"}</span>
126 <strong>{publicStats.weeklyDeploysShipped.toLocaleString()}</strong>
127 {" deploys shipped overnight"}
128 </li>
129 <li>
130 <span class="landing-hero-rail-check" aria-hidden="true">{"✓"}</span>
131 {"~"}
132 <strong>{Math.round(publicStats.weeklyHoursSaved).toLocaleString()}</strong>
133 {" hours saved by AI"}
134 </li>
135 </ul>
136 )}
137
138 {/* L8 — free-tier reassurance link. Keeps anxiety low for the AI-curious. */}
139 <p class="landing-hero-freenote">
140 Free forever for the AI-curious.{" "}
141 <a href="/pricing" class="landing-hero-freenote-link">
142 See pricing &rarr;
143 </a>
144 </p>
145
958d26aClaude146 <p class="landing-hero-caption">
147 Already have a repo?
148 <span class="landing-hero-cmd">
149 <span class="kbd">git</span>
150 <span class="kbd">remote</span>
151 <span class="kbd">add</span>
152 <span class="kbd">gluecron</span>
153 <span class="landing-hero-arrow">{"→"}</span>
154 <span class="kbd">git push</span>
155 </span>
156 </p>
157
158 {hasStats && (
159 <p class="landing-stats">
160 {stats!.publicRepos !== undefined && stats!.publicRepos > 0 && (
161 <span>
162 <strong>{stats!.publicRepos.toLocaleString()}</strong>
163 {stats!.publicRepos === 1 ? " repo" : " repos"}
164 </span>
165 )}
166 {stats!.publicRepos !== undefined &&
167 stats!.publicRepos > 0 &&
168 stats!.users !== undefined &&
169 stats!.users > 0 && <span class="landing-stats-sep">·</span>}
170 {stats!.users !== undefined && stats!.users > 0 && (
171 <span>
172 <strong>{stats!.users.toLocaleString()}</strong>
173 {stats!.users === 1 ? " developer" : " developers"}
174 </span>
175 )}
176 <span class="landing-stats-sep">·</span>
4c47454Claude177 <span>
958d26aClaude178 <strong>100%</strong> AI-native
4c47454Claude179 </span>
958d26aClaude180 </p>
181 )}
182 </div>
c963db5Claude183 </section>
c475ee6Claude184
52ad8b1Claude185 {/* ---------- L4 social-proof counters (animated count-up) ---------- */}
186 {tiles && (
187 <section class="landing-counters" aria-label="Gluecron live counters">
188 <div class="landing-counters-grid">
189 {tiles.map((t) => (
190 <div class="landing-counter">
191 <div
192 class="landing-counter-num"
193 data-counter-target={String(t.value)}
194 data-counter-suffix={t.suffix ?? ""}
195 data-counter-prefix={t.prefix ?? ""}
196 >
197 {t.prefix ?? ""}
198 {t.value.toLocaleString()}
199 {t.suffix ?? ""}
200 </div>
201 <div class="landing-counter-label">{t.label}</div>
202 </div>
203 ))}
204 </div>
205 <script dangerouslySetInnerHTML={{ __html: landingCountersJs }} />
206 </section>
207 )}
208
5f2e749Claude209 {/* ---------- L10 — Three reasons to switch ---------- */}
210 <section class="landing-section landing-reasons" aria-label="Three reasons to switch">
211 <div class="section-header">
212 <div class="eyebrow">Three reasons to switch</div>
213 <h2>Built so Claude can do the work.</h2>
214 </div>
215 <div class="landing-reasons-grid">
216 <ReasonCard
217 icon={
218 <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">
219 <path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z" />
220 </svg>
221 }
222 title="Toggle Sleep Mode"
223 body="Claude does the work overnight. You get a 9 AM digest of what shipped — PRs merged, deploys live, incidents triaged."
224 link={{ href: "/sleep-mode", label: "Turn on Sleep Mode" }}
225 />
226 <ReasonCard
227 icon={
228 <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">
229 <polyline points="4 17 10 11 4 5" />
230 <line x1="12" y1="19" x2="20" y2="19" />
231 </svg>
232 }
233 title="One command to migrate"
234 body="Drop a single curl into your shell. Gluecron rehosts your repo, your issues, your branches — no SaaS rip-and-replace project required."
235 extra={
236 <code class="landing-reasons-code">curl -sSL gluecron.com/install | bash</code>
237 }
238 link={{ href: "/import", label: "Or import from GitHub" }}
239 />
240 <ReasonCard
241 icon={
242 <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">
243 <polygon points="5 3 19 12 5 21 5 3" />
244 </svg>
245 }
246 title="Open the demo, watch it work"
247 body="The demo repo is real. Label an issue, hit refresh, see Claude open the PR. Inspect the diff. Approve the merge. Zero setup, zero credit card."
248 link={{ href: "/demo", label: "Open the live demo" }}
249 />
250 </div>
251 </section>
252
c963db5Claude253 {/* ---------- Capability strip — uppercase tracked grid (crontech-style) ---------- */}
254 <section class="landing-caps">
255 <div class="landing-caps-grid">
256 <span class="landing-cap">Claude-powered AI</span>
257 <span class="landing-cap">Spec-to-PR</span>
258 <span class="landing-cap">Auto-repair</span>
259 <span class="landing-cap">Real-time gates</span>
260 <span class="landing-cap">MCP-native</span>
261 <span class="landing-cap">Workflow runner</span>
262 <span class="landing-cap">Self-hostable</span>
263 <span class="landing-cap">Branch protection</span>
264 <span class="landing-cap">Bun + Hono</span>
265 <span class="landing-cap">Drizzle + Postgres</span>
266 <span class="landing-cap">JSX server-rendered</span>
267 <span class="landing-cap">Type-safe end to end</span>
c475ee6Claude268 </div>
958d26aClaude269 </section>
270
c963db5Claude271 {/* ---------- Big stat row (crontech-style hero closer) ---------- */}
272 <section class="landing-bigstats">
273 <div class="landing-bigstats-grid">
274 <div class="landing-bigstat">
275 <div class="landing-bigstat-num">Claude-powered</div>
276 <div class="landing-bigstat-label">The best AI, native</div>
277 </div>
278 <div class="landing-bigstat">
279 <div class="landing-bigstat-num">Self-hosted</div>
280 <div class="landing-bigstat-label">On your hardware</div>
281 </div>
282 <div class="landing-bigstat">
283 <div class="landing-bigstat-num">MCP-native</div>
284 <div class="landing-bigstat-label">Claude · Cursor · Code</div>
285 </div>
286 <div class="landing-bigstat">
287 <div class="landing-bigstat-num">Real-time</div>
288 <div class="landing-bigstat-label">SSE everywhere</div>
289 </div>
958d26aClaude290 </div>
4c47454Claude291 </section>
292
293 {/* ---------- Feature grid ---------- */}
958d26aClaude294 <section class="landing-section">
295 <div class="section-header">
296 <div class="eyebrow">The platform</div>
297 <h2>An IDE for your repo, not just a host.</h2>
298 <p>
299 Gluecron ships the surfaces GitHub charges extra for, and the
300 ones it never built. AI is a teammate with its own commits, not
301 a sidebar.
2b821b7Claude302 </p>
303 </div>
4c47454Claude304
958d26aClaude305 <div class="landing-features stagger">
306 <FeatureCard
307 icon={
308 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
309 <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" />
310 </svg>
311 }
312 title="AI as a teammate"
313 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."
314 />
315 <FeatureCard
316 icon={
317 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
318 <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" />
319 <path d="M9 12l2 2 4-4" />
320 </svg>
321 }
322 title="Quality gate that learns"
323 desc="GateTest scans every push. Auto-repair fixes regressions before you see them. Required checks block bad PRs from merging. Your software self-corrects."
324 />
325 <FeatureCard
326 icon={
327 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
328 <path d="M13 2L4 14h7l-1 8 9-12h-7z" />
329 </svg>
330 }
331 title="Real-time everything"
332 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."
333 />
334 <FeatureCard
335 icon={
336 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
337 <rect x="3" y="3" width="18" height="18" rx="3" />
338 <path d="M3 9h18M9 21V9" />
339 </svg>
340 }
341 title="Workflow runner"
342 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."
343 />
344 <FeatureCard
345 icon={
346 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
347 <circle cx="12" cy="12" r="9" />
348 <path d="M3 12h18M12 3a14 14 0 010 18M12 3a14 14 0 000 18" />
349 </svg>
350 }
351 title="MCP-native"
352 desc="Claude, Cursor, Code — they speak Model Context Protocol. Gluecron exposes search, file read, issues, codebase explain as MCP tools by default."
353 />
354 <FeatureCard
355 icon={
356 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
357 <path d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
358 <path d="M12 7v5l3 2" />
359 </svg>
360 }
361 title="Yours, on your hardware"
362 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."
363 />
2b821b7Claude364 </div>
958d26aClaude365 </section>
2b821b7Claude366
958d26aClaude367 {/* ---------- Workflow walkthrough ---------- */}
368 <section class="landing-section landing-walk">
369 <div class="section-header">
370 <div class="eyebrow">How it works</div>
371 <h2>Push code. Watch it ship.</h2>
372 <p>
373 Every push triggers the same pipeline whether the commit came
374 from you, from CI, or from an AI agent.
4c47454Claude375 </p>
376 </div>
958d26aClaude377
378 <div class="landing-walk-grid">
379 <WalkStep n="01" title="Push" desc="git push to gluecron — Smart-HTTP, SSH, or via the web editor." />
380 <WalkStep n="02" title="Gate" desc="GateTest runs. Secret scanner runs. AI security review posts inline comments." />
381 <WalkStep n="03" title="Repair" desc="If a gate fails, auto-repair tries to fix it. New commit gets re-gated." />
382 <WalkStep n="04" title="Ship" desc="Green push to default branch fires deploy webhook. Crontech, Fly, your prod." />
383 </div>
4c47454Claude384 </section>
2b821b7Claude385
4c47454Claude386 {/* ---------- Terminal block ---------- */}
958d26aClaude387 <section class="landing-section landing-terminal-section">
388 <div class="landing-terminal-wrap">
389 <div class="landing-terminal" role="img" aria-label="Example git push to gluecron with passing gates">
390 <div class="landing-terminal-chrome">
391 <span class="landing-terminal-dot landing-terminal-dot-r" />
392 <span class="landing-terminal-dot landing-terminal-dot-y" />
393 <span class="landing-terminal-dot landing-terminal-dot-g" />
394 <span class="landing-terminal-title">~/your-repo &mdash; zsh</span>
395 </div>
396 <div class="landing-terminal-body">
397 <div class="landing-term-line">
398 <span class="landing-term-prompt">$</span>
399 <span>git remote add gluecron https://gluecron.com/you/your-repo.git</span>
400 </div>
401 <div class="landing-term-line">
402 <span class="landing-term-prompt">$</span>
403 <span>git push -u gluecron main</span>
404 </div>
405 <div class="landing-term-line landing-term-out">
406 <span class="landing-term-meta">remote:</span>
407 <span>Resolving deltas… 100% (24/24)</span>
408 </div>
409 <div class="landing-term-line landing-term-out landing-term-ok-line">
410 <span class="landing-term-ok">{"✓"}</span>
411 <span>pushed to gluecron.com/you/your-repo</span>
412 </div>
413 <div class="landing-term-line landing-term-out landing-term-ok-line">
414 <span class="landing-term-ok">{"✓"}</span>
415 <span>GateTest passed (12 rules, 0 violations)</span>
416 </div>
417 <div class="landing-term-line landing-term-out landing-term-ok-line">
418 <span class="landing-term-ok">{"✓"}</span>
419 <span>AI review posted (2 suggestions, 0 blockers)</span>
420 </div>
421 <div class="landing-term-line landing-term-out landing-term-ok-line">
422 <span class="landing-term-ok">{"✓"}</span>
423 <span>deployed to your-repo.gluecron.com <span class="landing-term-meta">(4.1s)</span></span>
424 </div>
425 <div class="landing-term-line landing-term-cursor">
426 <span class="landing-term-prompt">$</span>
427 <span class="landing-term-blink">▍</span>
428 </div>
429 </div>
4c47454Claude430 </div>
958d26aClaude431 </div>
432 </section>
433
434 {/* ---------- Comparison ---------- */}
435 <section class="landing-section">
436 <div class="section-header">
437 <div class="eyebrow">vs the incumbent</div>
438 <h2>Everything GitHub charges for. And the parts they didn't build.</h2>
439 </div>
440
441 <div class="landing-compare">
442 <CompareRow feature="Git hosting + Smart-HTTP push" them="✓" us="✓" />
443 <CompareRow feature="Issues, PRs, code review" them="✓" us="✓" />
444 <CompareRow feature="Workflow runner (Actions-equivalent)" them="paid minutes" us="self-hosted, unmetered" highlight />
445 <CompareRow feature="AI code review on every PR" them="Copilot subscription" us="built in" highlight />
446 <CompareRow feature="Spec-to-PR (NL feature → draft PR)" them="—" us="✓" highlight />
447 <CompareRow feature="Auto-repair on failed gates" them="—" us="✓" highlight />
448 <CompareRow feature="Real-time SSE for logs + PRs" them="polling" us="streaming" highlight />
449 <CompareRow feature="MCP server (Claude / Cursor)" them="—" us="✓" highlight />
450 <CompareRow feature="Self-host on your own infra" them="enterprise tier" us="single binary" highlight />
451 <CompareRow feature="Pre-receive policy enforcement" them="rulesets (GHE)" us="✓" />
452 </div>
453 </section>
454
455 {/* ---------- Pricing teaser ---------- */}
456 <section class="landing-section">
457 <div class="section-header">
458 <div class="eyebrow">Pricing</div>
459 <h2>Free to start. Honest at scale.</h2>
460 <p>
461 Self-hosting is free forever. Hosted plans price the AI calls,
462 not the seats.
463 </p>
464 </div>
465
466 <div class="landing-pricing">
467 <PricingCard
468 tier="Free"
469 price="$0"
470 cadence="forever"
471 desc="For personal projects + open source. Public + private repos, full AI suite, fair quotas."
472 features={["Unlimited public repos", "3 private repos", "5K AI calls / mo", "Community support"]}
473 cta="Start free"
474 href="/register"
475 />
476 <PricingCard
477 tier="Pro"
478 price="$12"
479 cadence="per user / mo"
480 desc="For working developers. Lifts every quota, adds priority routing, no Gluecron branding on deploys."
481 features={["Unlimited private repos", "100K AI calls / mo", "Priority queue", "Custom domains"]}
482 cta="Go Pro"
483 href="/settings/billing"
484 highlight
485 />
486 <PricingCard
487 tier="Team"
488 price="Talk to us"
489 cadence="custom"
490 desc="For orgs running production on Gluecron. SSO, audit retention, enterprise SLA, on-prem."
491 features={["SSO + SCIM", "On-prem deploy", "Dedicated capacity", "24/7 incident response"]}
492 cta="Contact"
493 href="mailto:hello@gluecron.com"
494 />
495 </div>
496 </section>
497
5f2e749Claude498 {/* ---------- L10 — "How is this different?" pull-quote ---------- */}
499 <section class="landing-pullquote-section" aria-label="How is this different from GitHub?">
500 <figure class="landing-pullquote">
501 <div class="landing-pullquote-eyebrow">How is this different from GitHub?</div>
502 <blockquote class="landing-pullquote-text">
503 Every other host bolts AI on as a sidecar. Gluecron is the first
504 git host where Claude is a first-class developer. Built to be
505 operated by AI agents, not just augmented by them.
506 </blockquote>
507 <a href="/vs-github" class="landing-pullquote-link">
508 See the full comparison
509 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
510 </a>
511 </figure>
512 </section>
513
958d26aClaude514 {/* ---------- Closing CTA ---------- */}
515 <section class="landing-cta-section">
516 <div class="landing-cta-card">
517 <div class="landing-cta-bg" aria-hidden="true" />
518 <div class="eyebrow">Ready when you are</div>
519 <h2 class="landing-cta-title">
520 Stop maintaining the platform.<br />
521 <span class="gradient-text">Start shipping the product.</span>
522 </h2>
523 <p class="landing-cta-sub">
524 Free to start, self-hosted-friendly, MCP-native. Migrate from
525 GitHub in one click.
526 </p>
527 <div class="landing-cta-buttons">
528 <a href="/register" class="btn btn-primary btn-xl">
529 Create your account
530 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
531 </a>
532 <a href="/import" class="btn btn-ghost btn-xl">
533 Migrate a repo
534 </a>
4c47454Claude535 </div>
536 </div>
537 </section>
5f2e749Claude538
539 {/* L10 — clipboard copy script for the hero install snippet. */}
540 <script dangerouslySetInnerHTML={{ __html: landingCopyJs }} />
4c47454Claude541 </div>
2b821b7Claude542 </>
543 );
544};
545
958d26aClaude546const FeatureCard: FC<{ icon: any; title: string; desc: string }> = ({
547 icon,
548 title,
549 desc,
550}) => (
551 <div class="landing-feature">
552 <div class="landing-feature-icon" aria-hidden="true">
553 {icon}
554 </div>
555 <h3 class="landing-feature-title">{title}</h3>
556 <p class="landing-feature-desc">{desc}</p>
557 </div>
558);
559
5f2e749Claude560// Block L10 — "Three reasons to switch" column.
561const ReasonCard: FC<{
562 icon: any;
563 title: string;
564 body: string;
565 link: { href: string; label: string };
566 extra?: any;
567}> = ({ icon, title, body, link, extra }) => (
568 <div class="landing-reason">
569 <div class="landing-reason-icon" aria-hidden="true">
570 {icon}
571 </div>
572 <h3 class="landing-reason-title">{title}</h3>
573 <p class="landing-reason-body">{body}</p>
574 {extra}
575 <a href={link.href} class="landing-reason-link">
576 {link.label}
577 <span class="landing-cta-arrow" aria-hidden="true">{"→"}</span>
578 </a>
579 </div>
580);
581
958d26aClaude582const WalkStep: FC<{ n: string; title: string; desc: string }> = ({
583 n,
584 title,
585 desc,
586}) => (
587 <div class="landing-walk-step">
588 <div class="landing-walk-num">{n}</div>
589 <h3 class="landing-walk-title">{title}</h3>
590 <p class="landing-walk-desc">{desc}</p>
591 </div>
592);
593
594const CompareRow: FC<{
595 feature: string;
596 them: string;
597 us: string;
598 highlight?: boolean;
599}> = ({ feature, them, us, highlight }) => (
600 <div class={`landing-compare-row${highlight ? " landing-compare-hl" : ""}`}>
601 <div class="landing-compare-feature">{feature}</div>
602 <div class="landing-compare-them">{them}</div>
603 <div class="landing-compare-us">{us === "✓" ? "✓" : us}</div>
604 </div>
605);
606
607const PricingCard: FC<{
608 tier: string;
609 price: string;
610 cadence: string;
611 desc: string;
612 features: string[];
613 cta: string;
614 href: string;
615 highlight?: boolean;
616}> = ({ tier, price, cadence, desc, features, cta, href, highlight }) => (
617 <div class={`landing-price-card${highlight ? " landing-price-hl" : ""}`}>
618 {highlight && <div class="landing-price-badge">Most popular</div>}
619 <div class="landing-price-tier">{tier}</div>
620 <div class="landing-price-amount">
621 <span class="landing-price-num">{price}</span>
622 <span class="landing-price-cad">{cadence}</span>
623 </div>
624 <p class="landing-price-desc">{desc}</p>
625 <ul class="landing-price-features">
626 {features.map((f) => (
627 <li>
628 <span class="landing-price-check" aria-hidden="true">{"✓"}</span>
629 {f}
630 </li>
631 ))}
632 </ul>
633 <a
634 href={href}
635 class={`btn ${highlight ? "btn-primary" : "btn-secondary"} btn-block landing-price-cta`}
636 >
637 {cta}
638 </a>
639 </div>
640);
641
52ad8b1Claude642// ─────────────────────────────────────────────────────────────────
643// Block L4 — social-proof tile builder.
644//
645// Pure: takes the cached PublicStats payload and emits the six
646// landing-page tiles in render order. Exported so tests / future
647// surfaces (dashboard, /about, …) can share the exact same copy.
648// ─────────────────────────────────────────────────────────────────
649
650export interface SocialProofTile {
651 label: string;
652 value: number;
653 prefix?: string;
654 suffix?: string;
655}
656
657export function buildSocialProofTiles(s: PublicStats): SocialProofTile[] {
658 return [
659 { label: "Public repos", value: s.totalPublicRepos },
660 { label: "Developers", value: s.totalUsers },
661 {
662 label: "PRs auto-merged this week",
663 value: s.weeklyPrsAutoMerged,
664 },
665 {
666 label: "Issues built by AI this week",
667 value: s.weeklyIssuesBuiltByAi,
668 },
669 {
670 label: "Deploys shipped this week",
671 value: s.weeklyDeploysShipped,
672 },
673 {
674 label: "Hours saved this week",
675 // Round to whole hours for the tile — the precise 0.1 figure
676 // lives on the dashboard widget; the marketing surface keeps
677 // the number scannable.
678 value: Math.round(s.weeklyHoursSaved),
679 prefix: "~",
680 suffix: "h",
681 },
682 ];
683}
684
4c47454Claude685// Backwards-compatible default — web.tsx imports `LandingPage`.
686export const LandingPage: FC<LandingPageProps> = (props) => (
687 <LandingHero {...props} />
2b821b7Claude688);
689
4c47454Claude690export default LandingPage;
691
2b821b7Claude692const landingCss = `
958d26aClaude693 /* ============================================================ */
694 /* Landing — Editorial-Technical 2026.05 */
695 /* ============================================================ */
4c47454Claude696 .landing-root {
697 position: relative;
958d26aClaude698 max-width: 1180px;
4c47454Claude699 margin: 0 auto;
700 padding: 0 16px;
958d26aClaude701 }
702 .landing-root > section { position: relative; }
703
704 /* ---------- Hero ---------- */
705 .landing-hero {
706 position: relative;
c475ee6Claude707 padding: var(--s-16) 0 var(--s-20);
958d26aClaude708 text-align: center;
4c47454Claude709 overflow: hidden;
710 }
c475ee6Claude711 .landing-hero-blob-1 {
712 animation: hero-blob-drift-1 18s var(--ease, ease) infinite alternate;
713 }
714 .landing-hero-blob-2 {
715 animation: hero-blob-drift-2 22s var(--ease, ease) infinite alternate;
716 }
717 @keyframes hero-blob-drift-1 {
718 0% { transform: translate(0, 0) scale(1); opacity: 0.55; }
719 100% { transform: translate(8%, 6%) scale(1.18); opacity: 0.75; }
720 }
721 @keyframes hero-blob-drift-2 {
722 0% { transform: translate(0, 0) scale(1); opacity: 0.40; }
723 100% { transform: translate(-10%, -4%) scale(1.25); opacity: 0.60; }
724 }
725
726 /* ---------- Hero product visual: live AI PR review card ---------- */
727 .landing-hero-visual {
728 position: relative;
729 max-width: 760px;
730 margin: var(--s-12) auto 0;
731 padding: 0 16px;
732 perspective: 1400px;
733 z-index: 2;
734 opacity: 0;
735 animation: hero-visual-in 700ms var(--ease-out-expo, cubic-bezier(0.19, 1, 0.22, 1)) 400ms forwards;
736 }
737 @keyframes hero-visual-in {
738 from { opacity: 0; transform: translateY(20px); }
739 to { opacity: 1; transform: translateY(0); }
740 }
741 .hero-pr-card {
742 position: relative;
743 background: linear-gradient(180deg, rgba(15,17,26,0.96) 0%, rgba(8,9,15,0.96) 100%);
744 border: 1px solid var(--border-strong);
745 border-radius: var(--r-xl);
746 overflow: hidden;
747 text-align: left;
748 box-shadow:
749 0 30px 80px -20px rgba(0,0,0,0.65),
750 0 0 0 1px rgba(140,109,255,0.18),
751 0 0 60px -10px rgba(140,109,255,0.30);
752 transform: rotateX(2deg) rotateY(-2deg);
753 transition: transform 600ms var(--ease, ease);
754 backdrop-filter: blur(12px);
755 -webkit-backdrop-filter: blur(12px);
756 }
757 .landing-hero-visual:hover .hero-pr-card {
758 transform: rotateX(0deg) rotateY(0deg);
759 }
760 .hero-pr-card::before {
761 content: '';
762 position: absolute;
763 inset: 0;
764 background: linear-gradient(135deg, rgba(140,109,255,0.10), transparent 35%, transparent 65%, rgba(54,197,214,0.08));
765 pointer-events: none;
766 }
767 .hero-pr-header {
768 display: flex;
769 align-items: center;
770 gap: 12px;
771 padding: 14px 18px;
772 border-bottom: 1px solid rgba(255,255,255,0.06);
773 background: rgba(255,255,255,0.025);
774 font-size: 13px;
775 }
776 .hero-pr-dot {
777 width: 10px; height: 10px;
778 border-radius: 50%;
779 background: var(--green);
780 box-shadow: 0 0 10px rgba(52,211,153,0.6);
781 flex-shrink: 0;
782 }
783 .hero-pr-title {
784 color: var(--text-strong);
785 font-weight: 600;
786 flex: 1;
787 overflow: hidden;
788 text-overflow: ellipsis;
789 white-space: nowrap;
790 }
791 .hero-pr-num {
792 color: var(--text-faint);
793 font-family: var(--font-mono);
794 font-weight: 500;
795 margin-right: 8px;
796 }
797 .hero-pr-status {
798 display: inline-flex;
799 align-items: center;
800 gap: 6px;
801 padding: 3px 10px;
802 border-radius: var(--r-full);
803 background: var(--accent-gradient-faint);
804 border: 1px solid rgba(140,109,255,0.30);
805 color: var(--accent);
806 font-family: var(--font-mono);
807 font-size: 11px;
808 letter-spacing: 0.04em;
809 flex-shrink: 0;
810 }
811 .hero-pr-status-pulse {
812 width: 6px; height: 6px;
813 border-radius: 50%;
814 background: var(--accent);
815 box-shadow: 0 0 0 0 rgba(140,109,255,0.6);
816 animation: hero-pulse 1.6s ease-out infinite;
817 }
818 @keyframes hero-pulse {
819 0% { box-shadow: 0 0 0 0 rgba(140,109,255,0.55); }
820 70% { box-shadow: 0 0 0 8px rgba(140,109,255,0); }
821 100% { box-shadow: 0 0 0 0 rgba(140,109,255,0); }
822 }
823
824 .hero-pr-body {
825 padding: 0;
826 }
827 .hero-pr-file {
828 display: flex;
829 align-items: center;
830 gap: 10px;
831 padding: 10px 18px;
832 border-bottom: 1px solid rgba(255,255,255,0.05);
833 font-family: var(--font-mono);
834 font-size: 12px;
835 background: rgba(255,255,255,0.012);
836 }
837 .hero-pr-file-icon { color: var(--accent-2); }
838 .hero-pr-file-name { color: var(--text); flex: 1; }
839 .hero-pr-file-stats { display: inline-flex; gap: 8px; }
840 .hero-pr-add { color: var(--green); font-weight: 600; }
841 .hero-pr-del { color: var(--red); font-weight: 600; }
842
843 .hero-pr-diff {
844 padding: 12px 18px;
845 font-family: var(--font-mono);
846 font-feature-settings: var(--mono-feat, 'calt');
847 font-size: 12.5px;
848 line-height: 1.7;
849 color: rgba(237,237,242,0.85);
850 overflow-x: auto;
851 }
852 .hero-pr-hunk {
853 color: rgba(140,109,255,0.85);
854 background: rgba(140,109,255,0.06);
855 padding: 2px 8px;
856 margin: 0 -8px 4px;
857 border-radius: 4px;
858 }
859 .hero-pr-line-add {
860 background: rgba(52,211,153,0.08);
861 color: rgba(167,243,208,0.95);
862 padding: 0 8px;
863 margin: 0 -8px;
864 border-left: 2px solid var(--green);
865 padding-left: 8px;
866 }
867
868 .hero-pr-comment {
869 margin: 14px 18px;
870 padding: 14px 16px;
871 background: linear-gradient(135deg, rgba(140,109,255,0.08), rgba(54,197,214,0.05));
872 border: 1px solid rgba(140,109,255,0.25);
873 border-radius: var(--r-md);
874 }
875 .hero-pr-bot-row {
876 display: flex;
877 align-items: center;
878 gap: 8px;
879 margin-bottom: 8px;
880 font-size: 12px;
881 }
882 .hero-pr-bot-avatar {
883 width: 22px; height: 22px;
884 display: inline-flex;
885 align-items: center;
886 justify-content: center;
887 border-radius: 50%;
888 background: var(--accent-gradient);
889 font-size: 11px;
890 box-shadow: 0 0 12px rgba(140,109,255,0.40);
891 }
892 .hero-pr-bot-name {
893 color: var(--text-strong);
894 font-weight: 600;
895 }
896 .hero-pr-bot-meta {
897 color: var(--text-faint);
898 font-family: var(--font-mono);
899 }
900 .hero-pr-bot-text {
901 color: var(--text);
902 font-size: 13px;
903 line-height: 1.55;
904 margin: 0;
905 }
906 .hero-pr-bot-text code {
907 background: rgba(255,255,255,0.06);
908 border: 1px solid rgba(255,255,255,0.10);
909 padding: 1px 6px;
910 border-radius: 4px;
911 font-size: 11.5px;
912 color: var(--accent);
913 }
914 .hero-pr-bot-link { color: var(--accent-2); text-decoration: underline; text-decoration-style: dotted; }
915
916 .hero-pr-gates {
917 display: flex;
918 flex-wrap: wrap;
919 gap: 6px;
920 padding: 12px 18px 16px;
921 border-top: 1px solid rgba(255,255,255,0.06);
922 background: rgba(255,255,255,0.012);
923 }
924 .hero-pr-gate {
925 display: inline-flex;
926 align-items: center;
927 gap: 6px;
928 padding: 4px 10px;
929 border-radius: var(--r-full);
930 font-family: var(--font-mono);
931 font-size: 11px;
932 border: 1px solid;
933 }
934 .hero-pr-gate-pass {
935 color: var(--green);
936 background: rgba(52,211,153,0.08);
937 border-color: rgba(52,211,153,0.30);
938 }
939 .hero-pr-gate-running {
940 color: var(--accent);
941 background: var(--accent-gradient-faint);
942 border-color: rgba(140,109,255,0.40);
943 }
944 .hero-pr-gate-spin {
945 width: 9px; height: 9px;
946 border: 1.5px solid rgba(140,109,255,0.30);
947 border-top-color: var(--accent);
948 border-radius: 50%;
949 animation: hero-spin 800ms linear infinite;
950 }
951 @keyframes hero-spin {
952 to { transform: rotate(360deg); }
953 }
954
955 /* Floating accent badges around the card */
956 .hero-float {
957 position: absolute;
958 display: inline-flex;
959 align-items: center;
960 gap: 6px;
961 padding: 6px 12px;
962 background: rgba(15,17,26,0.92);
963 border: 1px solid rgba(140,109,255,0.35);
964 border-radius: var(--r-full);
965 font-family: var(--font-mono);
966 font-size: 11px;
967 color: var(--text);
968 box-shadow: 0 12px 24px -8px rgba(0,0,0,0.5), 0 0 18px -4px rgba(140,109,255,0.30);
969 backdrop-filter: blur(8px);
970 -webkit-backdrop-filter: blur(8px);
971 }
972 .hero-float-icon { color: var(--accent); }
973 .hero-float-1 {
974 top: -14px;
975 left: -8px;
976 animation: hero-float-bob-1 5s var(--ease, ease) infinite alternate;
977 }
978 .hero-float-2 {
979 bottom: -14px;
980 right: -8px;
981 animation: hero-float-bob-2 6s var(--ease, ease) infinite alternate;
982 }
983 @keyframes hero-float-bob-1 {
984 from { transform: translate(0, 0); }
985 to { transform: translate(-8px, -10px); }
986 }
987 @keyframes hero-float-bob-2 {
988 from { transform: translate(0, 0); }
989 to { transform: translate(8px, 8px); }
990 }
991
992 @media (max-width: 720px) {
993 .landing-hero-visual { padding: 0 8px; }
994 .hero-pr-card { transform: none; }
995 .hero-pr-title { font-size: 12px; }
996 .hero-pr-diff { font-size: 11px; line-height: 1.6; }
997 .hero-float { display: none; }
998 }
958d26aClaude999 .landing-hero-bg {
1000 position: absolute;
1001 inset: -10% -20%;
1002 pointer-events: none;
1003 z-index: 0;
4c47454Claude1004 }
958d26aClaude1005 .landing-hero-blob {
1006 position: absolute;
1007 border-radius: 50%;
1008 filter: blur(80px);
1009 opacity: 0.55;
1010 }
1011 .landing-hero-blob-1 {
1012 top: -10%;
1013 left: 30%;
1014 width: 480px;
1015 height: 480px;
1016 background: radial-gradient(circle, rgba(140,109,255,0.55), transparent 65%);
1017 }
1018 .landing-hero-blob-2 {
1019 top: 10%;
1020 left: 50%;
1021 width: 380px;
1022 height: 380px;
1023 background: radial-gradient(circle, rgba(54,197,214,0.40), transparent 65%);
1024 }
1025 .landing-hero-grid {
4c47454Claude1026 position: absolute;
1027 inset: 0;
958d26aClaude1028 background-image:
1029 linear-gradient(to right, rgba(255,255,255,0.04) 1px, transparent 1px),
1030 linear-gradient(to bottom, rgba(255,255,255,0.04) 1px, transparent 1px);
1031 background-size: 60px 60px;
1032 mask-image: radial-gradient(ellipse 50% 50% at 50% 30%, #000 0%, transparent 75%);
1033 -webkit-mask-image: radial-gradient(ellipse 50% 50% at 50% 30%, #000 0%, transparent 75%);
1034 }
1035 :root[data-theme='light'] .landing-hero-grid {
1036 background-image:
1037 linear-gradient(to right, rgba(15,16,28,0.06) 1px, transparent 1px),
1038 linear-gradient(to bottom, rgba(15,16,28,0.06) 1px, transparent 1px);
4c47454Claude1039 }
1040
958d26aClaude1041 .landing-hero-inner {
1042 position: relative;
1043 z-index: 1;
1044 max-width: 960px;
2b821b7Claude1045 margin: 0 auto;
1046 }
958d26aClaude1047 .landing-hero-eyebrow {
1048 margin: 0 auto var(--s-6);
1049 color: var(--accent);
1050 }
1051 .landing-hero-eyebrow::before { display: none; }
1052 .landing-hero-pulse {
1053 width: 7px;
1054 height: 7px;
1055 border-radius: 50%;
1056 background: var(--accent);
1057 box-shadow: 0 0 0 0 rgba(140,109,255,0.6);
1058 animation: pulse 1.8s ease-out infinite;
1059 flex-shrink: 0;
1060 }
1061 @keyframes pulse {
1062 0% { box-shadow: 0 0 0 0 rgba(140,109,255,0.55); }
1063 70% { box-shadow: 0 0 0 10px rgba(140,109,255,0); }
1064 100% { box-shadow: 0 0 0 0 rgba(140,109,255,0); }
1065 }
1066
2b821b7Claude1067 .landing-hero-title {
c963db5Claude1068 font-size: clamp(48px, 9.5vw, 124px);
1069 line-height: 0.96;
1070 letter-spacing: -0.045em;
1071 font-weight: 700;
1072 margin: 0 0 var(--s-7);
958d26aClaude1073 color: var(--text-strong);
2b821b7Claude1074 }
c963db5Claude1075 .landing-hero-title .gradient-text {
1076 background-image: linear-gradient(135deg, #a48bff 0%, #8c6dff 50%, #6d4dff 100%);
1077 -webkit-background-clip: text;
1078 background-clip: text;
1079 -webkit-text-fill-color: transparent;
1080 color: transparent;
1081 }
958d26aClaude1082
2b821b7Claude1083 .landing-hero-sub {
958d26aClaude1084 font-size: clamp(15px, 1.6vw, 19px);
2b821b7Claude1085 color: var(--text-muted);
958d26aClaude1086 max-width: 680px;
1087 margin: 0 auto;
4c47454Claude1088 line-height: 1.55;
958d26aClaude1089 letter-spacing: -0.005em;
2b821b7Claude1090 }
4c47454Claude1091
2b821b7Claude1092 .landing-hero-ctas {
1093 display: flex;
958d26aClaude1094 gap: 12px;
2b821b7Claude1095 justify-content: center;
1096 flex-wrap: wrap;
958d26aClaude1097 margin-top: var(--s-10);
2b821b7Claude1098 }
958d26aClaude1099 .landing-cta-arrow {
1100 transition: transform var(--t-base) var(--ease-spring);
1101 display: inline-block;
2b821b7Claude1102 }
958d26aClaude1103 .btn:hover .landing-cta-arrow,
1104 .landing-cta-primary:hover .landing-cta-arrow {
1105 transform: translateX(4px);
8e9f1d9Claude1106 }
2b821b7Claude1107
5f2e749Claude1108 /* L8 — free-tier reassurance link beneath the CTA row. */
1109 .landing-hero-freenote {
1110 margin-top: var(--s-5);
1111 font-size: var(--t-sm);
1112 color: var(--text-muted);
1113 text-align: center;
1114 }
1115 .landing-hero-freenote-link {
1116 color: var(--accent);
1117 text-decoration: none;
1118 font-weight: 500;
1119 border-bottom: 1px dotted rgba(140,109,255,0.4);
1120 transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
1121 }
1122 .landing-hero-freenote-link:hover {
1123 color: var(--text-strong);
1124 border-bottom-color: var(--accent);
1125 }
1126
4c47454Claude1127 .landing-hero-caption {
958d26aClaude1128 margin-top: var(--s-8);
1129 font-size: var(--t-sm);
4c47454Claude1130 color: var(--text-muted);
958d26aClaude1131 display: flex;
1132 flex-wrap: wrap;
1133 align-items: center;
1134 justify-content: center;
1135 gap: 12px;
2b821b7Claude1136 }
958d26aClaude1137 .landing-hero-cmd {
1138 display: inline-flex;
1139 align-items: center;
1140 gap: 4px;
1141 padding: 6px 12px;
1142 background: var(--bg-elevated);
4c47454Claude1143 border: 1px solid var(--border);
958d26aClaude1144 border-radius: var(--r-full);
1145 box-shadow: var(--elev-1);
1146 }
1147 .landing-hero-cmd .kbd {
1148 border: 0;
1149 background: transparent;
1150 padding: 0 4px;
1151 color: var(--text-muted);
1152 font-size: 12px;
1153 }
1154 .landing-hero-cmd .kbd:nth-last-of-type(1) { color: var(--accent); }
1155 .landing-hero-arrow {
1156 color: var(--text-faint);
1157 font-size: 13px;
1158 margin: 0 2px;
2b821b7Claude1159 }
4c47454Claude1160
1161 .landing-stats {
958d26aClaude1162 margin-top: var(--s-7);
1163 font-family: var(--font-mono);
1164 font-size: 12px;
1165 color: var(--text-muted);
1166 display: flex;
1167 align-items: center;
1168 justify-content: center;
1169 gap: 10px;
1170 flex-wrap: wrap;
1171 letter-spacing: 0.02em;
1172 }
1173 .landing-stats strong {
1174 color: var(--text-strong);
1175 font-weight: 600;
1176 font-feature-settings: 'tnum';
1177 }
1178 .landing-stats-sep { opacity: 0.4; }
1179
c963db5Claude1180 /* ---------- Capability grid (crontech-style uppercase tracked) ---------- */
1181 .landing-caps {
1182 margin: var(--s-12) auto var(--s-16);
1183 max-width: 1080px;
1184 padding: var(--s-7) var(--s-4);
958d26aClaude1185 border-top: 1px solid var(--border-subtle);
1186 border-bottom: 1px solid var(--border-subtle);
c963db5Claude1187 }
1188 .landing-caps-grid {
1189 display: grid;
1190 grid-template-columns: repeat(4, 1fr);
1191 gap: 24px 16px;
958d26aClaude1192 text-align: center;
1193 }
c963db5Claude1194 .landing-cap {
958d26aClaude1195 font-family: var(--font-mono);
1196 font-size: 11px;
c963db5Claude1197 font-weight: 600;
958d26aClaude1198 text-transform: uppercase;
c963db5Claude1199 letter-spacing: 0.16em;
1200 color: var(--text-muted);
1201 transition: color var(--t-fast) var(--ease);
958d26aClaude1202 }
c963db5Claude1203 .landing-cap:hover { color: var(--text-strong); }
1204 @media (max-width: 800px) {
1205 .landing-caps-grid { grid-template-columns: repeat(2, 1fr); gap: 18px 12px; }
1206 }
1207 @media (max-width: 480px) {
1208 .landing-caps-grid { grid-template-columns: 1fr; }
1209 }
1210
1211 /* ---------- Big stat row (crontech-style hero closer) ---------- */
1212 .landing-bigstats {
1213 margin: var(--s-10) auto var(--s-20);
1214 max-width: 1180px;
1215 padding: 0 var(--s-4);
1216 }
1217 .landing-bigstats-grid {
1218 display: grid;
1219 grid-template-columns: repeat(4, 1fr);
1220 gap: 32px;
1221 text-align: left;
958d26aClaude1222 }
c963db5Claude1223 .landing-bigstat {
1224 padding: var(--s-2) 0;
1225 }
1226 .landing-bigstat-num {
958d26aClaude1227 font-family: var(--font-display);
c963db5Claude1228 font-size: clamp(28px, 3.5vw, 44px);
1229 line-height: 1.05;
1230 letter-spacing: -0.03em;
1231 font-weight: 700;
1232 color: var(--text-strong);
1233 margin-bottom: var(--s-2);
1234 }
1235 .landing-bigstat-label {
1236 font-family: var(--font-mono);
1237 font-size: 11px;
958d26aClaude1238 font-weight: 500;
c963db5Claude1239 text-transform: uppercase;
1240 letter-spacing: 0.14em;
1241 color: var(--text-faint);
2b821b7Claude1242 }
c963db5Claude1243 @media (max-width: 800px) {
1244 .landing-bigstats-grid { grid-template-columns: repeat(2, 1fr); gap: 28px 16px; }
1245 }
1246 @media (max-width: 480px) {
1247 .landing-bigstats-grid { grid-template-columns: 1fr; gap: 24px; }
958d26aClaude1248 }
1249
1250 /* ---------- Section base ---------- */
1251 .landing-section { margin: var(--s-20) auto; }
2b821b7Claude1252
4c47454Claude1253 /* ---------- Feature grid ---------- */
1254 .landing-features {
2b821b7Claude1255 display: grid;
1256 grid-template-columns: repeat(3, 1fr);
958d26aClaude1257 gap: 16px;
2b821b7Claude1258 }
1259 .landing-feature {
958d26aClaude1260 background: var(--bg-elevated);
2b821b7Claude1261 border: 1px solid var(--border);
958d26aClaude1262 border-radius: var(--r-lg);
1263 padding: var(--s-7);
1264 position: relative;
1265 overflow: hidden;
1266 isolation: isolate;
1267 transition:
1268 transform var(--t-base) var(--ease-out-quart),
1269 border-color var(--t-base) var(--ease),
1270 box-shadow var(--t-base) var(--ease);
1271 }
1272 .landing-feature::before {
1273 content: '';
1274 position: absolute;
1275 inset: 0;
1276 background: radial-gradient(120% 100% at 0% 0%, rgba(140,109,255,0.08), transparent 55%);
1277 opacity: 0;
1278 transition: opacity var(--t-base) var(--ease);
1279 z-index: -1;
4c47454Claude1280 }
1281 .landing-feature:hover {
958d26aClaude1282 transform: translateY(-3px);
1283 border-color: var(--border-strong);
1284 box-shadow: var(--elev-2);
2b821b7Claude1285 }
958d26aClaude1286 .landing-feature:hover::before { opacity: 1; }
2b821b7Claude1287 .landing-feature-icon {
4c47454Claude1288 display: inline-flex;
1289 align-items: center;
1290 justify-content: center;
958d26aClaude1291 width: 40px;
1292 height: 40px;
1293 border-radius: var(--r);
1294 background: var(--accent-gradient-soft);
1295 color: var(--accent);
1296 margin-bottom: var(--s-4);
1297 border: 1px solid rgba(140,109,255,0.20);
2b821b7Claude1298 }
1299 .landing-feature-title {
958d26aClaude1300 font-family: var(--font-display);
1301 font-size: 19px;
1302 font-weight: 600;
1303 letter-spacing: -0.018em;
1304 margin: 0 0 var(--s-2);
1305 color: var(--text-strong);
2b821b7Claude1306 }
1307 .landing-feature-desc {
958d26aClaude1308 font-size: var(--t-sm);
1309 color: var(--text-muted);
1310 line-height: 1.6;
1311 margin: 0;
1312 }
1313
1314 /* ---------- Walkthrough ---------- */
1315 .landing-walk-grid {
1316 display: grid;
1317 grid-template-columns: repeat(4, 1fr);
1318 gap: 16px;
1319 counter-reset: walk;
1320 }
1321 .landing-walk-step {
1322 position: relative;
1323 padding: var(--s-7) var(--s-6) var(--s-6);
1324 background: var(--bg-elevated);
1325 border: 1px solid var(--border);
1326 border-radius: var(--r-lg);
1327 }
1328 .landing-walk-step::after {
1329 content: '';
1330 position: absolute;
1331 top: 50%;
1332 right: -12px;
1333 width: 12px;
1334 height: 1px;
1335 background: var(--border-strong);
1336 }
1337 .landing-walk-step:last-child::after { display: none; }
1338 .landing-walk-num {
1339 display: inline-block;
1340 font-family: var(--font-mono);
1341 font-size: 11px;
1342 color: var(--accent);
1343 background: var(--accent-gradient-faint);
1344 border: 1px solid rgba(140,109,255,0.30);
1345 padding: 3px 8px;
1346 border-radius: var(--r-full);
1347 letter-spacing: 0.06em;
1348 margin-bottom: var(--s-3);
1349 }
1350 .landing-walk-title {
1351 font-family: var(--font-display);
1352 font-size: 22px;
1353 font-weight: 600;
1354 letter-spacing: -0.022em;
1355 margin: 0 0 var(--s-2);
1356 color: var(--text-strong);
1357 }
1358 .landing-walk-desc {
1359 font-size: var(--t-sm);
2b821b7Claude1360 color: var(--text-muted);
1361 line-height: 1.55;
1362 margin: 0;
1363 }
1364
958d26aClaude1365 /* ---------- Terminal ---------- */
1366 .landing-terminal-section { margin-top: var(--s-16); }
4c47454Claude1367 .landing-terminal-wrap {
1368 display: flex;
2b821b7Claude1369 justify-content: center;
1370 }
4c47454Claude1371 .landing-terminal {
1372 width: 100%;
958d26aClaude1373 max-width: 820px;
1374 background: linear-gradient(180deg, #0a0b12 0%, #06070c 100%);
1375 border: 1px solid var(--border-strong);
1376 border-radius: var(--r-lg);
1377 overflow: hidden;
1378 box-shadow: var(--elev-3), 0 0 60px -10px rgba(140,109,255,0.18);
4c47454Claude1379 text-align: left;
2b821b7Claude1380 }
958d26aClaude1381 :root[data-theme='light'] .landing-terminal {
1382 background: linear-gradient(180deg, #0f111a 0%, #06070c 100%);
1383 }
1384 .landing-terminal-chrome {
1385 display: flex;
1386 align-items: center;
1387 gap: 7px;
1388 padding: 11px 14px;
1389 background: rgba(255,255,255,0.025);
1390 border-bottom: 1px solid rgba(255,255,255,0.06);
1391 position: relative;
1392 }
1393 .landing-terminal-dot {
1394 width: 11px;
1395 height: 11px;
1396 border-radius: 50%;
1397 flex-shrink: 0;
1398 }
1399 .landing-terminal-dot-r { background: #ff5f57; }
1400 .landing-terminal-dot-y { background: #febc2e; }
1401 .landing-terminal-dot-g { background: #28c840; }
1402 .landing-terminal-title {
1403 position: absolute;
1404 left: 50%;
1405 transform: translateX(-50%);
1406 font-family: var(--font-mono);
1407 font-size: 11px;
1408 color: rgba(237,237,242,0.55);
1409 letter-spacing: 0.01em;
1410 }
1411 .landing-terminal-body {
1412 padding: var(--s-6) var(--s-7);
1413 font-family: var(--font-mono);
1414 font-feature-settings: var(--mono-feat);
1415 font-size: 13.5px;
1416 line-height: 1.85;
1417 color: rgba(237,237,242,0.92);
1418 }
4c47454Claude1419 .landing-term-line {
1420 display: flex;
1421 gap: 10px;
1422 white-space: pre-wrap;
1423 word-break: break-all;
2b821b7Claude1424 }
958d26aClaude1425 .landing-term-out { color: rgba(237,237,242,0.7); }
1426 .landing-term-prompt { color: rgba(140,109,255,0.85); user-select: none; flex-shrink: 0; }
1427 .landing-term-meta { color: rgba(237,237,242,0.45); }
1428 .landing-term-ok { color: var(--green); user-select: none; flex-shrink: 0; }
1429 .landing-term-ok-line { color: rgba(237,237,242,0.92); }
1430 .landing-term-cursor { margin-top: 4px; }
1431 .landing-term-blink {
1432 animation: blink 1.05s steps(2) infinite;
1433 color: var(--accent);
1434 }
1435 @keyframes blink { 50% { opacity: 0; } }
1436
1437 /* ---------- Comparison ---------- */
1438 .landing-compare {
1439 max-width: 920px;
1440 margin: 0 auto;
1441 border: 1px solid var(--border);
1442 border-radius: var(--r-lg);
1443 overflow: hidden;
1444 background: var(--bg-elevated);
1445 }
1446 .landing-compare-row {
1447 display: grid;
1448 grid-template-columns: 1fr 180px 180px;
1449 align-items: center;
1450 padding: 14px 20px;
1451 border-bottom: 1px solid var(--border-subtle);
1452 font-size: var(--t-sm);
1453 transition: background var(--t-fast) var(--ease);
1454 }
1455 .landing-compare-row:last-child { border-bottom: none; }
1456 .landing-compare-row:hover { background: var(--bg-hover); }
1457 .landing-compare-feature {
1458 color: var(--text-strong);
1459 font-weight: 500;
1460 }
1461 .landing-compare-them, .landing-compare-us {
1462 text-align: center;
1463 font-family: var(--font-mono);
1464 font-size: 12px;
1465 color: var(--text-muted);
1466 }
1467 .landing-compare-us { color: var(--green); font-weight: 500; }
1468 .landing-compare-hl .landing-compare-us {
1469 color: var(--accent);
1470 font-weight: 600;
2b821b7Claude1471 }
958d26aClaude1472 .landing-compare-hl .landing-compare-feature::after {
1473 content: 'NEW';
1474 margin-left: 8px;
1475 padding: 1px 6px;
1476 border-radius: 4px;
1477 background: var(--accent-gradient-faint);
1478 color: var(--accent);
1479 font-family: var(--font-mono);
1480 font-size: 9px;
1481 letter-spacing: 0.1em;
1482 font-weight: 600;
1483 vertical-align: 1px;
1484 }
1485 @media (max-width: 720px) {
1486 .landing-compare-row { grid-template-columns: 1fr 80px 80px; padding: 12px 14px; }
1487 .landing-compare-hl .landing-compare-feature::after { display: none; }
1488 }
1489
1490 /* ---------- Pricing ---------- */
1491 .landing-pricing {
1492 display: grid;
1493 grid-template-columns: repeat(3, 1fr);
1494 gap: 16px;
1495 max-width: 1080px;
1496 margin: 0 auto;
1497 align-items: stretch;
1498 }
1499 .landing-price-card {
1500 position: relative;
1501 background: var(--bg-elevated);
1502 border: 1px solid var(--border);
1503 border-radius: var(--r-lg);
1504 padding: var(--s-7);
1505 display: flex;
1506 flex-direction: column;
1507 gap: var(--s-4);
1508 transition: border-color var(--t-base) var(--ease), transform var(--t-base) var(--ease);
1509 }
1510 .landing-price-card:hover {
1511 border-color: var(--border-strong);
1512 transform: translateY(-2px);
1513 }
1514 .landing-price-hl {
1515 border-color: rgba(140,109,255,0.35);
1516 box-shadow: var(--elev-2), 0 0 0 1px rgba(140,109,255,0.25);
1517 background:
1518 linear-gradient(180deg, rgba(140,109,255,0.05), transparent 50%),
1519 var(--bg-elevated);
1520 }
1521 .landing-price-hl:hover { border-color: rgba(140,109,255,0.55); }
1522 .landing-price-badge {
1523 position: absolute;
1524 top: -10px;
1525 left: 50%;
1526 transform: translateX(-50%);
1527 padding: 3px 12px;
1528 background: var(--accent-gradient);
1529 color: #fff;
1530 font-family: var(--font-mono);
1531 font-size: 10px;
1532 letter-spacing: 0.1em;
1533 text-transform: uppercase;
1534 font-weight: 600;
1535 border-radius: var(--r-full);
1536 box-shadow: 0 4px 12px -2px rgba(140,109,255,0.4);
1537 }
1538 .landing-price-tier {
1539 font-family: var(--font-mono);
1540 font-size: 11px;
1541 text-transform: uppercase;
1542 letter-spacing: 0.16em;
1543 color: var(--text-muted);
1544 }
1545 .landing-price-amount {
1546 display: flex;
1547 align-items: baseline;
1548 gap: 8px;
1549 }
1550 .landing-price-num {
1551 font-family: var(--font-display);
1552 font-size: 40px;
1553 font-weight: 600;
1554 letter-spacing: -0.03em;
1555 color: var(--text-strong);
1556 }
1557 .landing-price-cad {
1558 font-size: var(--t-sm);
1559 color: var(--text-faint);
1560 }
1561 .landing-price-desc {
1562 font-size: var(--t-sm);
1563 color: var(--text-muted);
1564 line-height: 1.55;
1565 margin: 0;
1566 }
1567 .landing-price-features {
1568 list-style: none;
1569 padding: 0;
1570 margin: 0;
1571 display: flex;
1572 flex-direction: column;
1573 gap: 8px;
1574 font-size: var(--t-sm);
1575 color: var(--text);
1576 }
1577 .landing-price-features li {
1578 display: flex;
1579 align-items: center;
1580 gap: 9px;
1581 }
1582 .landing-price-check {
1583 color: var(--accent);
1584 font-weight: 600;
4c47454Claude1585 flex-shrink: 0;
2b821b7Claude1586 }
958d26aClaude1587 .landing-price-cta { margin-top: auto; }
1588
1589 /* ---------- Closing CTA ---------- */
1590 .landing-cta-section { margin: var(--s-20) auto var(--s-16); }
1591 .landing-cta-card {
1592 position: relative;
1593 text-align: center;
1594 padding: var(--s-16) var(--s-7);
1595 border: 1px solid var(--border-strong);
1596 border-radius: var(--r-2xl);
1597 background: var(--bg-elevated);
1598 overflow: hidden;
1599 isolation: isolate;
1600 }
1601 .landing-cta-bg {
1602 position: absolute;
1603 inset: 0;
1604 z-index: -1;
1605 background:
1606 radial-gradient(60% 100% at 50% 0%, rgba(140,109,255,0.16), transparent 65%),
1607 radial-gradient(40% 80% at 80% 100%, rgba(54,197,214,0.10), transparent 65%);
1608 }
1609 .landing-cta-card::after {
1610 content: '';
1611 position: absolute;
1612 inset: 0;
1613 z-index: -1;
1614 background-image: radial-gradient(rgba(255,255,255,0.04) 1px, transparent 1px);
1615 background-size: 24px 24px;
1616 mask-image: radial-gradient(ellipse at center, #000 0%, transparent 65%);
1617 -webkit-mask-image: radial-gradient(ellipse at center, #000 0%, transparent 65%);
1618 opacity: 0.6;
1619 }
1620 :root[data-theme='light'] .landing-cta-card::after {
1621 background-image: radial-gradient(rgba(15,16,28,0.07) 1px, transparent 1px);
1622 }
1623 .landing-cta-card .eyebrow { justify-content: center; }
1624 .landing-cta-title {
1625 font-family: var(--font-display);
1626 font-size: clamp(28px, 4.4vw, 56px);
1627 line-height: 1.05;
1628 letter-spacing: -0.03em;
1629 font-weight: 600;
1630 margin: var(--s-3) 0 var(--s-4);
1631 color: var(--text-strong);
1632 }
1633 .landing-cta-sub {
1634 font-size: var(--t-md);
1635 color: var(--text-muted);
1636 max-width: 560px;
1637 margin: 0 auto var(--s-8);
1638 line-height: 1.55;
1639 }
1640 .landing-cta-buttons {
1641 display: flex;
1642 gap: 12px;
1643 justify-content: center;
1644 flex-wrap: wrap;
1645 }
2b821b7Claude1646
1647 /* ---------- Responsive ---------- */
958d26aClaude1648 @media (max-width: 960px) {
1649 .landing-features { grid-template-columns: repeat(2, 1fr); }
1650 .landing-walk-grid { grid-template-columns: repeat(2, 1fr); }
1651 .landing-walk-step::after { display: none; }
1652 .landing-pricing { grid-template-columns: 1fr; max-width: 480px; }
1653 }
1654 @media (max-width: 640px) {
1655 .landing-hero { padding: var(--s-14) 0 var(--s-10); }
1656 .landing-hero-cmd { flex-wrap: wrap; justify-content: center; }
1657 .landing-hero-ctas { flex-direction: column; align-items: stretch; }
1658 .landing-hero-ctas .btn { width: 100%; justify-content: center; }
1659 .landing-features { grid-template-columns: 1fr; }
1660 .landing-walk-grid { grid-template-columns: 1fr; }
1661 .landing-section { margin: var(--s-12) auto; }
1662 .landing-cta-card { padding: var(--s-10) var(--s-5); }
1663 .landing-cta-buttons .btn { width: 100%; justify-content: center; }
2b821b7Claude1664 }
52ad8b1Claude1665
1666 /* ---------- L4 social-proof counters ---------- */
1667 .landing-counters {
1668 margin: var(--s-10) auto var(--s-12);
1669 max-width: 1180px;
1670 padding: 0 var(--s-4);
1671 }
1672 .landing-counters-grid {
1673 display: grid;
1674 grid-template-columns: repeat(6, 1fr);
1675 gap: 20px;
1676 text-align: left;
1677 }
1678 .landing-counter {
1679 padding: var(--s-3) 0;
1680 border-top: 1px solid var(--border-subtle);
1681 }
1682 .landing-counter-num {
1683 font-family: var(--font-display);
1684 font-size: clamp(24px, 3vw, 38px);
1685 line-height: 1.05;
1686 letter-spacing: -0.03em;
1687 font-weight: 700;
1688 margin-bottom: 6px;
1689 font-feature-settings: 'tnum';
1690 background-image: var(--accent-gradient);
1691 -webkit-background-clip: text;
1692 background-clip: text;
1693 -webkit-text-fill-color: transparent;
1694 color: transparent;
1695 }
1696 .landing-counter-label {
1697 font-family: var(--font-mono);
1698 font-size: 10.5px;
1699 font-weight: 500;
1700 text-transform: uppercase;
1701 letter-spacing: 0.12em;
1702 color: var(--text-faint);
1703 line-height: 1.4;
1704 }
1705 @media (max-width: 960px) {
1706 .landing-counters-grid { grid-template-columns: repeat(3, 1fr); gap: 20px 16px; }
1707 }
1708 @media (max-width: 540px) {
1709 .landing-counters-grid { grid-template-columns: repeat(2, 1fr); gap: 18px 12px; }
1710 }
5f2e749Claude1711
1712 /* ---------- L10 hero install snippet ---------- */
1713 .landing-hero-install {
1714 display: inline-flex;
1715 align-items: stretch;
1716 gap: 0;
1717 margin: var(--s-8) auto 0;
1718 background: var(--bg-elevated);
1719 border: 1px solid var(--border-strong);
1720 border-radius: var(--r);
1721 box-shadow: var(--elev-1);
1722 overflow: hidden;
1723 max-width: 100%;
1724 font-family: var(--font-mono);
1725 }
1726 .landing-hero-install-code {
1727 display: inline-flex;
1728 align-items: center;
1729 gap: 10px;
1730 padding: 10px 14px;
1731 font-size: 13.5px;
1732 color: var(--text-strong);
1733 background: transparent;
1734 border: 0;
1735 white-space: nowrap;
1736 overflow-x: auto;
1737 }
1738 .landing-hero-install-prompt {
1739 color: var(--accent);
1740 user-select: none;
1741 }
1742 .landing-hero-install-copy {
1743 appearance: none;
1744 border: 0;
1745 border-left: 1px solid var(--border);
1746 background: transparent;
1747 color: var(--text-muted);
1748 font-family: var(--font-mono);
1749 font-size: 12px;
1750 font-weight: 600;
1751 letter-spacing: 0.06em;
1752 text-transform: uppercase;
1753 padding: 0 16px;
1754 cursor: pointer;
1755 transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
1756 }
1757 .landing-hero-install-copy:hover {
1758 background: var(--accent-gradient-faint);
1759 color: var(--accent);
1760 }
1761 .landing-hero-install-copy[data-copied="1"] {
1762 color: var(--green, #34d399);
1763 }
1764
1765 /* ---------- L10 hero "what just happened" rail ---------- */
1766 .landing-hero-rail {
1767 list-style: none;
1768 padding: 0;
1769 margin: var(--s-7) auto 0;
1770 display: flex;
1771 flex-wrap: wrap;
1772 justify-content: center;
1773 gap: 8px 22px;
1774 font-family: var(--font-sans);
1775 font-size: var(--t-sm);
1776 color: var(--text-muted);
1777 max-width: 760px;
1778 }
1779 .landing-hero-rail li {
1780 display: inline-flex;
1781 align-items: center;
1782 gap: 7px;
1783 line-height: 1.4;
1784 }
1785 .landing-hero-rail strong {
1786 color: var(--text-strong);
1787 font-weight: 600;
1788 font-feature-settings: 'tnum';
1789 }
1790 .landing-hero-rail-check {
1791 color: var(--accent);
1792 font-weight: 700;
1793 flex-shrink: 0;
1794 }
1795
1796 /* ---------- L10 three-reasons section ---------- */
1797 .landing-reasons { margin-top: var(--s-12); }
1798 .landing-reasons-grid {
1799 display: grid;
1800 grid-template-columns: repeat(3, 1fr);
1801 gap: 16px;
1802 }
1803 .landing-reason {
1804 background: var(--bg-elevated);
1805 border: 1px solid var(--border);
1806 border-radius: var(--r-lg);
1807 padding: var(--s-7);
1808 display: flex;
1809 flex-direction: column;
1810 gap: var(--s-3);
1811 transition: border-color var(--t-base) var(--ease), transform var(--t-base) var(--ease);
1812 }
1813 .landing-reason:hover {
1814 border-color: var(--border-strong);
1815 transform: translateY(-2px);
1816 }
1817 .landing-reason-icon {
1818 display: inline-flex;
1819 align-items: center;
1820 justify-content: center;
1821 width: 40px;
1822 height: 40px;
1823 border-radius: var(--r);
1824 background: var(--accent-gradient-soft);
1825 color: var(--accent);
1826 border: 1px solid rgba(140,109,255,0.20);
1827 }
1828 .landing-reason-title {
1829 font-family: var(--font-display);
1830 font-size: 20px;
1831 font-weight: 600;
1832 letter-spacing: -0.02em;
1833 margin: 0;
1834 color: var(--text-strong);
1835 }
1836 .landing-reason-body {
1837 font-size: var(--t-sm);
1838 color: var(--text-muted);
1839 line-height: 1.55;
1840 margin: 0;
1841 }
1842 .landing-reasons-code {
1843 display: block;
1844 font-family: var(--font-mono);
1845 font-size: 12px;
1846 color: var(--text-strong);
1847 background: var(--bg);
1848 border: 1px solid var(--border);
1849 border-radius: var(--r);
1850 padding: 8px 12px;
1851 overflow-x: auto;
1852 white-space: nowrap;
1853 }
1854 .landing-reason-link {
1855 margin-top: auto;
1856 display: inline-flex;
1857 align-items: center;
1858 gap: 6px;
1859 color: var(--accent);
1860 font-size: var(--t-sm);
1861 font-weight: 500;
1862 text-decoration: none;
1863 }
1864 .landing-reason-link:hover { text-decoration: underline; }
1865 @media (max-width: 960px) {
1866 .landing-reasons-grid { grid-template-columns: 1fr; max-width: 520px; margin: 0 auto; }
1867 }
1868
1869 /* ---------- L10 "How is this different" pull-quote ---------- */
1870 .landing-pullquote-section {
1871 margin: var(--s-20) auto var(--s-12);
1872 max-width: 920px;
1873 padding: 0 var(--s-4);
1874 text-align: center;
1875 }
1876 .landing-pullquote {
1877 margin: 0;
1878 padding: var(--s-10) var(--s-7);
1879 background:
1880 radial-gradient(80% 100% at 50% 0%, rgba(140,109,255,0.10), transparent 65%),
1881 var(--bg-elevated);
1882 border: 1px solid var(--border-strong);
1883 border-radius: var(--r-xl);
1884 position: relative;
1885 overflow: hidden;
1886 }
1887 .landing-pullquote-eyebrow {
1888 font-family: var(--font-mono);
1889 font-size: 11px;
1890 font-weight: 600;
1891 letter-spacing: 0.16em;
1892 text-transform: uppercase;
1893 color: var(--accent);
1894 margin-bottom: var(--s-4);
1895 }
1896 .landing-pullquote-text {
1897 font-family: var(--font-display);
1898 font-size: clamp(20px, 2.4vw, 28px);
1899 line-height: 1.4;
1900 letter-spacing: -0.018em;
1901 color: var(--text-strong);
1902 margin: 0 auto;
1903 max-width: 760px;
1904 quotes: "\\201C" "\\201D";
1905 }
1906 .landing-pullquote-text::before { content: open-quote; color: var(--accent); margin-right: 4px; }
1907 .landing-pullquote-text::after { content: close-quote; color: var(--accent); margin-left: 4px; }
1908 .landing-pullquote-link {
1909 display: inline-flex;
1910 align-items: center;
1911 gap: 6px;
1912 margin-top: var(--s-6);
1913 color: var(--accent);
1914 font-size: var(--t-sm);
1915 font-weight: 500;
1916 text-decoration: none;
1917 }
1918 .landing-pullquote-link:hover { text-decoration: underline; }
1919
1920 /* ---------- L10 hero responsive overrides ---------- */
1921 @media (max-width: 640px) {
1922 .landing-hero-install { width: 100%; }
1923 .landing-hero-install-code { flex: 1; font-size: 12px; }
1924 .landing-hero-rail { flex-direction: column; align-items: flex-start; gap: 6px; padding: 0 var(--s-3); }
1925 .landing-hero-rail li { width: 100%; }
1926 }
52ad8b1Claude1927`;
1928
1929/**
1930 * Block L4 — count-up animation.
1931 *
1932 * Reads each `[data-counter-target]` and animates the in-DOM text from
1933 * 0 → target over ~1.2s when the element first scrolls into view.
1934 *
1935 * Render-once semantics: each tile already contains the final value as
1936 * HTML, so visitors with JS disabled — or anyone before the script
1937 * loads — sees the correct number. The script just animates the text.
1938 *
1939 * Falls back to the static value (no animation) when IntersectionObserver
1940 * isn't available, or when the user prefers reduced motion.
1941 */
1942const landingCountersJs = `
1943(function(){
1944 try {
1945 var els = document.querySelectorAll('[data-counter-target]');
1946 if (!els.length) return;
1947 var reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
1948 if (reduced || typeof IntersectionObserver !== 'function') return;
1949
1950 function animate(el) {
1951 var target = parseInt(el.getAttribute('data-counter-target') || '0', 10);
1952 if (!isFinite(target) || target <= 0) return;
1953 var prefix = el.getAttribute('data-counter-prefix') || '';
1954 var suffix = el.getAttribute('data-counter-suffix') || '';
1955 var duration = 1200;
1956 var start = performance.now();
1957 function frame(now) {
1958 var t = Math.min(1, (now - start) / duration);
1959 // ease-out cubic
1960 var eased = 1 - Math.pow(1 - t, 3);
1961 var v = Math.floor(eased * target);
1962 el.textContent = prefix + v.toLocaleString() + suffix;
1963 if (t < 1) requestAnimationFrame(frame);
1964 else el.textContent = prefix + target.toLocaleString() + suffix;
1965 }
1966 // Reset to zero before animating in.
1967 el.textContent = prefix + '0' + suffix;
1968 requestAnimationFrame(frame);
1969 }
1970
1971 var io = new IntersectionObserver(function(entries) {
1972 entries.forEach(function(entry){
1973 if (entry.isIntersecting) {
1974 animate(entry.target);
1975 io.unobserve(entry.target);
1976 }
1977 });
1978 }, { threshold: 0.4 });
1979 els.forEach(function(el){ io.observe(el); });
1980 } catch (_) { /* swallow — static numbers remain */ }
1981})();
2b821b7Claude1982`;
5f2e749Claude1983
1984/**
1985 * Block L10 — clipboard copy for the hero install snippet.
1986 *
1987 * Pure progressive enhancement. Without JS the user can still
1988 * triple-click + Cmd/Ctrl-C the snippet — the button is the
1989 * speed-bump, not the only path.
1990 */
1991const landingCopyJs = `
1992(function(){
1993 try {
1994 var btns = document.querySelectorAll('[data-copy-target]');
1995 if (!btns.length) return;
1996 btns.forEach(function(btn){
1997 btn.addEventListener('click', function(){
1998 var id = btn.getAttribute('data-copy-target') || '';
1999 var src = document.getElementById(id);
2000 if (!src) return;
2001 var text = src.textContent || '';
2002 var done = function(){
2003 var prev = btn.textContent;
2004 btn.textContent = 'Copied';
2005 btn.setAttribute('data-copied', '1');
2006 setTimeout(function(){
2007 btn.textContent = prev || 'Copy';
2008 btn.removeAttribute('data-copied');
2009 }, 1500);
2010 };
2011 if (navigator.clipboard && navigator.clipboard.writeText) {
2012 navigator.clipboard.writeText(text).then(done, function(){ done(); });
2013 } else {
2014 // Legacy fallback — temp textarea + execCommand.
2015 try {
2016 var ta = document.createElement('textarea');
2017 ta.value = text;
2018 ta.style.position = 'fixed';
2019 ta.style.opacity = '0';
2020 document.body.appendChild(ta);
2021 ta.select();
2022 document.execCommand('copy');
2023 document.body.removeChild(ta);
2024 done();
2025 } catch (_) {}
2026 }
2027 });
2028 });
2029 } catch (_) { /* swallow */ }
2030})();
2031`;