From 7ba5e063ca2ddde1a53bf1da54527d5c265834df Mon Sep 17 00:00:00 2001 From: Usman Baig Date: Fri, 13 Mar 2026 21:28:04 +0100 Subject: [PATCH] feat: add free plan to pricing page and enforce 1-site limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Show the free tier (€0, 1 site, 5k pageviews, 6 months retention) as the first card on the pricing page. Enforce a 1-site limit for free plan users in the frontend. --- CHANGELOG.md | 5 +++++ components/PricingSection.tsx | 37 ++++++++++++++++++++++++++++++++++- lib/plans.ts | 4 ++-- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9df91f..936b2a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## [Unreleased] +### Added + +- **Free plan now visible on the Pricing page.** The free tier is no longer hidden — it's displayed as the first option on the Pricing page so you can see exactly what you get before signing up: 1 site, 5,000 monthly pageviews, and 6 months of data retention, completely free. +- **Free plan limited to 1 site.** Free accounts are now limited to a single site. If you need more, you can upgrade to Solo or above from the Pricing page. + ### Improved - **Sites now show their verification status.** Each site on your dashboard now displays either a green "Active" badge (if verified) or an amber "Unverified" badge. When you verify your tracking script installation, the status is saved permanently — no more showing "Active" for sites that haven't been set up yet. diff --git a/components/PricingSection.tsx b/components/PricingSection.tsx index 048f942..8205816 100644 --- a/components/PricingSection.tsx +++ b/components/PricingSection.tsx @@ -292,7 +292,42 @@ export default function PricingSection() { {/* Pricing Grid */} -
+
+ {/* Free Plan */} +
+
+

Free

+

For trying Pulse on a personal project

+
+ €0 + /forever +
+
+ + + +
    + {['1 site', '5k monthly pageviews', '6 months data retention', '100% Data ownership'].map((feature) => ( +
  • + + {feature} +
  • + ))} +
+
+ {PLANS.map((plan) => { const priceDetails = getPriceDetails(plan.id) const isTeam = plan.id === 'team' diff --git a/lib/plans.ts b/lib/plans.ts index 5456837..a2d2ce9 100644 --- a/lib/plans.ts +++ b/lib/plans.ts @@ -7,9 +7,9 @@ export const PLAN_ID_SOLO = 'solo' export const PLAN_ID_TEAM = 'team' export const PLAN_ID_BUSINESS = 'business' -/** Sites limit per plan. Returns null for free (no limit enforced in UI). */ +/** Sites limit per plan. */ export function getSitesLimitForPlan(planId: string | null | undefined): number | null { - if (!planId || planId === 'free') return null + if (!planId || planId === 'free') return 1 switch (planId) { case 'solo': return 1 case 'team': return 5