Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history

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

pricing.tsxBlame928 lines · 2 contributors
5f2e749Claude1/**
2 * Block L8 — public `/pricing` page.
3 *
4 * Anonymous-safe GET /pricing. Reads the real plan rows from
5 * `billing_plans` (or `FALLBACK_PLANS` when seeds aren't loaded yet — both
6 * mirror migration 0020) so the price column never drifts from the actual
7 * billing config. This route is mounted BEFORE `routes/marketing.tsx` in
8 * `app.tsx` so the new editorial layout wins; the legacy marketing /pricing
9 * remains as a safety net.
10 *
11 * Anchors:
12 * #free → "What you get on the free tier" block
13 * #self-host → Self-host vs Cloud comparison
14 * #faq → Frequently asked questions
15 *
16 * Pure presentational — no billing logic is created here. The page only
17 * surfaces what `src/lib/billing.ts` already ships.
18 */
19
20import { Hono } from "hono";
21import type { FC } from "hono/jsx";
22import { Layout } from "../views/layout";
23import { softAuth } from "../middleware/auth";
24import type { AuthEnv } from "../middleware/auth";
25import { formatPrice, listPlans } from "../lib/billing";
26
27const pricing = new Hono<AuthEnv>();
28pricing.use("*", softAuth);
29
30// ---- Per-slug copy: tagline + included-bullet list ------------------------
31// Indexed by the seeded plan slugs. Anything not in this map falls back to
32// generic copy derived from the plan's numeric limits so we never miss a
33// row even if a future migration adds a new tier.
34const PLAN_COPY: Record<
35 string,
36 { tagline: string; supportTier: string }
37> = {
38 free: {
a65653bClaude39 tagline: "AI review on every PR, auto-merge, and spec-to-PR — on unlimited public repos, forever.",
5f2e749Claude40 supportTier: "Community support",
41 },
42 pro: {
a65653bClaude43 tagline: "Private repos + higher AI quota. AI review, auto-merge, and spec-to-PR for working developers shipping daily.",
5f2e749Claude44 supportTier: "Email support, priority AI queue",
45 },
46 team: {
a65653bClaude47 tagline: "Full AI suite for production teams — AI review, auto-merge when gates pass, and spec-to-PR at scale.",
5f2e749Claude48 supportTier: "Slack channel + 24h response",
49 },
50 enterprise: {
a65653bClaude51 tagline: "SSO, audit log, on-prem deploy — with the full AI suite: review, auto-merge, and spec-to-PR.",
5f2e749Claude52 supportTier: "24/7 incident response + DPA",
53 },
54};
55
56pricing.get("/pricing", async (c) => {
57 const user = c.get("user");
58 const plans = await listPlans();
59 return c.html(
5acce80Claude60 <Layout
61 title="Pricing — Gluecron"
62 user={user}
63 description="Simple pricing for AI-native git hosting. Unlimited repos, AI review, auto-merge, spec-to-PR. Start free."
64 ogTitle="Pricing — Gluecron"
65 ogDescription="Simple pricing for AI-native git hosting. Unlimited repos, AI review, auto-merge, spec-to-PR. Start free."
66 twitterCard="summary"
67 >
5f2e749Claude68 <PricingPage plans={plans} loggedIn={!!user} />
69 </Layout>
70 );
71});
72
73interface Plan {
74 slug: string;
75 name: string;
76 priceCents: number;
77 repoLimit: number;
78 storageMbLimit: number;
79 aiTokensMonthly: number;
80 bandwidthGbMonthly: number;
81 privateRepos: boolean;
82}
83
84const PricingPage: FC<{ plans: Plan[]; loggedIn: boolean }> = ({
85 plans,
86 loggedIn,
87}) => (
88 <>
89 <style dangerouslySetInnerHTML={{ __html: pricingCss }} />
90 <div class="pl-root">
91 {/* ------------------- Hero ------------------- */}
92 <header class="pl-hero">
8929744Claude93 <div class="pl-hero-hairline" aria-hidden="true" />
94 <div class="pl-hero-orb" aria-hidden="true" />
5f2e749Claude95 <div class="eyebrow">Pricing</div>
a65653bClaude96 <p class="pl-hero-speed">Spec to PR in 90 seconds.</p>
5f2e749Claude97 <h1 class="display pl-hero-title">
13ac035Claude98 One subscription.{" "}
99 <span class="gradient-text">
100 Replaces three on GitHub.
101 </span>
5f2e749Claude102 </h1>
103 <p class="pl-hero-sub">
a65653bClaude104 AI review fires in ~8s. Merges the instant gates pass. Spec to PR in
105 90 seconds. What costs $89/user on GitHub + Copilot + Advanced
13ac035Claude106 Security starts at $0 here.
5f2e749Claude107 </p>
108 <div class="pl-hero-jumps">
13ac035Claude109 <a href="#compare" class="pl-jump">Bundle math vs GitHub →</a>
110 <a href="#free" class="pl-jump">What's free</a>
5f2e749Claude111 <a href="#self-host" class="pl-jump">Self-host vs Cloud</a>
112 <a href="#faq" class="pl-jump">FAQ</a>
113 </div>
114 </header>
115
116 {/* ------------------- Plan cards ------------------- */}
117 <section class="pl-plans stagger">
118 {plans.map((p) => (
119 <PlanCard plan={p} loggedIn={loggedIn} />
120 ))}
121 </section>
122
a65653bClaude123 {/* ------------------- Included in every plan ------------------- */}
124 <section class="pl-section pl-every-plan">
125 <div class="pl-every-plan-inner">
126 <div class="pl-every-plan-label">Included in every plan</div>
127 <ul class="pl-every-plan-list">
128 <li>AI review on every PR</li>
129 <li>Auto-merge when gates pass</li>
130 <li>Spec to PR in 90 seconds</li>
131 <li>Push Watch live stream</li>
132 </ul>
133 </div>
134 </section>
135
13ac035Claude136 {/* ------------------- Bundle math vs GitHub ------------------- */}
137 <section id="compare" class="pl-section pl-compare">
138 <div class="section-header">
139 <div class="eyebrow">Bundle math</div>
140 <h2>
141 What you'd actually pay on GitHub
142 <span class="gradient-text"> for the same features.</span>
143 </h2>
144 <p>
145 GitHub charges separately for Copilot, Advanced Security, and the
146 base plan. Gluecron bundles everything. Here's the receipt.
147 </p>
148 </div>
149 <div class="pl-compare-grid">
150 <div class="pl-compare-col">
151 <div class="pl-compare-name">GitHub stack</div>
152 <div class="pl-compare-price">$89<span class="pl-compare-per">/user/mo</span></div>
153 <ul class="pl-compare-feats">
154 <li><span class="pl-compare-bullet">$21</span> GitHub Enterprise (per-user)</li>
155 <li><span class="pl-compare-bullet">$19</span> GitHub Copilot Business</li>
156 <li><span class="pl-compare-bullet">$49</span> GitHub Advanced Security add-on</li>
157 <li class="pl-compare-missing">— No AI auto-merge</li>
158 <li class="pl-compare-missing">— No AI incident responder</li>
159 <li class="pl-compare-missing">— No AI spec-to-PR</li>
160 <li class="pl-compare-missing">— No MCP-native integration</li>
161 </ul>
162 </div>
163 <div class="pl-compare-col pl-compare-us">
164 <div class="pl-compare-name">Gluecron Team</div>
165 <div class="pl-compare-price">$29<span class="pl-compare-per">/user/mo</span></div>
166 <ul class="pl-compare-feats">
167 <li><span class="pl-compare-check">✓</span> Unlimited private repos + git host</li>
168 <li><span class="pl-compare-check">✓</span> AI code review on every PR (Claude Sonnet)</li>
169 <li><span class="pl-compare-check">✓</span> AI auto-merge when gates pass</li>
170 <li><span class="pl-compare-check">✓</span> AI incident responder on deploy failure</li>
171 <li><span class="pl-compare-check">✓</span> AI spec-to-PR (label an issue, get a PR)</li>
172 <li><span class="pl-compare-check">✓</span> AI completion + commit messages + tests</li>
173 <li><span class="pl-compare-check">✓</span> MCP server — drive it from Claude Desktop</li>
174 <li><span class="pl-compare-check">✓</span> SSO, audit log, branch protection, rulesets</li>
175 </ul>
176 </div>
177 </div>
178 <p class="pl-compare-footer">
179 For a 10-person team, the math is{" "}
180 <strong>$10,680/year on GitHub vs $3,480/year on Gluecron</strong>
181 {" — "}
182 and you get the AI features that don't exist on GitHub at any price.
183 </p>
184 </section>
185
5f2e749Claude186 {/* ------------------- What's on the free tier ------------------- */}
187 <section id="free" class="pl-section pl-free">
188 <div class="section-header">
189 <div class="eyebrow">Free tier</div>
190 <h2>Everything below is yours on the free tier.</h2>
191 <p>
192 All the AI features. Not a "try it for 14 days" trial. Not a
193 "core features" stub. The whole Claude-powered platform — on
194 unlimited public repos, forever.
195 </p>
196 </div>
197 <ul class="pl-free-grid">
198 <FreeItem label="Unlimited public repos" />
199 <FreeItem label="AI code review on every PR (Sonnet 4)" />
200 <FreeItem label="AI auto-merge when checks pass (K2)" />
201 <FreeItem label="ai:build label → spec-to-PR (K3)" />
202 <FreeItem label="Sleep Mode digest (L1)" />
203 <FreeItem label="AI hours saved counter (L9)" />
204 <FreeItem label="MCP server access (K1)" />
205 <FreeItem label="Claude Code skill bundle (L7)" />
206 <FreeItem label="One-command install" />
207 <FreeItem label="GitHub OIDC sign-in" />
208 <FreeItem label="Webhooks + REST API v2 + GraphQL" />
209 <FreeItem label="Package registry + Pages hosting" />
210 </ul>
211 </section>
212
213 {/* ------------------- Self-host vs Cloud ------------------- */}
214 <section id="self-host" class="pl-section">
215 <div class="section-header">
216 <div class="eyebrow">Two ways to run it</div>
217 <h2>Self-host on your metal. Or let us run it.</h2>
218 <p>
219 Same product, same code, same Claude-powered features. The only
220 difference is who pays the electricity bill.
221 </p>
222 </div>
223 <div class="pl-host-grid">
224 <div class="pl-host-col">
225 <div class="pl-host-name">Self-host</div>
226 <div class="pl-host-price">Free forever</div>
227 <ul class="pl-host-feats">
228 <li>Free forever — no license, no per-seat fee</li>
229 <li>Your database, your disk, your control</li>
230 <li>You pay your Anthropic API key directly</li>
231 <li>Run via <code>curl gluecron.com/install</code></li>
232 <li>Or the Hetzner bootstrap script in 30 seconds</li>
233 </ul>
234 <a href="/install" class="btn btn-secondary btn-block pl-host-cta">
235 Self-host guide
236 </a>
237 </div>
238 <div class="pl-host-col pl-host-cloud">
239 <div class="pl-host-name">Gluecron Cloud</div>
240 <div class="pl-host-price">From $0/mo</div>
241 <ul class="pl-host-feats">
242 <li>Managed — we run the server, you push code</li>
243 <li>Opinionated stack, zero ops on your end</li>
244 <li>Automatic upgrades to every new block</li>
245 <li>Support included on paid plans</li>
246 <li>Plan-based pricing, no surprise overage</li>
247 </ul>
248 <a
249 href={loggedIn ? "/settings/billing" : "/register?next=/settings/billing"}
250 class="btn btn-primary btn-block pl-host-cta"
251 >
252 Start on Cloud
253 </a>
254 </div>
255 </div>
256 </section>
257
258 {/* ------------------- FAQ ------------------- */}
259 <section id="faq" class="pl-section">
260 <div class="section-header">
261 <div class="eyebrow">Questions</div>
262 <h2>The fine print, in plain English.</h2>
263 </div>
264 <div class="pl-faq">
265 <FaqItem
266 q="Is it really free? What's the catch?"
267 a="Really free. The free tier exists because we want every Claude-curious developer to try Gluecron without a credit card. The catch — if you can call it that — is that we hope you'll upgrade to Pro once you're shipping production traffic and need higher AI quotas."
268 />
269 <FaqItem
270 q="Do I need to bring my own Anthropic API key on the free tier?"
271 a="No. The free tier includes a generous monthly AI quota powered by our keys. If you'd rather use your own key (for cost control or enterprise rate limits), you can plug it in at /settings — Pro and above can route AI through your account."
272 />
273 <FaqItem
274 q="What happens when I exceed my plan's quota?"
275 a="AI features degrade gracefully — git push, hosting, and gates keep working. AI suggestions queue at the back of the line until the next cycle. We never auto-bill you for overage or auto-upgrade your plan."
276 />
277 <FaqItem
278 q="Can I migrate from GitHub for free?"
5472beaccanty labs279 a="Yes. The migration tool is on every tier, free included. Point it at a GitHub repo URL and we import code, issues, PRs, and releases in one shot. No vendor lock — you can migrate back the same way."
5f2e749Claude280 />
281 <FaqItem
282 q="Does the free tier include private repos?"
283 a="The free tier is built around unlimited public repos. Private repos start on the Pro plan — that's the main paid-tier perk along with the higher AI quota. If you're self-hosting, all repos are private by default and there's no plan to worry about."
284 />
285 </div>
286 </section>
287
288 {/* ------------------- CTA ------------------- */}
289 <section class="pl-section pl-cta-wrap">
290 <div class="pl-cta">
291 <h2 class="pl-cta-title">
292 Ready to push your first repo?
293 </h2>
294 <p class="pl-cta-sub">
295 Free, no credit card, full AI suite from minute one.
296 </p>
297 <div class="pl-cta-buttons">
298 <a href="/register" class="btn btn-primary btn-xl">
299 Start free
300 </a>
301 <a href="/vs-github" class="btn btn-ghost btn-xl">
302 Compare to GitHub
303 </a>
304 </div>
305 </div>
306 </section>
307 </div>
308 </>
309);
310
311// ---- Sub-components -------------------------------------------------------
312
313const PlanCard: FC<{ plan: Plan; loggedIn: boolean }> = ({ plan, loggedIn }) => {
314 const copy = PLAN_COPY[plan.slug] || {
315 tagline: `${plan.name} plan.`,
316 supportTier: "Support included",
317 };
318 const href = loggedIn
319 ? `/settings/billing?plan=${plan.slug}`
320 : `/register?next=/settings/billing?plan=${plan.slug}`;
321 const isPro = plan.slug === "pro";
322 return (
323 <div class={`pl-card${isPro ? " pl-card-hl" : ""}`}>
324 {isPro && <div class="pl-card-badge">Most popular</div>}
325 <div class="pl-card-name">{plan.name}</div>
326 <div class="pl-card-price">
327 <span class="pl-card-num">{formatPrice(plan.priceCents)}</span>
328 </div>
329 <p class="pl-card-tag">{copy.tagline}</p>
330 <ul class="pl-card-feats">
331 <li>
332 <span class="pl-check">{"✓"}</span>
333 {plan.repoLimit.toLocaleString()} repos
334 {plan.privateRepos ? " (public + private)" : " (public only)"}
335 </li>
336 <li>
337 <span class="pl-check">{"✓"}</span>
338 {plan.aiTokensMonthly.toLocaleString()} AI tokens / month
339 </li>
340 <li>
341 <span class="pl-check">{"✓"}</span>
342 {plan.storageMbLimit.toLocaleString()} MB storage
343 </li>
344 <li>
345 <span class="pl-check">{"✓"}</span>
346 {plan.bandwidthGbMonthly.toLocaleString()} GB bandwidth / month
347 </li>
348 <li>
349 <span class="pl-check">{"✓"}</span>
350 {copy.supportTier}
351 </li>
352 </ul>
353 <a
354 href={href}
355 class={`btn ${isPro ? "btn-primary" : "btn-secondary"} btn-block pl-card-cta`}
356 >
357 Choose {plan.name}
358 </a>
359 </div>
360 );
361};
362
363const FreeItem: FC<{ label: string }> = ({ label }) => (
364 <li class="pl-free-item">
365 <span class="pl-free-check">{"✓"}</span>
366 <span>{label}</span>
367 </li>
368);
369
370const FaqItem: FC<{ q: string; a: string }> = ({ q, a }) => (
371 <details class="pl-faq-item">
372 <summary class="pl-faq-q">
373 <span>{q}</span>
374 <span class="pl-faq-toggle" aria-hidden="true">{"+"}</span>
375 </summary>
376 <p class="pl-faq-a">{a}</p>
377 </details>
378);
379
380// ---- Styles (scoped under .pl-) -------------------------------------------
381
382const pricingCss = `
383 .pl-root { max-width: 1180px; margin: 0 auto; padding: 0 16px; }
384
385 /* Hero */
386 .pl-hero {
387 text-align: center;
388 padding: var(--s-16) 0 var(--s-10);
389 max-width: 920px;
390 margin: 0 auto;
391 position: relative;
392 }
393 .pl-hero::before {
394 content: '';
395 position: absolute;
396 top: 0; left: 50%;
397 transform: translateX(-50%);
398 width: 70%; height: 60%;
6fd5915Claude399 background: radial-gradient(ellipse at center, rgba(91,110,232,0.14), transparent 65%);
5f2e749Claude400 z-index: -1;
401 pointer-events: none;
402 }
8929744Claude403 /* 2026 polish — gradient hairline accent across the top of the hero
404 (matches /admin, /admin/ops, error pages). */
405 .pl-hero-hairline {
406 position: absolute;
407 top: 0; left: 8%; right: 8%;
408 height: 2px;
6fd5915Claude409 background: linear-gradient(90deg, transparent 0%, #5b6ee8 30%, #5f8fa0 70%, transparent 100%);
8929744Claude410 opacity: 0.65;
411 pointer-events: none;
412 z-index: 0;
413 border-radius: 2px;
414 }
415 /* 2026 polish — soft animated radial orb behind the headline. */
416 .pl-hero-orb {
417 position: absolute;
418 top: 8%; left: 50%;
419 transform: translateX(-50%);
420 width: 420px; height: 420px;
6fd5915Claude421 background: radial-gradient(circle, rgba(91,110,232,0.20), rgba(95,143,160,0.10) 45%, transparent 70%);
8929744Claude422 filter: blur(80px);
423 opacity: 0.7;
424 pointer-events: none;
425 z-index: -1;
426 animation: plHeroOrb 16s ease-in-out infinite;
427 }
428 @keyframes plHeroOrb {
429 0%, 100% { transform: translateX(-50%) scale(1); opacity: 0.55; }
430 50% { transform: translateX(-50%) scale(1.12); opacity: 0.85; }
431 }
432 @media (prefers-reduced-motion: reduce) {
433 .pl-hero-orb { animation: none; }
434 }
5f2e749Claude435 .pl-hero .eyebrow { justify-content: center; margin: 0 auto var(--s-4); }
a65653bClaude436 .pl-hero-speed {
437 font-family: var(--font-mono);
438 font-size: clamp(13px, 1.4vw, 16px);
439 font-weight: 600;
440 letter-spacing: 0.04em;
441 color: var(--accent);
442 margin: 0 0 var(--s-3);
443 text-transform: uppercase;
444 }
5f2e749Claude445 .pl-hero-title {
446 font-size: clamp(36px, 6.5vw, 72px);
447 line-height: 1.02;
448 letter-spacing: -0.038em;
449 margin: 0 0 var(--s-5);
450 }
451 .pl-hero-sub {
452 font-size: clamp(15px, 1.5vw, 18px);
453 color: var(--text-muted);
454 max-width: 640px;
455 margin: 0 auto;
456 line-height: 1.55;
457 }
458 .pl-hero-jumps {
459 display: flex;
460 gap: 18px;
461 justify-content: center;
462 flex-wrap: wrap;
463 margin-top: var(--s-7);
464 }
465 .pl-jump {
466 font-family: var(--font-mono);
467 font-size: 12px;
468 color: var(--text-muted);
469 text-decoration: none;
470 padding: 6px 12px;
471 border: 1px solid var(--border-subtle);
472 border-radius: var(--r-full);
473 transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
474 }
6fd5915Claude475 .pl-jump:hover { color: var(--accent); border-color: rgba(91,110,232,0.35); }
5f2e749Claude476
477 /* Plan cards */
478 .pl-plans {
479 display: grid;
480 grid-template-columns: repeat(4, 1fr);
481 gap: 14px;
482 margin: var(--s-10) auto var(--s-14);
483 align-items: stretch;
484 }
485 .pl-card {
486 position: relative;
487 background: var(--bg-elevated);
488 border: 1px solid var(--border);
489 border-radius: var(--r-lg);
490 padding: var(--s-7) var(--s-6);
491 display: flex;
492 flex-direction: column;
493 gap: var(--s-4);
494 transition: border-color var(--t-base) var(--ease), transform var(--t-base) var(--ease-out-quart);
495 }
496 .pl-card:hover { border-color: var(--border-strong); transform: translateY(-3px); }
497 .pl-card-hl {
6fd5915Claude498 border-color: rgba(91,110,232,0.40);
499 box-shadow: var(--elev-2), 0 0 0 1px rgba(91,110,232,0.30);
5f2e749Claude500 background:
6fd5915Claude501 linear-gradient(180deg, rgba(91,110,232,0.05), transparent 50%),
5f2e749Claude502 var(--bg-elevated);
503 }
504 .pl-card-badge {
505 position: absolute;
506 top: -10px;
507 left: 50%;
508 transform: translateX(-50%);
509 padding: 3px 12px;
510 background: var(--accent-gradient);
511 color: #fff;
512 font-family: var(--font-mono);
513 font-size: 10px;
514 letter-spacing: 0.1em;
515 text-transform: uppercase;
516 font-weight: 600;
517 border-radius: var(--r-full);
6fd5915Claude518 box-shadow: 0 4px 14px -2px rgba(91,110,232,0.45);
5f2e749Claude519 white-space: nowrap;
520 }
521 .pl-card-name {
522 font-family: var(--font-mono);
523 font-size: 11px;
524 text-transform: uppercase;
525 letter-spacing: 0.16em;
526 color: var(--text-muted);
527 }
528 .pl-card-price { display: flex; align-items: baseline; gap: 6px; }
529 .pl-card-num {
530 font-family: var(--font-display);
531 font-size: 32px;
532 font-weight: 600;
533 letter-spacing: -0.03em;
534 color: var(--text-strong);
535 }
536 .pl-card-tag {
537 font-size: var(--t-sm);
538 color: var(--text-muted);
539 line-height: 1.5;
540 margin: 0;
541 }
542 .pl-card-feats {
543 list-style: none;
544 padding: 0;
545 margin: 0;
546 display: flex;
547 flex-direction: column;
548 gap: 7px;
549 font-size: 13px;
550 color: var(--text);
551 }
552 .pl-card-feats li {
553 display: flex;
554 align-items: flex-start;
555 gap: 9px;
556 line-height: 1.45;
557 }
558 .pl-check {
559 color: var(--accent);
560 font-weight: 600;
561 flex-shrink: 0;
562 line-height: 1.45;
563 }
564 .pl-card-cta { margin-top: auto; }
565
566 /* Section base */
567 .pl-section { margin: var(--s-14) auto; }
568
569 /* Free-tier block */
570 .pl-free-grid {
571 list-style: none;
572 padding: 0;
573 margin: var(--s-6) auto 0;
574 display: grid;
575 grid-template-columns: repeat(2, 1fr);
576 gap: 10px 32px;
577 max-width: 880px;
578 }
579 .pl-free-item {
580 display: flex;
581 align-items: flex-start;
582 gap: 10px;
583 padding: 12px 16px;
584 background: var(--bg-elevated);
585 border: 1px solid var(--border-subtle);
586 border-radius: var(--r);
587 font-size: var(--t-sm);
588 color: var(--text);
589 line-height: 1.45;
590 transition: border-color var(--t-fast) var(--ease);
591 }
6fd5915Claude592 .pl-free-item:hover { border-color: rgba(91,110,232,0.35); }
5f2e749Claude593 .pl-free-check {
594 color: var(--green);
595 font-weight: 700;
596 flex-shrink: 0;
597 }
598
599 /* Self-host vs Cloud */
600 .pl-host-grid {
601 display: grid;
602 grid-template-columns: 1fr 1fr;
603 gap: 16px;
604 max-width: 920px;
605 margin: 0 auto;
606 }
607 .pl-host-col {
608 background: var(--bg-elevated);
609 border: 1px solid var(--border);
610 border-radius: var(--r-lg);
611 padding: var(--s-7) var(--s-6);
612 display: flex;
613 flex-direction: column;
614 gap: var(--s-4);
615 }
616 .pl-host-cloud {
6fd5915Claude617 border-color: rgba(91,110,232,0.35);
618 box-shadow: 0 0 0 1px rgba(91,110,232,0.20);
5f2e749Claude619 background:
6fd5915Claude620 linear-gradient(180deg, rgba(91,110,232,0.04), transparent 60%),
5f2e749Claude621 var(--bg-elevated);
622 }
623 .pl-host-name {
624 font-family: var(--font-mono);
625 font-size: 11px;
626 text-transform: uppercase;
627 letter-spacing: 0.16em;
628 color: var(--text-muted);
629 }
630 .pl-host-price {
631 font-family: var(--font-display);
632 font-size: 28px;
633 font-weight: 600;
634 letter-spacing: -0.025em;
635 color: var(--text-strong);
636 }
637 .pl-host-feats {
638 list-style: none;
639 padding: 0;
640 margin: 0;
641 display: flex;
642 flex-direction: column;
643 gap: 8px;
644 font-size: var(--t-sm);
645 color: var(--text);
646 }
647 .pl-host-feats li {
648 display: flex;
649 gap: 9px;
650 line-height: 1.5;
651 }
652 .pl-host-feats li::before {
653 content: '→';
654 color: var(--accent);
655 flex-shrink: 0;
656 }
657 .pl-host-feats code {
658 background: var(--bg-secondary);
659 border: 1px solid var(--border-subtle);
660 padding: 1px 6px;
661 border-radius: 4px;
662 font-size: 11.5px;
663 font-family: var(--font-mono);
664 color: var(--accent);
665 }
666 .pl-host-cta { margin-top: auto; }
667
13ac035Claude668 /* ------------------- Bundle-math comparison (vs GitHub) ------------------- */
669 /* 2026 polish — side-by-side cost comparison that makes the AI-bundled
670 value proposition unmissable. Same visual rhythm as .pl-host-grid; the
671 "us" side wins via accent border + subtle gradient glow. */
672 .pl-compare {
41a1450Claude673 margin-top: var(--s-14);
13ac035Claude674 }
675 .pl-compare-grid {
676 display: grid;
677 grid-template-columns: 1fr 1fr;
678 gap: var(--space-5);
679 max-width: 1040px;
680 margin: 0 auto;
681 }
682 .pl-compare-col {
683 background: var(--bg-elevated);
684 border: 1px solid var(--border);
685 border-radius: 16px;
686 padding: var(--space-5) var(--space-5) var(--space-6);
687 display: flex;
688 flex-direction: column;
689 transition: transform var(--t-base, 180ms) var(--ease, ease),
690 box-shadow var(--t-base, 180ms) var(--ease, ease);
691 }
692 .pl-compare-col:hover { transform: translateY(-2px); }
693 .pl-compare-us {
694 position: relative;
695 border: 1px solid transparent;
696 background-image:
697 linear-gradient(var(--bg-elevated), var(--bg-elevated)),
6fd5915Claude698 linear-gradient(135deg, #5b6ee8 0%, #5f8fa0 100%);
13ac035Claude699 background-origin: border-box;
700 background-clip: padding-box, border-box;
6fd5915Claude701 box-shadow: 0 16px 56px -12px rgba(91,110,232, 0.20);
13ac035Claude702 }
703 .pl-compare-us::before {
704 content: 'Better deal';
705 position: absolute;
706 top: -12px;
707 left: 50%;
708 transform: translateX(-50%);
709 padding: 4px 12px;
710 border-radius: 999px;
6fd5915Claude711 background: linear-gradient(135deg, #5b6ee8 0%, #5f8fa0 100%);
13ac035Claude712 color: #fff;
713 font-size: 11px;
714 font-weight: 600;
715 letter-spacing: 0.04em;
716 text-transform: uppercase;
717 white-space: nowrap;
718 }
719 .pl-compare-name {
720 font-size: 14px;
721 font-weight: 600;
722 color: var(--text-muted);
723 text-transform: uppercase;
724 letter-spacing: 0.08em;
725 margin-bottom: var(--space-2);
726 }
727 .pl-compare-us .pl-compare-name {
728 color: var(--accent);
729 }
730 .pl-compare-price {
731 font-family: var(--font-display);
732 font-size: 56px;
733 font-weight: 800;
734 letter-spacing: -0.03em;
735 line-height: 1;
736 color: var(--text-strong);
737 margin-bottom: var(--space-5);
738 }
739 .pl-compare-per {
740 font-size: 16px;
741 font-weight: 500;
742 color: var(--text-muted);
743 margin-left: 4px;
744 letter-spacing: 0;
745 }
746 .pl-compare-feats {
747 list-style: none;
748 padding: 0;
749 margin: 0;
750 display: flex;
751 flex-direction: column;
752 gap: 10px;
753 font-size: 14.5px;
754 line-height: 1.5;
755 color: var(--text);
756 }
757 .pl-compare-bullet {
758 display: inline-block;
759 min-width: 44px;
760 font-family: var(--font-mono);
761 font-weight: 600;
762 color: var(--text-muted);
763 margin-right: 8px;
764 }
765 .pl-compare-check {
766 display: inline-block;
767 min-width: 22px;
768 color: var(--accent);
769 font-weight: 700;
770 }
771 .pl-compare-missing {
772 color: var(--text-muted);
773 font-style: italic;
774 }
775 .pl-compare-footer {
776 max-width: 720px;
777 margin: var(--space-6) auto 0;
778 text-align: center;
779 font-size: 16px;
780 line-height: 1.55;
781 color: var(--text);
782 }
783 .pl-compare-footer strong {
784 color: var(--text-strong);
6fd5915Claude785 background-image: linear-gradient(transparent 62%, rgba(91,110,232, 0.18) 62%);
13ac035Claude786 }
787 @media (max-width: 720px) {
788 .pl-compare-grid { grid-template-columns: 1fr; }
789 .pl-compare-price { font-size: 44px; }
790 }
791
5f2e749Claude792 /* FAQ */
793 .pl-faq {
794 max-width: 760px;
795 margin: 0 auto;
796 border: 1px solid var(--border);
797 border-radius: var(--r-lg);
798 overflow: hidden;
799 background: var(--bg-elevated);
800 }
801 .pl-faq-item { border-bottom: 1px solid var(--border-subtle); }
802 .pl-faq-item:last-child { border-bottom: none; }
803 .pl-faq-q {
804 display: flex;
805 justify-content: space-between;
806 align-items: center;
807 gap: 16px;
808 padding: 18px 24px;
809 cursor: pointer;
810 font-size: var(--t-md);
811 font-weight: 500;
812 color: var(--text-strong);
813 list-style: none;
814 transition: background var(--t-fast) var(--ease);
815 }
816 .pl-faq-q::-webkit-details-marker { display: none; }
817 .pl-faq-q:hover { background: var(--bg-hover); }
818 .pl-faq-toggle {
819 font-family: var(--font-mono);
820 font-size: 18px;
821 color: var(--text-muted);
822 transition: transform var(--t-base) var(--ease-spring);
823 flex-shrink: 0;
824 }
825 .pl-faq-item[open] .pl-faq-toggle { transform: rotate(45deg); color: var(--accent); }
826 .pl-faq-a {
827 padding: 0 24px 20px;
828 color: var(--text-muted);
829 font-size: var(--t-sm);
830 line-height: 1.6;
831 margin: 0;
832 }
833
834 /* CTA */
835 .pl-cta-wrap { margin: var(--s-16) auto var(--s-10); }
836 .pl-cta {
837 position: relative;
838 text-align: center;
839 padding: var(--s-12) var(--s-6);
840 border: 1px solid var(--border-strong);
841 border-radius: var(--r-2xl);
842 background:
6fd5915Claude843 radial-gradient(60% 100% at 50% 0%, rgba(91,110,232,0.14), transparent 65%),
5f2e749Claude844 var(--bg-elevated);
845 overflow: hidden;
846 }
847 .pl-cta-title {
848 font-family: var(--font-display);
849 font-size: clamp(24px, 3.5vw, 40px);
850 line-height: 1.1;
851 letter-spacing: -0.025em;
852 font-weight: 600;
853 margin: 0 0 var(--s-3);
854 color: var(--text-strong);
855 }
856 .pl-cta-sub {
857 font-size: var(--t-md);
858 color: var(--text-muted);
859 margin: 0 auto var(--s-6);
860 max-width: 480px;
861 }
862 .pl-cta-buttons {
863 display: flex;
864 gap: 12px;
865 justify-content: center;
866 flex-wrap: wrap;
867 }
868
a65653bClaude869 /* Every-plan strip */
870 .pl-every-plan { margin: calc(var(--s-14) * -0.5) auto var(--s-10); }
871 .pl-every-plan-inner {
872 display: flex;
873 align-items: center;
874 gap: 20px;
875 flex-wrap: wrap;
876 justify-content: center;
877 padding: 14px 28px;
878 border: 1px solid var(--border-subtle);
879 border-radius: var(--r-full);
880 background: var(--bg-elevated);
881 max-width: 760px;
882 margin: 0 auto;
883 font-size: 13px;
884 }
885 .pl-every-plan-label {
886 font-family: var(--font-mono);
887 font-size: 11px;
888 text-transform: uppercase;
889 letter-spacing: 0.14em;
890 color: var(--text-muted);
891 white-space: nowrap;
892 }
893 .pl-every-plan-list {
894 list-style: none;
895 padding: 0;
896 margin: 0;
897 display: flex;
898 gap: 0;
899 flex-wrap: wrap;
900 justify-content: center;
901 }
902 .pl-every-plan-list li {
903 color: var(--text);
904 display: flex;
905 align-items: center;
906 gap: 10px;
907 }
908 .pl-every-plan-list li + li::before {
909 content: '·';
910 color: var(--text-muted);
911 margin-right: 10px;
912 }
913
5f2e749Claude914 /* Responsive */
915 @media (max-width: 960px) {
916 .pl-plans { grid-template-columns: repeat(2, 1fr); }
917 }
918 @media (max-width: 720px) {
919 .pl-host-grid { grid-template-columns: 1fr; }
920 .pl-free-grid { grid-template-columns: 1fr; }
921 }
922 @media (max-width: 560px) {
923 .pl-plans { grid-template-columns: 1fr; }
924 .pl-cta-buttons .btn { width: 100%; justify-content: center; }
925 }
926`;
927
928export default pricing;