From d8e1063c3282000949163d7716ebef2386286f13 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Wed, 13 May 2026 15:33:04 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(pricing):=20v3=20model=20=E2=80=94=20o?= =?UTF-8?q?rders=20&=20drivers=20free,=20interactive=20calculator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Companion to fleetbase/billing#32. Updates the public pricing page to reflect the v3 resource-unit model and ships an interactive calculator that lets visitors plug in their own numbers. Narrative pivot (hero & explainer): - "One price for your whole fleet. Not per driver." - "Most TMS platforms charge $29–$99 per driver per month. Fleetbase charges $0." - Reframes Resource Units as a monthly "activity allowance" measured against the *structure* of the business (vehicles, contacts, places) rather than its activity (orders) or workforce (drivers). - Adds a comparison FAQ entry against Onfleet, Detrack, Routific. - Updates trial cap copy from 50 → 100 units. Plan ladder (CLOUD_TIERS in pricing-data.ts): - Same prices, same overage rates, same 13 tiers. - Included units expanded ~1.5–2× per tier to match config/plans.php in the billing module. - Adds a "fits" hint per featured plan (e.g. "5 drivers, ~200 orders/mo") derived from the v3 unit math. Resource units explainer: - Orders and Drivers now render with a FREE badge instead of a unit count. - Adds a "Free — never billable" legend alongside the existing "resets" and "rolling" legend entries. Pricing calculator (new — pricing-calculator.tsx): - Two-panel layout: inputs on the left, live output on the right. - Primary inputs always visible (drivers, orders/mo, vehicles, users). Drivers and orders are tagged FREE — moving the slider does not affect the bill (proves the marketing claim live). - Advanced section expands to expose contacts, places, vendors, service rates, service areas, zones, webhooks, API keys. - Output panels: 1. Live usage breakdown (Vehicle ×5 → 5 units, etc.). 2. Recommended plan with headroom calculation. 3. Side-by-side comparison vs a per-driver TMS at the verified $35/driver/mo benchmark (midpoint of Detrack, OptimoRoute, Track-POD; range $29–$99). Shows $/mo and $/yr savings. - Inherits the page's monthly/annual billing toggle. - Debounced emits the existing pricing_calculator_changed PostHog event (already in lib/analytics/events.ts). - All sliders use the native range input styled with Tailwind + accent-primary — no new dependency. Data extraction (pricing-data.ts): - Lifts CLOUD_TIERS, FEATURED_TIERS, RESOURCE_UNITS, OVERAGE_PACKS out of pricing-client.tsx into a shared data module so the calculator and the page render from a single source of truth. - Exports recommendPlan() and computeUnits() helpers. - COMPETITOR_PER_DRIVER_USD constant ($35) is the documented anchor for the comparison panel — single point of truth for adjustments. Rollout coupling: - This PR must NOT merge to main / ship to production until fleetbase/billing#32 has been merged, the billing seeder & sync command have run on prod, and the subscription migration command has migrated all active accounts to v3 Stripe Prices. - The marketing site will otherwise advertise prices we are not yet billing. Verified locally via the dev server: - Default inputs (5 drivers, 5 vehicles, 200 orders, 2 users + a small structural footprint) → 220 units → Lite $50/mo + 80 unit headroom. Competitor compare: $175 → $50, save $125/mo. - Setting drivers to 100 and orders to 5,000: total unchanged at 220 units; bill unchanged at $50; competitor compare shows $3,500 vs $50 ($41,400/yr savings) — proves drivers/orders are free. - Mid-size scenario (15 drivers, 15 vehicles, 1,000 orders + advanced inputs): 848 units → Starter Plus $300/mo with 152 unit headroom. - Mobile 375px: no horizontal overflow; grid stacks single-column. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/app/pricing/pricing-calculator.tsx | 343 +++++++++++++++++++++++ src/app/pricing/pricing-client.tsx | 363 +++++++++---------------- src/app/pricing/pricing-data.ts | 286 +++++++++++++++++++ 3 files changed, 764 insertions(+), 228 deletions(-) create mode 100644 src/app/pricing/pricing-calculator.tsx create mode 100644 src/app/pricing/pricing-data.ts diff --git a/src/app/pricing/pricing-calculator.tsx b/src/app/pricing/pricing-calculator.tsx new file mode 100644 index 00000000..d5c40bff --- /dev/null +++ b/src/app/pricing/pricing-calculator.tsx @@ -0,0 +1,343 @@ +'use client'; + +import { ArrowRight, Calculator, ChevronDown, ChevronUp, Sparkles } from 'lucide-react'; +import Link from 'next/link'; +import { useEffect, useMemo, useRef, useState } from 'react'; + +import { Button } from '@/components/ui/button'; +import { Card, CardContent } from '@/components/ui/card'; +import { track } from '@/lib/analytics/posthog'; +import { cn } from '@/lib/utils'; + +import { + CalculatorInputs, + COMPETITOR_PER_DRIVER_USD, + computeUnits, + recommendPlan, +} from './pricing-data'; + +const DEFAULT_INPUTS: CalculatorInputs = { + drivers: 5, + vehicles: 5, + orders: 200, + users: 2, + contacts: 100, + places: 100, + vendors: 0, + serviceRates: 3, + serviceAreas: 1, + zones: 0, + webhooks: 0, + apiKeys: 1, +}; + +type Field = { + key: keyof CalculatorInputs; + label: string; + min: number; + max: number; + step: number; + helper?: string; + free?: boolean; +}; + +const PRIMARY_FIELDS: Field[] = [ + { key: 'drivers', label: 'Drivers', min: 0, max: 200, step: 1, free: true, helper: 'Free — never billed.' }, + { key: 'orders', label: 'Orders per month', min: 0, max: 10000, step: 25, free: true, helper: 'Free — never billed.' }, + { key: 'vehicles', label: 'Vehicles', min: 0, max: 200, step: 1 }, + { key: 'users', label: 'Dispatchers / Users', min: 1, max: 100, step: 1, helper: '5 units each' }, +]; + +const ADVANCED_FIELDS: Field[] = [ + { key: 'contacts', label: 'Customer contacts (new this month)', min: 0, max: 5000, step: 10 }, + { key: 'places', label: 'Delivery addresses (new this month)', min: 0, max: 5000, step: 10 }, + { key: 'vendors', label: 'Vendors', min: 0, max: 200, step: 1 }, + { key: 'serviceRates', label: 'Service rate configurations', min: 0, max: 50, step: 1 }, + { key: 'serviceAreas', label: 'Service areas', min: 0, max: 50, step: 1 }, + { key: 'zones', label: 'Zones', min: 0, max: 100, step: 1 }, + { key: 'webhooks', label: 'Webhook endpoints', min: 0, max: 50, step: 1, helper: '5 units each' }, + { key: 'apiKeys', label: 'API keys', min: 0, max: 50, step: 1 }, +]; + +type Props = { + billing: 'monthly' | 'annual'; +}; + +export default function PricingCalculator({ billing }: Props) { + const [inputs, setInputs] = useState(DEFAULT_INPUTS); + const [advancedOpen, setAdvancedOpen] = useState(false); + const lastTrackedUnits = useRef(null); + + const { total, breakdown } = useMemo(() => computeUnits(inputs), [inputs]); + const recommendation = useMemo(() => recommendPlan(total, billing), [total, billing]); + + // Competitor comparison anchored on driver count. + const competitorMonthly = inputs.drivers * COMPETITOR_PER_DRIVER_USD; + const fleetbaseMonthly = + billing === 'annual' ? recommendation.totalCost * 12 / 12 : recommendation.totalCost; + const monthlySavings = Math.max(0, competitorMonthly - fleetbaseMonthly); + const yearlySavings = monthlySavings * 12; + + // Debounced analytics emission on settle. + useEffect(() => { + const t = setTimeout(() => { + if (lastTrackedUnits.current === total) return; + lastTrackedUnits.current = total; + track('pricing_calculator_changed', { + units: total, + derived_price: recommendation.totalCost, + }); + }, 500); + return () => clearTimeout(t); + }, [total, recommendation.totalCost]); + + const setField = (key: keyof CalculatorInputs, value: number) => { + setInputs((prev) => ({ ...prev, [key]: value })); + }; + + return ( +
+
+
+
+ + Pricing Calculator +
+

+ Tell us about your operation. We'll find your plan. +

+

+ Drivers and orders are always free. + Adjust the numbers below and see what your real bill looks like — and what you'd + pay on a typical per-driver competitor. +

+
+ +
+ {/* Inputs */} + + + {PRIMARY_FIELDS.map((field) => ( + setField(field.key, v)} + /> + ))} + + + + {advancedOpen && ( +
+ {ADVANCED_FIELDS.map((field) => ( + setField(field.key, v)} + /> + ))} +
+ )} +
+
+ + {/* Output */} +
+ {/* Usage */} + + +
+
Your monthly usage
+
{total.toLocaleString()} units
+
+ {breakdown.length > 0 ? ( +
+ {breakdown.map((b) => ( +
+ + {b.label} ×{b.quantity} + + {b.units.toLocaleString()} units +
+ ))} +
+ Orders ×{inputs.orders.toLocaleString()} + FREE +
+
+ Drivers ×{inputs.drivers} + FREE +
+
+ ) : ( +
No billable usage yet — start with at least 1 user or vehicle.
+ )} +
+
+ + {/* Recommended plan */} + +
+ Your recommended plan +
+ +
+
{recommendation.plan.name}
+
+ ${recommendation.totalCost.toLocaleString(undefined, { maximumFractionDigits: 0 })} + /mo +
+
+ {billing === 'annual' && recommendation.fits && ( +
+ Billed annually (${(recommendation.totalCost * 12).toLocaleString()}/yr) +
+ )} +
+ {recommendation.fits ? ( + <> + Includes {recommendation.plan.units.toLocaleString()} units —{' '} + + {(recommendation.plan.units - total).toLocaleString()} units headroom + . + + ) : ( + <> + Largest plan ({recommendation.plan.units.toLocaleString()} units) plus{' '} + {recommendation.overageUnits.toLocaleString()} overage units at ${recommendation.plan.overage}/unit.{' '} + Talk to us about a custom Enterprise plan → + + )} +
+ +
+
+ + {/* Competitor comparison */} + {inputs.drivers > 0 && ( + + +
+ +
Compared to a typical per-driver TMS
+
+
+
+
Per-driver TMS
+
+ ${competitorMonthly.toLocaleString()}/mo +
+
+ {inputs.drivers} drivers × ${COMPETITOR_PER_DRIVER_USD}/driver +
+
+
+
Fleetbase
+
+ ${fleetbaseMonthly.toLocaleString(undefined, { maximumFractionDigits: 0 })}/mo +
+
{recommendation.plan.name} plan, all-in
+
+
+ {monthlySavings > 0 && ( +
+ + You save ${monthlySavings.toLocaleString(undefined, { maximumFractionDigits: 0 })}/month + {' '} + + (${yearlySavings.toLocaleString(undefined, { maximumFractionDigits: 0 })}/year) + +
+ )} +

+ Per-driver benchmark of ${COMPETITOR_PER_DRIVER_USD}/driver/mo is the midpoint of published + rates from Detrack, OptimoRoute, and Track-POD ($29–$99). Comparison is for drivers + only — competitors typically charge separately for orders, integrations, and users. +

+
+
+ )} +
+
+
+
+ ); +} + +function CalculatorRow({ + field, + value, + onChange, +}: { + field: Field; + value: number; + onChange: (n: number) => void; +}) { + return ( +
+
+ + { + const n = Number(e.target.value); + if (Number.isFinite(n)) onChange(Math.max(field.min, Math.min(field.max, Math.round(n)))); + }} + className="w-20 text-right text-sm border rounded px-2 py-1 bg-background" + /> +
+ onChange(Number(e.target.value))} + className={cn( + 'w-full accent-primary cursor-pointer', + field.free && 'opacity-90' + )} + /> + {field.helper && ( +
{field.helper}
+ )} +
+ ); +} diff --git a/src/app/pricing/pricing-client.tsx b/src/app/pricing/pricing-client.tsx index 8278ba37..41e4510a 100644 --- a/src/app/pricing/pricing-client.tsx +++ b/src/app/pricing/pricing-client.tsx @@ -1,192 +1,35 @@ 'use client'; -import { -ArrowRight, Building2, - Check, ChevronDown, ChevronUp, Globe, Key, Layers, MapPin, Package, - Receipt, Server, Truck, User, UserRound, -Users, Webhook, X, Zap, } from 'lucide-react'; +import { ArrowRight, Check, ChevronDown, ChevronUp, Server, X, Zap } from 'lucide-react'; import Link from 'next/link'; import { useEffect, useState } from 'react'; -import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion'; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/components/ui/accordion'; import { Button } from '@/components/ui/button'; -import { Card, CardContent, CardDescription, CardFooter,CardHeader, CardTitle } from '@/components/ui/card'; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from '@/components/ui/card'; import { track } from '@/lib/analytics/posthog'; import { cn } from '@/lib/utils'; -// ─── Cloud Pricing Tiers ────────────────────────────────────────────────────── -const CLOUD_TIERS = [ - { - name: 'Micro', - monthlyPrice: 25, - annualPrice: 20, - units: 100, - overage: 0.75, - description: 'Individuals and very small teams.', - featured: true, - highlight: false, - badge: null, - }, - { - name: 'Lite', - monthlyPrice: 50, - annualPrice: 40, - units: 180, - overage: 0.75, - description: 'Small teams getting started.', - featured: true, - highlight: false, - badge: null, - }, - { - name: 'Essential', - monthlyPrice: 100, - annualPrice: 80, - units: 240, - overage: 0.75, - description: 'Growing operations with more to manage.', - featured: true, - highlight: false, - badge: null, - }, - { - name: 'Starter', - monthlyPrice: 200, - annualPrice: 160, - units: 300, - overage: 0.75, - description: 'Established small teams scaling up.', - featured: true, - highlight: true, - badge: 'Most Popular', - }, - { - name: 'Starter Plus', - monthlyPrice: 300, - annualPrice: 240, - units: 500, - overage: 0.65, - description: 'Enhanced capacity at a lower unit cost.', - featured: false, - highlight: false, - badge: null, - }, - { - name: 'Scale', - monthlyPrice: 400, - annualPrice: 320, - units: 800, - overage: 0.55, - description: 'High-volume operational teams.', - featured: false, - highlight: false, - badge: null, - }, - { - name: 'Scale Plus', - monthlyPrice: 500, - annualPrice: 400, - units: 1200, - overage: 0.45, - description: 'Serious scale with a lower unit cost.', - featured: false, - highlight: false, - badge: null, - }, - { - name: 'Pro', - monthlyPrice: 600, - annualPrice: 480, - units: 1700, - overage: 0.40, - description: 'Power teams running complex operations.', - featured: false, - highlight: false, - badge: null, - }, - { - name: 'Pro Plus', - monthlyPrice: 700, - annualPrice: 560, - units: 2300, - overage: 0.35, - description: 'High-throughput pro operations.', - featured: false, - highlight: false, - badge: null, - }, - { - name: 'Elite', - monthlyPrice: 800, - annualPrice: 640, - units: 3000, - overage: 0.30, - description: 'Enterprise-grade volume at high velocity.', - featured: false, - highlight: false, - badge: null, - }, - { - name: 'Elite Plus', - monthlyPrice: 900, - annualPrice: 720, - units: 3800, - overage: 0.25, - description: 'Maximum capacity before Enterprise.', - featured: false, - highlight: false, - badge: null, - }, - { - name: 'Enterprise', - monthlyPrice: 1000, - annualPrice: 800, - units: 5000, - overage: 0.20, - description: 'Full enterprise scale with the lowest unit cost.', - featured: false, - highlight: false, - badge: null, - }, - { - name: 'Enterprise Plus', - monthlyPrice: 1500, - annualPrice: 1200, - units: 7500, - overage: 0.15, - description: 'Largest cloud plan. Maximum capacity.', - featured: false, - highlight: false, - badge: null, - }, -]; - -const FEATURED_TIERS = CLOUD_TIERS.filter((t) => t.featured); - -// ─── Resource Unit Costs ────────────────────────────────────────────────────── -// rolling: true = count persists across billing cycles (does not reset) -const RESOURCE_UNITS = [ - { icon: Package, label: 'Order', units: 2, rolling: false }, - { icon: UserRound, label: 'Contact', units: 1, rolling: false }, - { icon: MapPin, label: 'Place', units: 1, rolling: false }, - { icon: Building2, label: 'Vendor', units: 1, rolling: false }, - { icon: Truck, label: 'Vehicle', units: 1, rolling: true }, - { icon: User, label: 'Driver', units: 1, rolling: true }, - { icon: Receipt, label: 'Service Rate', units: 1, rolling: false }, - { icon: Globe, label: 'Service Area', units: 1, rolling: false }, - { icon: Layers, label: 'Zone', units: 1, rolling: false }, - { icon: Users, label: 'User', units: 5, rolling: true }, - { icon: Webhook, label: 'Webhook', units: 5, rolling: true }, - { icon: Key, label: 'API Key', units: 1, rolling: true }, -]; - -// ─── Overage Packs ──────────────────────────────────────────────────────────── -const OVERAGE_PACKS = [ - { name: 'Small', price: 90, units: 100 }, - { name: 'Medium', price: 240, units: 300 }, - { name: 'Large', price: 375, units: 500 }, - { name: 'Jumbo', price: 700, units: 1000 }, -]; +import PricingCalculator from './pricing-calculator'; +import { + CLOUD_TIERS, + FEATURED_TIERS, + OVERAGE_PACKS, + RESOURCE_UNITS, + type CloudTier, +} from './pricing-data'; // ─── Support Tiers ──────────────────────────────────────────────────────────── const SUPPORT_TIERS = [ @@ -346,11 +189,23 @@ const LICENSE_OPTIONS = [ }, ]; -// ─── FAQs ───────────────────────────────────────────────────────────────────── +// ─── FAQs (v3 pricing) ──────────────────────────────────────────────────────── const FAQS = [ + { + q: 'How does Fleetbase compare to Onfleet, Detrack, or Routific?', + a: 'Most per-driver TMS platforms charge $29–$99 per driver per month — so a 10-driver fleet typically pays $290–$990/month just for driver seats, before any orders, integrations, or user logins. Fleetbase doesn’t charge per driver. A 10-driver fleet processing 400 orders/month fits on the Essential plan at $100/month, with the full platform included (Fleet-Ops, Storefront, Pallet, Ledger, Marketplace). Use the calculator above to compare with your numbers.', + }, { q: 'What is a Resource Unit?', - a: 'Resource Units are the currency of your Fleetbase Cloud plan. Each resource type consumes a set number of units: Orders = 2 units; Users = 5 units; Webhooks = 5 units; Contacts, Places, Vendors, Vehicles, Drivers, Service Rates, Service Areas, Zones, and API Keys = 1 unit each. Most resources reset each billing cycle. Rolling resources — Users, Vehicles, Drivers, Webhooks, and API Keys — do not reset; their count persists into the next billing cycle. You only pay overage for usage beyond your monthly allocation.', + a: 'Resource Units are how we measure the structural size of your operation — things like vehicles, customer contacts, delivery addresses, service rates, zones, and users. Each plan includes a monthly allocation. Drivers and orders are tracked for visibility but are NEVER billable — so growing your team or running more deliveries does not increase your bill. Most resources reset each billing cycle. Rolling resources (Users, Vehicles, Webhooks, API Keys) carry their count into the next cycle.', + }, + { + q: 'Are orders and drivers really free?', + a: 'Yes — neither contributes any units toward your monthly allocation, regardless of how many you have or process. You can add as many drivers as you need and run as many orders as you want without your bill changing. We make money on the resource units that represent the structural size of your business (vehicles, contacts, places, users) and on bundled platform value, not on activity or workforce headcount.', + }, + { + q: 'How much does each resource cost in units?', + a: 'Vehicles, contacts, places, vendors, service rates, service areas, zones, and API keys are 1 unit each. Users are 5 units each (rolling — they persist across cycles). Webhook endpoints are 5 units each (rolling). Orders and drivers are 0 — they are tracked but never billable.', }, { q: 'Can I switch plans at any time?', @@ -366,7 +221,7 @@ const FAQS = [ }, { q: 'Is there a free trial?', - a: 'Yes — every Cloud plan includes a 7-day free trial capped at 50 resource units. Billing begins when either limit is reached first, so you can evaluate the platform against real operational usage.', + a: 'Yes — every Cloud plan includes a 7-day free trial capped at 100 resource units. Billing begins when either limit is reached first, so you can evaluate the platform against real operational usage.', }, { q: 'What does the Self-Hosted implementation fee include?', @@ -376,17 +231,13 @@ const FAQS = [ q: 'Can I add more Resource Units mid-month?', a: 'Yes. You can purchase Resource Unit Packs at any time: Small (100 units / $90), Medium (300 units / $240), Large (500 units / $375), or Jumbo (1,000 units / $700). These top up your allocation immediately.', }, - { - q: 'What is Professional Services?', - a: 'Professional Services covers custom development work — building bespoke extensions, integrating with your existing ERP/CRM, custom workflow automation, data migration, and training. Pricing is scoped per project. Contact our sales team for a quote.', - }, ]; export default function PricingClient() { const [billing, setBilling] = useState<'monthly' | 'annual'>('monthly'); const [showAllPlans, setShowAllPlans] = useState(false); - const tierPrice = (tier: (typeof CLOUD_TIERS)[0]) => + const tierPrice = (tier: CloudTier) => billing === 'annual' ? tier.annualPrice : tier.monthlyPrice; useEffect(() => { @@ -401,7 +252,7 @@ export default function PricingClient() { track('pricing_billing_toggled', { to_cycle: cycle }); }; - const onTierCtaClick = (tier: (typeof CLOUD_TIERS)[0]) => { + const onTierCtaClick = (tier: CloudTier) => { track('pricing_tier_cta_clicked', { tier: tier.name, billing_cycle: billing, @@ -416,14 +267,19 @@ export default function PricingClient() {
- Transparent, Usage-Based Pricing + Drivers and orders are free — always

- Pay for what you use.{' '} - Nothing more. + One price for your whole fleet.{' '} + Not per driver.

-

- Fleetbase Cloud starts at $25/month. No per-seat charges. No hidden fees. Self-hosted implementation is a one-time $2,500 fee. +

+ Most TMS platforms charge $29–$99 per driver per month. Fleetbase charges $0. + Add as many drivers and process as many orders as you need — your bill won't + change. You pay for the structure of your business, not its activity or workforce. +

+

+ Cloud starts at $25/month. Self-hosted from $2,500 one-time.

{/* Billing Toggle */} @@ -472,19 +328,17 @@ export default function PricingClient() {

- 7 days or 50 resource units — whichever comes first. + 7 days or 100 resource units — whichever comes first.

@@ -494,8 +348,9 @@ export default function PricingClient() {

Fleetbase Cloud

-

- Fully managed. Automatic updates. Unlimited users and drivers. All platform modules included on every plan. +

+ Fully managed. Automatic updates. Unlimited drivers and orders on every plan. + All platform modules included.

@@ -504,7 +359,10 @@ export default function PricingClient() { {FEATURED_TIERS.map((tier) => ( {tier.badge && (
@@ -531,7 +389,10 @@ export default function PricingClient() {
Overage: ${tier.overage}/unit
-
{tier.description}
+ {tier.fits && ( +
Fits: {tier.fits}
+ )} +
{tier.description}
@@ -640,29 +501,57 @@ export default function PricingClient() {
+ {/* Calculator */} + + {/* Resource Units Explainer */} -
+
-

How Resource Units Work

+

One plan covers everything you do

- Your plan includes a monthly unit allocation. Each resource type consumes a set number of units per item created. Most resources reset each billing cycle — rolling resources (marked below) carry their count into the next cycle. + Per-driver pricing punishes growth. Instead, each plan gives you a monthly + activity allowance measured in resource units. + Drivers and orders are free. Everything else + draws from your monthly budget — so you can grow your team and run more deliveries without + your bill changing.

{RESOURCE_UNITS.map((r) => ( -
- -
{r.units}
+
+ + {r.billable ? ( +
{r.units}
+ ) : ( +
FREE
+ )}
{r.label}
- {r.rolling && ( -
rolling
+ {r.rolling && r.billable && ( +
+ rolling +
)}
))}
-
+
+
+ + Free — never billable +
Resets each billing cycle @@ -685,7 +574,9 @@ export default function PricingClient() {
{pack.name}
${pack.price}
-
{pack.units.toLocaleString()} units
+
+ {pack.units.toLocaleString()} units +
))}
@@ -694,12 +585,13 @@ export default function PricingClient() {
{/* Self-Hosted + Professional Services */} -
+

Other Deployment Options

- Full control over your infrastructure, or custom-built solutions for your unique requirements. + Full control over your infrastructure, or custom-built solutions for your unique + requirements.

@@ -791,19 +683,23 @@ export default function PricingClient() {
{/* Support Tiers */} -
+

Support Levels

- Choose the level of ongoing support that matches your team's capacity and ambition. Available for both Cloud and Self-Hosted customers. + Choose the level of ongoing support that matches your team's capacity and ambition. + Available for both Cloud and Self-Hosted customers.

{SUPPORT_TIERS.map((tier) => (
@@ -841,19 +737,24 @@ export default function PricingClient() {
{/* Commercial License Options */} -
+

Commercial License Options

- Building proprietary extensions or integrations? A Commercial License waives AGPL obligations and keeps your custom code private. Fleetbase Core remains open-source — only your extensions are covered. + Building proprietary extensions or integrations? A Commercial License waives AGPL + obligations and keeps your custom code private. Fleetbase Core remains open-source — + only your extensions are covered.

{LICENSE_OPTIONS.map((lic) => ( {lic.highlight && (
@@ -899,7 +800,10 @@ export default function PricingClient() { Read our licensing guide {' '} or{' '} - + talk to our team . @@ -908,7 +812,7 @@ export default function PricingClient() {
{/* FAQ */} -
+

Frequently Asked Questions

@@ -923,7 +827,9 @@ export default function PricingClient() { {faq.q} - {faq.a} + + {faq.a} + ))} @@ -931,14 +837,15 @@ export default function PricingClient() {
{/* Bottom CTA */} -
+

Ready to get started?

- Try Fleetbase free for 7 days or 50 resource units, whichever comes first. Or speak to our team to find the right plan for your operation. + Try Fleetbase free for 7 days or 100 resource units, whichever comes first. Or speak + to our team to find the right plan for your operation.

{advancedOpen && ( -
+
{ADVANCED_FIELDS.map((field) => ( {/* Usage */} - - + +
Your monthly usage
-
{total.toLocaleString()} units
+
+ {total.toLocaleString()}{' '} + units +
{breakdown.length > 0 ? ( -
+
{breakdown.map((b) => (
@@ -168,13 +171,13 @@ export default function PricingCalculator({ billing }: Props) { {b.units.toLocaleString()} units
))} -
+
Orders ×{inputs.orders.toLocaleString()} - FREE + FREE
Drivers ×{inputs.drivers} - FREE + FREE
) : ( @@ -184,14 +187,14 @@ export default function PricingCalculator({ billing }: Props) { {/* Recommended plan */} - +
Your recommended plan
- +
{recommendation.plan.name}
-
+
${recommendation.totalCost.toLocaleString(undefined, { maximumFractionDigits: 0 })} /mo
@@ -237,45 +240,49 @@ export default function PricingCalculator({ billing }: Props) { {/* Competitor comparison */} - {inputs.drivers > 0 && ( - - + {inputs.drivers > 0 && monthlySavings > 0 && ( + +
-
Compared to a typical per-driver TMS
+
+ Compared to a typical per-driver TMS +
-
-
+
+
Per-driver TMS
-
- ${competitorMonthly.toLocaleString()}/mo +
+ ${competitorMonthly.toLocaleString()} + /mo
- {inputs.drivers} drivers × ${COMPETITOR_PER_DRIVER_USD}/driver + {inputs.drivers} × ${COMPETITOR_PER_DRIVER_USD}/driver
-
+
Fleetbase
-
- ${fleetbaseMonthly.toLocaleString(undefined, { maximumFractionDigits: 0 })}/mo +
+ ${fleetbaseMonthly.toLocaleString(undefined, { maximumFractionDigits: 0 })} + /mo +
+
+ {recommendation.plan.name}, all-in
-
{recommendation.plan.name} plan, all-in
- {monthlySavings > 0 && ( -
- - You save ${monthlySavings.toLocaleString(undefined, { maximumFractionDigits: 0 })}/month - {' '} - - (${yearlySavings.toLocaleString(undefined, { maximumFractionDigits: 0 })}/year) - +
+
+ Save ${monthlySavings.toLocaleString(undefined, { maximumFractionDigits: 0 })}/mo
- )} +
+ That's ${yearlySavings.toLocaleString(undefined, { maximumFractionDigits: 0 })} a year +
+

- Per-driver benchmark of ${COMPETITOR_PER_DRIVER_USD}/driver/mo is the midpoint of published - rates from Detrack, OptimoRoute, and Track-POD ($29–$99). Comparison is for drivers - only — competitors typically charge separately for orders, integrations, and users. + Benchmark of ${COMPETITOR_PER_DRIVER_USD}/driver/mo is the midpoint of published rates + from Detrack, OptimoRoute, and Track-POD ($29–$99). Competitors typically charge + separately for orders, integrations, and users.

@@ -296,16 +303,24 @@ function CalculatorRow({ value: number; onChange: (n: number) => void; }) { + // Skip the inline helper for "free" fields — the FREE badge says it all. + const showHelper = field.helper && !field.free; return (
-
-
); }