From 834ddc226f28cf728b2f5b0fe9bf363443ed7ee3 Mon Sep 17 00:00:00 2001
From: lawrencecchen <54008264+lawrencecchen@users.noreply.github.com>
Date: Sat, 1 Aug 2026 01:51:22 -0700
Subject: [PATCH 1/6] Simplify annual pricing labels
---
web/app/[locale]/dashboard/billing/page.tsx | 27 ---------------------
web/app/[locale]/pricing/page.tsx | 26 --------------------
web/app/app-pricing/page.tsx | 26 --------------------
web/messages/en.json | 5 ++--
web/messages/ja.json | 5 ++--
web/tests/app-pricing-page.test.tsx | 8 +++---
web/tests/dashboard-billing-page.test.tsx | 8 +++---
web/tests/pricing-page.test.tsx | 8 +++---
8 files changed, 16 insertions(+), 97 deletions(-)
diff --git a/web/app/[locale]/dashboard/billing/page.tsx b/web/app/[locale]/dashboard/billing/page.tsx
index a0460b4f0fd8..0246c35f4db3 100644
--- a/web/app/[locale]/dashboard/billing/page.tsx
+++ b/web/app/[locale]/dashboard/billing/page.tsx
@@ -12,7 +12,6 @@ import { localizedVaultPath, vaultSignInHref } from "@/app/lib/vault-auth";
import {
FeatureList,
PlanCard,
- PrimaryLink,
visibleProFeatures,
} from "@/app/components/pricing-shared";
import {
@@ -291,19 +290,6 @@ function FreePlanUpsell({
annual={pricingT("perMonthBilledYearly")}
/>
}
- priceDetail={
-
Includes:
'); + expect(html).not.toContain('style="min-height:4rem"'); expect(html).toContain("sm:grid-cols-2 min-[1800px]:grid-cols-4"); expect(html.split("api/billing/checkout?plan=pro")).toHaveLength(2); expect(html.split("api/billing/checkout?plan=team")).toHaveLength(2); diff --git a/web/tests/dashboard-billing-page.test.tsx b/web/tests/dashboard-billing-page.test.tsx index 7eae7abf1b1e..da02262e3d4f 100644 --- a/web/tests/dashboard-billing-page.test.tsx +++ b/web/tests/dashboard-billing-page.test.tsx @@ -107,6 +107,9 @@ describe("dashboard billing page", () => { ); expect(html).toContain("Get Pro"); expect(html).toContain("Get Teams"); + expect(html).toContain("/mo."); + expect(html).toContain("/user/mo."); + expect(html).not.toContain('style="min-height:4rem"'); expect(html).toContain('href="/dashboard/testflight"'); expect(html).toContain("Join the iOS beta"); expect(html).toContain("active personal Pro subscribers"); diff --git a/web/tests/pricing-page.test.tsx b/web/tests/pricing-page.test.tsx index 0203c280c22c..4729d2213a7d 100644 --- a/web/tests/pricing-page.test.tsx +++ b/web/tests/pricing-page.test.tsx @@ -79,11 +79,14 @@ describe("localized pricing page", () => { expect(html).not.toContain("/api/billing/portal"); expect(html).not.toContain("Manage billing"); + expect(html).toContain("/mo."); + expect(html).toContain("/user/mo."); expect(html).toContain("$35/user/month"); expect(html).toContain( "/api/billing/checkout?plan=team&cmux_external_browser=1&interval=month", ); expect(html).toContain('Includes:
'); + expect(html).not.toContain('style="min-height:4rem"'); }); test("renders Stack metadata-only Pro snapshots as Free", async () => { From 6502a49a3b8b1ef7135a878277d07621fb17f685 Mon Sep 17 00:00:00 2001 From: lawrencecchen <54008264+lawrencecchen@users.noreply.github.com> Date: Sat, 1 Aug 2026 02:43:16 -0700 Subject: [PATCH 3/6] Align pricing numerals and app grid --- web/app/app-pricing/page.tsx | 2 +- web/app/components/pricing-shared.tsx | 10 +++++++--- web/tests/app-pricing-page.test.tsx | 3 ++- web/tests/dashboard-billing-page.test.tsx | 1 + web/tests/pricing-page.test.tsx | 1 + 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/web/app/app-pricing/page.tsx b/web/app/app-pricing/page.tsx index d10beb657de8..7cb57cd45529 100644 --- a/web/app/app-pricing/page.tsx +++ b/web/app/app-pricing/page.tsx @@ -129,7 +129,7 @@ export default async function AppPricingPage({ surface="app_pricing" /> -Includes:
'); expect(html).not.toContain('style="min-height:4rem"'); - expect(html).toContain("sm:grid-cols-2 min-[1800px]:grid-cols-4"); + expect(html).toContain("text-3xl font-medium tabular-nums tracking-tight"); + expect(html).toContain("sm:grid-cols-2 lg:grid-cols-4"); expect(html.split("api/billing/checkout?plan=pro")).toHaveLength(2); expect(html.split("api/billing/checkout?plan=team")).toHaveLength(2); expect(html).toContain("Compare plans"); diff --git a/web/tests/dashboard-billing-page.test.tsx b/web/tests/dashboard-billing-page.test.tsx index da02262e3d4f..bad3cea0038b 100644 --- a/web/tests/dashboard-billing-page.test.tsx +++ b/web/tests/dashboard-billing-page.test.tsx @@ -110,6 +110,7 @@ describe("dashboard billing page", () => { expect(html).toContain("/mo."); expect(html).toContain("/user/mo."); expect(html).not.toContain('style="min-height:4rem"'); + expect(html).toContain("text-3xl font-medium tabular-nums tracking-tight"); expect(html).toContain('href="/dashboard/testflight"'); expect(html).toContain("Join the iOS beta"); expect(html).toContain("active personal Pro subscribers"); diff --git a/web/tests/pricing-page.test.tsx b/web/tests/pricing-page.test.tsx index 4729d2213a7d..0c92de2cfb21 100644 --- a/web/tests/pricing-page.test.tsx +++ b/web/tests/pricing-page.test.tsx @@ -87,6 +87,7 @@ describe("localized pricing page", () => { ); expect(html).toContain('Includes:
'); expect(html).not.toContain('style="min-height:4rem"'); + expect(html).toContain("text-3xl font-medium tabular-nums tracking-tight"); }); test("renders Stack metadata-only Pro snapshots as Free", async () => { From 61bfa204137da0e576b053ff89463552bf6a4c9c Mon Sep 17 00:00:00 2001 From: lawrencecchen <54008264+lawrencecchen@users.noreply.github.com> Date: Sat, 1 Aug 2026 02:53:34 -0700 Subject: [PATCH 4/6] Remove annual pricing totals --- web/app/[locale]/pricing/page.tsx | 2 -- web/app/app-pricing/page.tsx | 2 -- web/messages/en.json | 28 ++++++++++----------- web/messages/ja.json | 14 +++++------ web/tests/app-pricing-page.test.tsx | 16 +++++++----- web/tests/dashboard-billing-page.test.tsx | 30 +++++++++++++---------- web/tests/pricing-page.test.tsx | 17 +++++++------ 7 files changed, 58 insertions(+), 51 deletions(-) diff --git a/web/app/[locale]/pricing/page.tsx b/web/app/[locale]/pricing/page.tsx index 8335fa518cbc..ade4402c5812 100644 --- a/web/app/[locale]/pricing/page.tsx +++ b/web/app/[locale]/pricing/page.tsx @@ -111,14 +111,12 @@ export default async function PricingPage({ }; const annualComparePrice = t("annualComparePrice", { monthly: PRO_PRICING_USD.year.monthlyEquivalent, - annual: PRO_PRICING_USD.year.billedAmount, }); const teamMonthlyComparePrice = t("teamMonthlyComparePrice", { monthly: TEAM_PRICING_USD.month.monthlyEquivalent, }); const teamAnnualComparePrice = t("teamAnnualComparePrice", { monthly: TEAM_PRICING_USD.year.monthlyEquivalent, - annual: TEAM_PRICING_USD.year.billedAmount, }); const freeFeatures = t.raw("free.features") as string[]; diff --git a/web/app/app-pricing/page.tsx b/web/app/app-pricing/page.tsx index 7cb57cd45529..5c5c25664a26 100644 --- a/web/app/app-pricing/page.tsx +++ b/web/app/app-pricing/page.tsx @@ -86,7 +86,6 @@ export default async function AppPricingPage({ const faqItems = visibleFaqItems(pricing.faq.items as FaqItem[]); const annualComparePrice = pricingMessage(pricing.annualComparePrice, { monthly: PRO_PRICING_USD.year.monthlyEquivalent, - annual: PRO_PRICING_USD.year.billedAmount, }); const teamMonthlyComparePrice = pricingMessage( pricing.teamMonthlyComparePrice, @@ -96,7 +95,6 @@ export default async function AppPricingPage({ pricing.teamAnnualComparePrice, { monthly: TEAM_PRICING_USD.year.monthlyEquivalent, - annual: TEAM_PRICING_USD.year.billedAmount, }, ); diff --git a/web/messages/en.json b/web/messages/en.json index f6635b70c5ec..b960fd7a2ba5 100644 --- a/web/messages/en.json +++ b/web/messages/en.json @@ -143,17 +143,17 @@ "name": "cmux Pro", "activeBody": "Your plan renews on {date}.", "pendingBody": "Your plan is scheduled to end on {date}.", - "monthlyPrice": "$30/month", - "annualPrice": "$288/year", - "legacyAnnualPrice": "$240/year" + "monthlyPrice": "$30/mo", + "annualPrice": "$24/mo, billed annually", + "legacyAnnualPrice": "$20/mo, billed annually" }, "team": { "name": "cmux Team", "fallbackName": "your team", "activeBody": "{team} renews on {date}.", "pendingBody": "{team} is scheduled to end on {date}.", - "price": "$35/seat/month", - "annualPrice": "$336/seat/year" + "price": "$35/seat/mo", + "annualPrice": "$28/seat/mo, billed annually" }, "details": { "renewsOn": "Renews on", @@ -403,17 +403,17 @@ "metaDescriptionNoVault": "cmux is a free, open source macOS terminal for AI coding agents. Upgrade to Pro for Cloud VMs and a model gateway. Enterprise adds SSO and self-hosting.", "metaDescriptionNoVaultShort": "cmux is a free, open source macOS terminal. Pro adds Cloud VMs and a model gateway; Enterprise adds SSO and self-hosting.", "title": "Pricing", - "perMonth": "/mo.", - "perMonthBilledYearly": "/mo.", - "perUserMonth": "/user/mo.", - "perUserMonthBilledYearly": "/user/mo.", + "perMonth": "/mo", + "perMonthBilledYearly": "/mo", + "perUserMonth": "/user/mo", + "perUserMonthBilledYearly": "/user/mo", "billingPeriod": "Billing period", "monthly": "Monthly", "annual": "Annual", "saveAnnual": "Save {discount}%", - "annualComparePrice": "${monthly}/month · ${annual}/year", - "teamMonthlyComparePrice": "${monthly}/user/month", - "teamAnnualComparePrice": "${monthly}/user/month · ${annual}/user/year", + "annualComparePrice": "${monthly}/mo", + "teamMonthlyComparePrice": "${monthly}/user/mo", + "teamAnnualComparePrice": "${monthly}/user/mo", "currentPlan": "Current plan", "manageBilling": "Manage billing", "free": { @@ -671,7 +671,7 @@ }, { "q": "How does billing work?", - "a": "Pro is $30 per month, or $288 per year ($24 per month billed annually, a 20% savings), and includes 20 active compute-hours per month on the 4 vCPU / 16 GB Cloud VM. Beyond that, cloud usage is billed per active compute-hour by VM size (see Cloud VM sizes above). Idle VMs suspend automatically, and running agents in parallel just uses more hours. The terminal and local agents stay free." + "a": "Pro is $30/mo, or $24/mo when billed annually (a 20% savings), and includes 20 active compute-hours per month on the 4 vCPU / 16 GB Cloud VM. Beyond that, cloud usage is billed per active compute-hour by VM size (see Cloud VM sizes above). Idle VMs suspend automatically, and running agents in parallel just uses more hours. The terminal and local agents stay free." }, { "q": "Can I self-host the cloud?", @@ -679,7 +679,7 @@ }, { "q": "Do you have a team plan?", - "a": "Yes. Team is $35 per user per month, or $336 per user per year ($28 per user per month billed annually): unified billing for the whole team, centralized seat management, pooled Cloud VM hours, and shared team rules. Enterprise adds SSO, self-hosting, and audit logs." + "a": "Yes. Team is $35/user/mo, or $28/user/mo when billed annually: unified billing for the whole team, centralized seat management, pooled Cloud VM hours, and shared team rules. Enterprise adds SSO, self-hosting, and audit logs." } ] }, diff --git a/web/messages/ja.json b/web/messages/ja.json index cdd68262dafd..905396b9a84b 100644 --- a/web/messages/ja.json +++ b/web/messages/ja.json @@ -144,8 +144,8 @@ "activeBody": "プランは{date}に更新されます。", "pendingBody": "プランは{date}に終了する予定です。", "monthlyPrice": "$30/月", - "annualPrice": "$288/年", - "legacyAnnualPrice": "$240/年" + "annualPrice": "$24/月(年払い)", + "legacyAnnualPrice": "$20/月(年払い)" }, "team": { "name": "cmux Team", @@ -153,7 +153,7 @@ "activeBody": "{team}は{date}に更新されます。", "pendingBody": "{team}は{date}に終了する予定です。", "price": "$35/シート/月", - "annualPrice": "$336/シート/年" + "annualPrice": "$28/シート/月(年払い)" }, "details": { "renewsOn": "更新日", @@ -411,9 +411,9 @@ "monthly": "月払い", "annual": "年払い", "saveAnnual": "{discount}%割引", - "annualComparePrice": "${monthly}/月 · ${annual}/年", + "annualComparePrice": "${monthly}/月", "teamMonthlyComparePrice": "${monthly}/ユーザー/月", - "teamAnnualComparePrice": "${monthly}/ユーザー/月 · ${annual}/ユーザー/年", + "teamAnnualComparePrice": "${monthly}/ユーザー/月", "currentPlan": "現在のプラン", "manageBilling": "請求を管理", "free": { @@ -671,7 +671,7 @@ }, { "q": "課金はどのように行われますか?", - "a": "Pro は月額 $30、または年額 $288(年払いでは月あたり $24、20%割引)で、4 vCPU / 16 GB の Cloud VM で毎月20時間のアクティブ計算時間が含まれます。それを超えると、VM サイズに応じてアクティブな計算時間あたりで課金されます(上記の Cloud VM サイズを参照)。アイドルの VM は自動的に一時停止し、エージェントを並列実行するとより多くの時間を使うだけです。ターミナルとローカルエージェントは無料のままです。" + "a": "Pro は月払いで $30/月、年払いでは $24/月(20%割引)で、4 vCPU / 16 GB の Cloud VM で毎月20時間のアクティブ計算時間が含まれます。それを超えると、VM サイズに応じてアクティブな計算時間あたりで課金されます(上記の Cloud VM サイズを参照)。アイドルの VM は自動的に一時停止し、エージェントを並列実行するとより多くの時間を使うだけです。ターミナルとローカルエージェントは無料のままです。" }, { "q": "クラウドをセルフホストできますか?", @@ -679,7 +679,7 @@ }, { "q": "チームプランはありますか?", - "a": "はい。Team は1ユーザーあたり月額 $35、または年額 $336(月額換算 $28、20%割引)です。チーム全体の一元請求、シート管理、Cloud VM 時間のプール、共有チームルールが含まれます。Enterprise では SSO、セルフホスト、監査ログが追加されます。" + "a": "はい。Team は月払いで $35/ユーザー/月、年払いでは $28/ユーザー/月です。チーム全体の一元請求、シート管理、Cloud VM 時間のプール、共有チームルールが含まれます。Enterprise では SSO、セルフホスト、監査ログが追加されます。" } ] }, diff --git a/web/tests/app-pricing-page.test.tsx b/web/tests/app-pricing-page.test.tsx index 78b3d58e2334..fee88e3c47d6 100644 --- a/web/tests/app-pricing-page.test.tsx +++ b/web/tests/app-pricing-page.test.tsx @@ -96,9 +96,10 @@ describe("app pricing page", () => { expect(html).toContain( "http://localhost:9210/api/billing/checkout?plan=team&cmux_external_browser=1&cmux_scheme=cmux-dev-test", ); - expect(html).toContain("/mo."); - expect(html).toContain("/user/mo."); - expect(html).toContain("$35/user/month"); + expect(html).toContain("/mo"); + expect(html).toContain("/user/mo"); + expect(html).not.toContain("/mo."); + expect(html).toContain("$35/user/mo"); expect(html).toContain('Includes:
'); expect(html).not.toContain('style="min-height:4rem"'); expect(html).toContain("text-3xl font-medium tabular-nums tracking-tight"); @@ -125,11 +126,14 @@ describe("app pricing page", () => { expect(html).toContain("$24"); expect(html).toContain("$28"); - expect(html).toContain("/mo."); - expect(html).toContain("/user/mo."); + expect(html).toContain("/mo"); + expect(html).toContain("/user/mo"); + expect(html).not.toContain("/mo."); expect(html).not.toContain("Billed $288 annually · save 20%"); expect(html).not.toContain("Billed $336 annually · save 20%"); - expect(html).toContain("$28/user/month · $336/user/year"); + expect(html).toContain("$28/user/mo"); + expect(html).not.toContain("$288/year"); + expect(html).not.toContain("$336/user/year"); expect(html).toContain( "http://localhost:9210/api/billing/checkout?plan=pro&cmux_external_browser=1&cmux_scheme=cmux-dev-test&interval=year", ); diff --git a/web/tests/dashboard-billing-page.test.tsx b/web/tests/dashboard-billing-page.test.tsx index bad3cea0038b..86245b4c2e8d 100644 --- a/web/tests/dashboard-billing-page.test.tsx +++ b/web/tests/dashboard-billing-page.test.tsx @@ -107,8 +107,9 @@ describe("dashboard billing page", () => { ); expect(html).toContain("Get Pro"); expect(html).toContain("Get Teams"); - expect(html).toContain("/mo."); - expect(html).toContain("/user/mo."); + expect(html).toContain("/mo"); + expect(html).toContain("/user/mo"); + expect(html).not.toContain("/mo."); expect(html).not.toContain('style="min-height:4rem"'); expect(html).toContain("text-3xl font-medium tabular-nums tracking-tight"); expect(html).toContain('href="/dashboard/testflight"'); @@ -122,8 +123,9 @@ describe("dashboard billing page", () => { expect(html).toContain("$24"); expect(html).toContain("$28"); - expect(html).toContain("/mo."); - expect(html).toContain("/user/mo."); + expect(html).toContain("/mo"); + expect(html).toContain("/user/mo"); + expect(html).not.toContain("/mo."); expect(html).not.toContain("Billed $288 annually · save 20%"); expect(html).not.toContain("Billed $336 annually · save 20%"); expect(html).toContain( @@ -142,7 +144,7 @@ describe("dashboard billing page", () => { expect(html).toContain("cmux Pro"); expect(html).toContain("Your plan renews on"); - expect(html).toContain("$30/month"); + expect(html).toContain("$30/mo"); expect(html).toContain("Cancel plan"); expect(html).toContain('action="/api/billing/subscription"'); expect(html).toContain('href="/api/billing/portal"'); @@ -156,7 +158,7 @@ describe("dashboard billing page", () => { }), ]; customerRows = [{ id: "cus_123" }]; - expect(await renderBillingPage()).toContain("$288/year"); + expect(await renderBillingPage()).toContain("$24/mo, billed annually"); subscriptionRows = [ stripeSubscriptionRow({ @@ -164,7 +166,7 @@ describe("dashboard billing page", () => { lookupKey: "cmux-pro-yearly", }), ]; - expect(await renderBillingPage()).toContain("$240/year"); + expect(await renderBillingPage()).toContain("$20/mo, billed annually"); }); test("renders pending cancellation with resume and end-date copy", async () => { @@ -201,7 +203,7 @@ describe("dashboard billing page", () => { expect(html).toContain("Team Pro renews on"); expect(html).toContain("Seats"); expect(html).toContain(">4<"); - expect(html).toContain("$35/seat/month"); + expect(html).toContain("$35/seat/mo"); expect(html).toContain('name="scope" value="team"'); expect(html).toContain('href="/api/billing/portal?scope=team"'); }); @@ -223,7 +225,7 @@ describe("dashboard billing page", () => { ]; customerRows = [{ id: "cus_team" }]; - expect(await renderBillingPage()).toContain("$336/seat/year"); + expect(await renderBillingPage()).toContain("$28/seat/mo, billed annually"); }); test("uses the current Stripe price interval over stale checkout metadata", async () => { @@ -244,7 +246,7 @@ describe("dashboard billing page", () => { ]; customerRows = [{ id: "cus_team" }]; - expect(await renderBillingPage()).toContain("$35/seat/month"); + expect(await renderBillingPage()).toContain("$35/seat/mo"); subscriptionResults = [ [], @@ -260,12 +262,14 @@ describe("dashboard billing page", () => { }), ], ]; - expect(await renderBillingPage()).toContain("$336/seat/year"); + expect(await renderBillingPage()).toContain("$28/seat/mo, billed annually"); }); test("localizes active annual Pro prices", () => { - expect(enMessages.dashboard.billing.pro.annualPrice).toBe("$288/year"); - expect(jaMessages.dashboard.billing.pro.annualPrice).toBe("$288/年"); + expect(enMessages.dashboard.billing.pro.annualPrice).toBe( + "$24/mo, billed annually", + ); + expect(jaMessages.dashboard.billing.pro.annualPrice).toBe("$24/月(年払い)"); }); test("renders active Stripe Team for a paid team when no team is selected", async () => { diff --git a/web/tests/pricing-page.test.tsx b/web/tests/pricing-page.test.tsx index 0c92de2cfb21..3a067fce9544 100644 --- a/web/tests/pricing-page.test.tsx +++ b/web/tests/pricing-page.test.tsx @@ -79,9 +79,10 @@ describe("localized pricing page", () => { expect(html).not.toContain("/api/billing/portal"); expect(html).not.toContain("Manage billing"); - expect(html).toContain("/mo."); - expect(html).toContain("/user/mo."); - expect(html).toContain("$35/user/month"); + expect(html).toContain("/mo"); + expect(html).toContain("/user/mo"); + expect(html).not.toContain("/mo."); + expect(html).toContain("$35/user/mo"); expect(html).toContain( "/api/billing/checkout?plan=team&cmux_external_browser=1&interval=month", ); @@ -122,11 +123,13 @@ describe("localized pricing page", () => { const html = renderToStaticMarkup(element); expect(html).toContain("$24"); - expect(html).toContain("/mo."); - expect(html).toContain("$24/month · $288/year"); + expect(html).toContain("/mo"); + expect(html).toContain("$24/mo"); expect(html).toContain("$28"); - expect(html).toContain("/user/mo."); - expect(html).toContain("$28/user/month · $336/user/year"); + expect(html).toContain("/user/mo"); + expect(html).toContain("$28/user/mo"); + expect(html).not.toContain("$288/year"); + expect(html).not.toContain("$336/user/year"); expect(html).not.toContain("Billed $288 annually · save 20%"); expect(html).not.toContain("Billed $336 annually · save 20%"); expect(html).toContain( From 5a4b88646ac034fdf52dfbf257e0f915e569a2a3 Mon Sep 17 00:00:00 2001 From: lawrencecchen <54008264+lawrencecchen@users.noreply.github.com> Date: Sat, 1 Aug 2026 03:02:01 -0700 Subject: [PATCH 5/6] Add annual billing cadence regression coverage --- web/tests/app-pricing-page.test.tsx | 2 ++ web/tests/dashboard-billing-page.test.tsx | 2 ++ web/tests/pricing-page.test.tsx | 2 ++ 3 files changed, 6 insertions(+) diff --git a/web/tests/app-pricing-page.test.tsx b/web/tests/app-pricing-page.test.tsx index fee88e3c47d6..ddcd5a518a6b 100644 --- a/web/tests/app-pricing-page.test.tsx +++ b/web/tests/app-pricing-page.test.tsx @@ -128,6 +128,8 @@ describe("app pricing page", () => { expect(html).toContain("$28"); expect(html).toContain("/mo"); expect(html).toContain("/user/mo"); + expect(html).toContain("/mo, billed yearly"); + expect(html).toContain("/user/mo, billed yearly"); expect(html).not.toContain("/mo."); expect(html).not.toContain("Billed $288 annually · save 20%"); expect(html).not.toContain("Billed $336 annually · save 20%"); diff --git a/web/tests/dashboard-billing-page.test.tsx b/web/tests/dashboard-billing-page.test.tsx index 86245b4c2e8d..1236b090977f 100644 --- a/web/tests/dashboard-billing-page.test.tsx +++ b/web/tests/dashboard-billing-page.test.tsx @@ -125,6 +125,8 @@ describe("dashboard billing page", () => { expect(html).toContain("$28"); expect(html).toContain("/mo"); expect(html).toContain("/user/mo"); + expect(html).toContain("/mo, billed yearly"); + expect(html).toContain("/user/mo, billed yearly"); expect(html).not.toContain("/mo."); expect(html).not.toContain("Billed $288 annually · save 20%"); expect(html).not.toContain("Billed $336 annually · save 20%"); diff --git a/web/tests/pricing-page.test.tsx b/web/tests/pricing-page.test.tsx index 3a067fce9544..f53763dca17d 100644 --- a/web/tests/pricing-page.test.tsx +++ b/web/tests/pricing-page.test.tsx @@ -127,6 +127,8 @@ describe("localized pricing page", () => { expect(html).toContain("$24/mo"); expect(html).toContain("$28"); expect(html).toContain("/user/mo"); + expect(html).toContain("/mo, billed yearly"); + expect(html).toContain("/user/mo, billed yearly"); expect(html).toContain("$28/user/mo"); expect(html).not.toContain("$288/year"); expect(html).not.toContain("$336/user/year"); From bb8d4b3b15253b53e9a33ead6d94914ff0b71c31 Mon Sep 17 00:00:00 2001 From: lawrencecchen <54008264+lawrencecchen@users.noreply.github.com> Date: Sat, 1 Aug 2026 03:04:22 -0700 Subject: [PATCH 6/6] Disclose yearly billing in compact labels --- web/messages/en.json | 4 ++-- web/messages/ja.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/web/messages/en.json b/web/messages/en.json index b960fd7a2ba5..9dd69e2f9427 100644 --- a/web/messages/en.json +++ b/web/messages/en.json @@ -404,9 +404,9 @@ "metaDescriptionNoVaultShort": "cmux is a free, open source macOS terminal. Pro adds Cloud VMs and a model gateway; Enterprise adds SSO and self-hosting.", "title": "Pricing", "perMonth": "/mo", - "perMonthBilledYearly": "/mo", + "perMonthBilledYearly": "/mo, billed yearly", "perUserMonth": "/user/mo", - "perUserMonthBilledYearly": "/user/mo", + "perUserMonthBilledYearly": "/user/mo, billed yearly", "billingPeriod": "Billing period", "monthly": "Monthly", "annual": "Annual", diff --git a/web/messages/ja.json b/web/messages/ja.json index 905396b9a84b..a8574dda7212 100644 --- a/web/messages/ja.json +++ b/web/messages/ja.json @@ -404,9 +404,9 @@ "metaDescriptionNoVaultShort": "cmux は無料・オープンソースの macOS ターミナルです。Pro では Cloud VM とモデルゲートウェイを利用でき、Enterprise では SSO とセルフホストに対応します。", "title": "料金", "perMonth": "/月", - "perMonthBilledYearly": "/月", + "perMonthBilledYearly": "/月(年払い)", "perUserMonth": "/ユーザー/月", - "perUserMonthBilledYearly": "/ユーザー/月", + "perUserMonthBilledYearly": "/ユーザー/月(年払い)", "billingPeriod": "請求期間", "monthly": "月払い", "annual": "年払い",