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

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.tsxBlame932 lines · 3 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;
8102dd4ccantynz-alt392 /* The decorative 420px orb is absolutely positioned and centred, so on
393 narrow viewports it hangs ~15px past each edge and gives the whole
394 document a horizontal scrollbar. Clip it — it is purely decorative. */
395 overflow: hidden;
5f2e749Claude396 }
397 .pl-hero::before {
398 content: '';
399 position: absolute;
400 top: 0; left: 50%;
401 transform: translateX(-50%);
402 width: 70%; height: 60%;
6fd5915Claude403 background: radial-gradient(ellipse at center, rgba(91,110,232,0.14), transparent 65%);
5f2e749Claude404 z-index: -1;
405 pointer-events: none;
406 }
8929744Claude407 /* 2026 polish — gradient hairline accent across the top of the hero
408 (matches /admin, /admin/ops, error pages). */
409 .pl-hero-hairline {
410 position: absolute;
411 top: 0; left: 8%; right: 8%;
412 height: 2px;
6fd5915Claude413 background: linear-gradient(90deg, transparent 0%, #5b6ee8 30%, #5f8fa0 70%, transparent 100%);
8929744Claude414 opacity: 0.65;
415 pointer-events: none;
416 z-index: 0;
417 border-radius: 2px;
418 }
419 /* 2026 polish — soft animated radial orb behind the headline. */
420 .pl-hero-orb {
421 position: absolute;
422 top: 8%; left: 50%;
423 transform: translateX(-50%);
424 width: 420px; height: 420px;
6fd5915Claude425 background: radial-gradient(circle, rgba(91,110,232,0.20), rgba(95,143,160,0.10) 45%, transparent 70%);
8929744Claude426 filter: blur(80px);
427 opacity: 0.7;
428 pointer-events: none;
429 z-index: -1;
430 animation: plHeroOrb 16s ease-in-out infinite;
431 }
432 @keyframes plHeroOrb {
433 0%, 100% { transform: translateX(-50%) scale(1); opacity: 0.55; }
434 50% { transform: translateX(-50%) scale(1.12); opacity: 0.85; }
435 }
436 @media (prefers-reduced-motion: reduce) {
437 .pl-hero-orb { animation: none; }
438 }
5f2e749Claude439 .pl-hero .eyebrow { justify-content: center; margin: 0 auto var(--s-4); }
a65653bClaude440 .pl-hero-speed {
441 font-family: var(--font-mono);
442 font-size: clamp(13px, 1.4vw, 16px);
443 font-weight: 600;
444 letter-spacing: 0.04em;
445 color: var(--accent);
446 margin: 0 0 var(--s-3);
447 text-transform: uppercase;
448 }
5f2e749Claude449 .pl-hero-title {
450 font-size: clamp(36px, 6.5vw, 72px);
451 line-height: 1.02;
452 letter-spacing: -0.038em;
453 margin: 0 0 var(--s-5);
454 }
455 .pl-hero-sub {
456 font-size: clamp(15px, 1.5vw, 18px);
457 color: var(--text-muted);
458 max-width: 640px;
459 margin: 0 auto;
460 line-height: 1.55;
461 }
462 .pl-hero-jumps {
463 display: flex;
464 gap: 18px;
465 justify-content: center;
466 flex-wrap: wrap;
467 margin-top: var(--s-7);
468 }
469 .pl-jump {
470 font-family: var(--font-mono);
471 font-size: 12px;
472 color: var(--text-muted);
473 text-decoration: none;
474 padding: 6px 12px;
475 border: 1px solid var(--border-subtle);
476 border-radius: var(--r-full);
477 transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
478 }
6fd5915Claude479 .pl-jump:hover { color: var(--accent); border-color: rgba(91,110,232,0.35); }
5f2e749Claude480
481 /* Plan cards */
482 .pl-plans {
483 display: grid;
484 grid-template-columns: repeat(4, 1fr);
485 gap: 14px;
486 margin: var(--s-10) auto var(--s-14);
487 align-items: stretch;
488 }
489 .pl-card {
490 position: relative;
491 background: var(--bg-elevated);
492 border: 1px solid var(--border);
493 border-radius: var(--r-lg);
494 padding: var(--s-7) var(--s-6);
495 display: flex;
496 flex-direction: column;
497 gap: var(--s-4);
498 transition: border-color var(--t-base) var(--ease), transform var(--t-base) var(--ease-out-quart);
499 }
500 .pl-card:hover { border-color: var(--border-strong); transform: translateY(-3px); }
501 .pl-card-hl {
6fd5915Claude502 border-color: rgba(91,110,232,0.40);
503 box-shadow: var(--elev-2), 0 0 0 1px rgba(91,110,232,0.30);
5f2e749Claude504 background:
6fd5915Claude505 linear-gradient(180deg, rgba(91,110,232,0.05), transparent 50%),
5f2e749Claude506 var(--bg-elevated);
507 }
508 .pl-card-badge {
509 position: absolute;
510 top: -10px;
511 left: 50%;
512 transform: translateX(-50%);
513 padding: 3px 12px;
514 background: var(--accent-gradient);
515 color: #fff;
516 font-family: var(--font-mono);
517 font-size: 10px;
518 letter-spacing: 0.1em;
519 text-transform: uppercase;
520 font-weight: 600;
521 border-radius: var(--r-full);
6fd5915Claude522 box-shadow: 0 4px 14px -2px rgba(91,110,232,0.45);
5f2e749Claude523 white-space: nowrap;
524 }
525 .pl-card-name {
526 font-family: var(--font-mono);
527 font-size: 11px;
528 text-transform: uppercase;
529 letter-spacing: 0.16em;
530 color: var(--text-muted);
531 }
532 .pl-card-price { display: flex; align-items: baseline; gap: 6px; }
533 .pl-card-num {
534 font-family: var(--font-display);
535 font-size: 32px;
536 font-weight: 600;
537 letter-spacing: -0.03em;
538 color: var(--text-strong);
539 }
540 .pl-card-tag {
541 font-size: var(--t-sm);
542 color: var(--text-muted);
543 line-height: 1.5;
544 margin: 0;
545 }
546 .pl-card-feats {
547 list-style: none;
548 padding: 0;
549 margin: 0;
550 display: flex;
551 flex-direction: column;
552 gap: 7px;
553 font-size: 13px;
554 color: var(--text);
555 }
556 .pl-card-feats li {
557 display: flex;
558 align-items: flex-start;
559 gap: 9px;
560 line-height: 1.45;
561 }
562 .pl-check {
563 color: var(--accent);
564 font-weight: 600;
565 flex-shrink: 0;
566 line-height: 1.45;
567 }
568 .pl-card-cta { margin-top: auto; }
569
570 /* Section base */
571 .pl-section { margin: var(--s-14) auto; }
572
573 /* Free-tier block */
574 .pl-free-grid {
575 list-style: none;
576 padding: 0;
577 margin: var(--s-6) auto 0;
578 display: grid;
579 grid-template-columns: repeat(2, 1fr);
580 gap: 10px 32px;
581 max-width: 880px;
582 }
583 .pl-free-item {
584 display: flex;
585 align-items: flex-start;
586 gap: 10px;
587 padding: 12px 16px;
588 background: var(--bg-elevated);
589 border: 1px solid var(--border-subtle);
590 border-radius: var(--r);
591 font-size: var(--t-sm);
592 color: var(--text);
593 line-height: 1.45;
594 transition: border-color var(--t-fast) var(--ease);
595 }
6fd5915Claude596 .pl-free-item:hover { border-color: rgba(91,110,232,0.35); }
5f2e749Claude597 .pl-free-check {
598 color: var(--green);
599 font-weight: 700;
600 flex-shrink: 0;
601 }
602
603 /* Self-host vs Cloud */
604 .pl-host-grid {
605 display: grid;
606 grid-template-columns: 1fr 1fr;
607 gap: 16px;
608 max-width: 920px;
609 margin: 0 auto;
610 }
611 .pl-host-col {
612 background: var(--bg-elevated);
613 border: 1px solid var(--border);
614 border-radius: var(--r-lg);
615 padding: var(--s-7) var(--s-6);
616 display: flex;
617 flex-direction: column;
618 gap: var(--s-4);
619 }
620 .pl-host-cloud {
6fd5915Claude621 border-color: rgba(91,110,232,0.35);
622 box-shadow: 0 0 0 1px rgba(91,110,232,0.20);
5f2e749Claude623 background:
6fd5915Claude624 linear-gradient(180deg, rgba(91,110,232,0.04), transparent 60%),
5f2e749Claude625 var(--bg-elevated);
626 }
627 .pl-host-name {
628 font-family: var(--font-mono);
629 font-size: 11px;
630 text-transform: uppercase;
631 letter-spacing: 0.16em;
632 color: var(--text-muted);
633 }
634 .pl-host-price {
635 font-family: var(--font-display);
636 font-size: 28px;
637 font-weight: 600;
638 letter-spacing: -0.025em;
639 color: var(--text-strong);
640 }
641 .pl-host-feats {
642 list-style: none;
643 padding: 0;
644 margin: 0;
645 display: flex;
646 flex-direction: column;
647 gap: 8px;
648 font-size: var(--t-sm);
649 color: var(--text);
650 }
651 .pl-host-feats li {
652 display: flex;
653 gap: 9px;
654 line-height: 1.5;
655 }
656 .pl-host-feats li::before {
657 content: '→';
658 color: var(--accent);
659 flex-shrink: 0;
660 }
661 .pl-host-feats code {
662 background: var(--bg-secondary);
663 border: 1px solid var(--border-subtle);
664 padding: 1px 6px;
665 border-radius: 4px;
666 font-size: 11.5px;
667 font-family: var(--font-mono);
668 color: var(--accent);
669 }
670 .pl-host-cta { margin-top: auto; }
671
13ac035Claude672 /* ------------------- Bundle-math comparison (vs GitHub) ------------------- */
673 /* 2026 polish — side-by-side cost comparison that makes the AI-bundled
674 value proposition unmissable. Same visual rhythm as .pl-host-grid; the
675 "us" side wins via accent border + subtle gradient glow. */
676 .pl-compare {
41a1450Claude677 margin-top: var(--s-14);
13ac035Claude678 }
679 .pl-compare-grid {
680 display: grid;
681 grid-template-columns: 1fr 1fr;
682 gap: var(--space-5);
683 max-width: 1040px;
684 margin: 0 auto;
685 }
686 .pl-compare-col {
687 background: var(--bg-elevated);
688 border: 1px solid var(--border);
689 border-radius: 16px;
690 padding: var(--space-5) var(--space-5) var(--space-6);
691 display: flex;
692 flex-direction: column;
693 transition: transform var(--t-base, 180ms) var(--ease, ease),
694 box-shadow var(--t-base, 180ms) var(--ease, ease);
695 }
696 .pl-compare-col:hover { transform: translateY(-2px); }
697 .pl-compare-us {
698 position: relative;
699 border: 1px solid transparent;
700 background-image:
701 linear-gradient(var(--bg-elevated), var(--bg-elevated)),
6fd5915Claude702 linear-gradient(135deg, #5b6ee8 0%, #5f8fa0 100%);
13ac035Claude703 background-origin: border-box;
704 background-clip: padding-box, border-box;
6fd5915Claude705 box-shadow: 0 16px 56px -12px rgba(91,110,232, 0.20);
13ac035Claude706 }
707 .pl-compare-us::before {
708 content: 'Better deal';
709 position: absolute;
710 top: -12px;
711 left: 50%;
712 transform: translateX(-50%);
713 padding: 4px 12px;
714 border-radius: 999px;
6fd5915Claude715 background: linear-gradient(135deg, #5b6ee8 0%, #5f8fa0 100%);
13ac035Claude716 color: #fff;
717 font-size: 11px;
718 font-weight: 600;
719 letter-spacing: 0.04em;
720 text-transform: uppercase;
721 white-space: nowrap;
722 }
723 .pl-compare-name {
724 font-size: 14px;
725 font-weight: 600;
726 color: var(--text-muted);
727 text-transform: uppercase;
728 letter-spacing: 0.08em;
729 margin-bottom: var(--space-2);
730 }
731 .pl-compare-us .pl-compare-name {
732 color: var(--accent);
733 }
734 .pl-compare-price {
735 font-family: var(--font-display);
736 font-size: 56px;
737 font-weight: 800;
738 letter-spacing: -0.03em;
739 line-height: 1;
740 color: var(--text-strong);
741 margin-bottom: var(--space-5);
742 }
743 .pl-compare-per {
744 font-size: 16px;
745 font-weight: 500;
746 color: var(--text-muted);
747 margin-left: 4px;
748 letter-spacing: 0;
749 }
750 .pl-compare-feats {
751 list-style: none;
752 padding: 0;
753 margin: 0;
754 display: flex;
755 flex-direction: column;
756 gap: 10px;
757 font-size: 14.5px;
758 line-height: 1.5;
759 color: var(--text);
760 }
761 .pl-compare-bullet {
762 display: inline-block;
763 min-width: 44px;
764 font-family: var(--font-mono);
765 font-weight: 600;
766 color: var(--text-muted);
767 margin-right: 8px;
768 }
769 .pl-compare-check {
770 display: inline-block;
771 min-width: 22px;
772 color: var(--accent);
773 font-weight: 700;
774 }
775 .pl-compare-missing {
776 color: var(--text-muted);
777 font-style: italic;
778 }
779 .pl-compare-footer {
780 max-width: 720px;
781 margin: var(--space-6) auto 0;
782 text-align: center;
783 font-size: 16px;
784 line-height: 1.55;
785 color: var(--text);
786 }
787 .pl-compare-footer strong {
788 color: var(--text-strong);
6fd5915Claude789 background-image: linear-gradient(transparent 62%, rgba(91,110,232, 0.18) 62%);
13ac035Claude790 }
791 @media (max-width: 720px) {
792 .pl-compare-grid { grid-template-columns: 1fr; }
793 .pl-compare-price { font-size: 44px; }
794 }
795
5f2e749Claude796 /* FAQ */
797 .pl-faq {
798 max-width: 760px;
799 margin: 0 auto;
800 border: 1px solid var(--border);
801 border-radius: var(--r-lg);
802 overflow: hidden;
803 background: var(--bg-elevated);
804 }
805 .pl-faq-item { border-bottom: 1px solid var(--border-subtle); }
806 .pl-faq-item:last-child { border-bottom: none; }
807 .pl-faq-q {
808 display: flex;
809 justify-content: space-between;
810 align-items: center;
811 gap: 16px;
812 padding: 18px 24px;
813 cursor: pointer;
814 font-size: var(--t-md);
815 font-weight: 500;
816 color: var(--text-strong);
817 list-style: none;
818 transition: background var(--t-fast) var(--ease);
819 }
820 .pl-faq-q::-webkit-details-marker { display: none; }
821 .pl-faq-q:hover { background: var(--bg-hover); }
822 .pl-faq-toggle {
823 font-family: var(--font-mono);
824 font-size: 18px;
825 color: var(--text-muted);
826 transition: transform var(--t-base) var(--ease-spring);
827 flex-shrink: 0;
828 }
829 .pl-faq-item[open] .pl-faq-toggle { transform: rotate(45deg); color: var(--accent); }
830 .pl-faq-a {
831 padding: 0 24px 20px;
832 color: var(--text-muted);
833 font-size: var(--t-sm);
834 line-height: 1.6;
835 margin: 0;
836 }
837
838 /* CTA */
839 .pl-cta-wrap { margin: var(--s-16) auto var(--s-10); }
840 .pl-cta {
841 position: relative;
842 text-align: center;
843 padding: var(--s-12) var(--s-6);
844 border: 1px solid var(--border-strong);
845 border-radius: var(--r-2xl);
846 background:
6fd5915Claude847 radial-gradient(60% 100% at 50% 0%, rgba(91,110,232,0.14), transparent 65%),
5f2e749Claude848 var(--bg-elevated);
849 overflow: hidden;
850 }
851 .pl-cta-title {
852 font-family: var(--font-display);
853 font-size: clamp(24px, 3.5vw, 40px);
854 line-height: 1.1;
855 letter-spacing: -0.025em;
856 font-weight: 600;
857 margin: 0 0 var(--s-3);
858 color: var(--text-strong);
859 }
860 .pl-cta-sub {
861 font-size: var(--t-md);
862 color: var(--text-muted);
863 margin: 0 auto var(--s-6);
864 max-width: 480px;
865 }
866 .pl-cta-buttons {
867 display: flex;
868 gap: 12px;
869 justify-content: center;
870 flex-wrap: wrap;
871 }
872
a65653bClaude873 /* Every-plan strip */
874 .pl-every-plan { margin: calc(var(--s-14) * -0.5) auto var(--s-10); }
875 .pl-every-plan-inner {
876 display: flex;
877 align-items: center;
878 gap: 20px;
879 flex-wrap: wrap;
880 justify-content: center;
881 padding: 14px 28px;
882 border: 1px solid var(--border-subtle);
883 border-radius: var(--r-full);
884 background: var(--bg-elevated);
885 max-width: 760px;
886 margin: 0 auto;
887 font-size: 13px;
888 }
889 .pl-every-plan-label {
890 font-family: var(--font-mono);
891 font-size: 11px;
892 text-transform: uppercase;
893 letter-spacing: 0.14em;
894 color: var(--text-muted);
895 white-space: nowrap;
896 }
897 .pl-every-plan-list {
898 list-style: none;
899 padding: 0;
900 margin: 0;
901 display: flex;
902 gap: 0;
903 flex-wrap: wrap;
904 justify-content: center;
905 }
906 .pl-every-plan-list li {
907 color: var(--text);
908 display: flex;
909 align-items: center;
910 gap: 10px;
911 }
912 .pl-every-plan-list li + li::before {
913 content: '·';
914 color: var(--text-muted);
915 margin-right: 10px;
916 }
917
5f2e749Claude918 /* Responsive */
919 @media (max-width: 960px) {
920 .pl-plans { grid-template-columns: repeat(2, 1fr); }
921 }
922 @media (max-width: 720px) {
923 .pl-host-grid { grid-template-columns: 1fr; }
924 .pl-free-grid { grid-template-columns: 1fr; }
925 }
926 @media (max-width: 560px) {
927 .pl-plans { grid-template-columns: 1fr; }
928 .pl-cta-buttons .btn { width: 100%; justify-content: center; }
929 }
930`;
931
932export default pricing;