Consolidate pricing onto a single source. Pro is deferred from launch (left dormant: no Stripe price, no card), so the offered set is 3 tiers. - Extract the pricing grid and footer into shared components (PricingGrid.vue, landing/SiteFooter.vue); add a /pricing route rendering Pricing.vue; remove the pricing section from Home - Repoint every upgrade link to the /pricing route (LandingNav and SiteFooter via RouterLink, UpsellBanner CTA) — no more #pricing anchors - Bump Smart (plus) SMS daily limit 1 -> 3 (PlanSeeder + PlanFactory), update PlanFeaturesTest assertion - Rewrite /pricing card bullets to match real entitlements (drop unbuilt promises: multi-location tracking, 14-day trend, supermarket anchor) - Fix stale "1/day" SMS references in notifications.md, tiers.md, docs/tiers.md - Delete unused resources/views/components/pricing-card.blade.php Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
126 lines
6.7 KiB
Vue
126 lines
6.7 KiB
Vue
<template>
|
|
<!-- Pricing -->
|
|
<section id="pricing" class="py-4 md:py-24 px-6 bg-zinc-50">
|
|
<div class="max-w-7xl mx-auto">
|
|
<div class="text-center mb-16">
|
|
<h2 class="text-4xl md:text-5xl font-black font-display text-zinc-800 mb-4">Pricing for every driver</h2>
|
|
<p class="text-zinc-500 text-lg mb-8">Save hundreds for less than the cost of a coffee.</p>
|
|
<div class="inline-flex items-center gap-1 p-1 bg-white border border-zinc-300 rounded-full">
|
|
<button
|
|
:class="cadence === 'monthly' ? 'bg-accent text-white' : 'text-zinc-500'"
|
|
class="px-5 py-2 rounded-full text-sm font-bold transition-colors"
|
|
type="button"
|
|
@click="cadence = 'monthly'"
|
|
>
|
|
Monthly
|
|
</button>
|
|
<button
|
|
:class="cadence === 'annual' ? 'bg-accent text-white' : 'text-zinc-500'"
|
|
class="px-5 py-2 rounded-full text-sm font-bold transition-colors"
|
|
type="button"
|
|
@click="cadence = 'annual'"
|
|
>
|
|
Annual <span class="text-[10px] opacity-80">(save 17%)</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-6 max-w-5xl mx-auto">
|
|
<!-- Free -->
|
|
<div class="bg-white border border-zinc-300 p-8 rounded-3xl flex flex-col h-full">
|
|
<div class="mb-8">
|
|
<h4 class="text-xl font-bold font-display mb-2">Free</h4>
|
|
<div class="flex items-baseline gap-1">
|
|
<span class="text-4xl font-black">£0</span>
|
|
<span class="text-zinc-500 text-sm">/mo</span>
|
|
</div>
|
|
</div>
|
|
<ul class="space-y-4 mb-8 flex-1">
|
|
<li class="text-sm flex gap-2"><iconify-icon class="text-accent" icon="lucide:check"></iconify-icon> Local station price search</li>
|
|
<li class="text-sm flex gap-2"><iconify-icon class="text-accent" icon="lucide:check"></iconify-icon> Buy-or-wait verdict</li>
|
|
<li class="text-sm flex gap-2 text-zinc-500"><iconify-icon class="text-zinc-300" icon="lucide:x"></iconify-icon> No price alerts</li>
|
|
</ul>
|
|
<a :href="ctaHref('free')" class="w-full py-3 px-4 border border-zinc-300 rounded-xl text-center font-bold hover:bg-zinc-100 transition-colors">{{ ctaLabel('free') }}</a>
|
|
</div>
|
|
|
|
<!-- Daily (backend: basic) -->
|
|
<div class="bg-white border border-zinc-300 p-8 rounded-3xl flex flex-col h-full">
|
|
<div class="mb-8">
|
|
<h4 class="text-xl font-bold font-display mb-2">Daily</h4>
|
|
<div class="flex items-baseline gap-1">
|
|
<span class="text-4xl font-black">{{ PRICES[cadence].basic }}</span>
|
|
<span class="text-zinc-500 text-sm">{{ PRICE_SUFFIX[cadence] }}</span>
|
|
</div>
|
|
</div>
|
|
<ul class="space-y-4 mb-8 flex-1">
|
|
<li class="text-sm flex gap-2"><iconify-icon class="text-accent" icon="lucide:check"></iconify-icon> Buy-or-wait score + reason</li>
|
|
<li class="text-sm flex gap-2"><iconify-icon class="text-accent" icon="lucide:check"></iconify-icon> Daily email, push & WhatsApp</li>
|
|
<li class="text-sm flex gap-2"><iconify-icon class="text-accent" icon="lucide:check"></iconify-icon> Price-drop & score alerts</li>
|
|
</ul>
|
|
<a :href="ctaHref('basic')" class="w-full py-3 px-4 border border-zinc-300 rounded-xl text-center font-bold hover:bg-zinc-100 transition-colors">{{ ctaLabel('basic') }}</a>
|
|
</div>
|
|
|
|
<!-- Smart (backend: plus) -->
|
|
<div class="bg-white border-2 border-accent p-8 rounded-3xl flex flex-col h-full relative">
|
|
<div class="absolute -top-4 left-1/2 -translate-x-1/2 bg-accent text-white px-4 py-1 rounded-full text-[10px] font-black uppercase tracking-widest whitespace-nowrap">Most pick this</div>
|
|
<div class="mb-8">
|
|
<h4 class="text-xl font-bold font-display mb-2">Smart</h4>
|
|
<div class="flex items-baseline gap-1">
|
|
<span class="text-4xl font-black text-accent">{{ PRICES[cadence].plus }}</span>
|
|
<span class="text-zinc-500 text-sm">{{ PRICE_SUFFIX[cadence] }}</span>
|
|
</div>
|
|
</div>
|
|
<ul class="space-y-4 mb-8 flex-1">
|
|
<li class="text-sm flex gap-2 font-bold"><iconify-icon class="text-accent" icon="lucide:check"></iconify-icon> AI fuel-price prediction</li>
|
|
<li class="text-sm flex gap-2"><iconify-icon class="text-accent" icon="lucide:check"></iconify-icon> Real-time email, push & WhatsApp</li>
|
|
<li class="text-sm flex gap-2"><iconify-icon class="text-accent" icon="lucide:check"></iconify-icon> SMS alerts (up to 3/day)</li>
|
|
</ul>
|
|
<a :href="ctaHref('plus')" class="w-full py-3 px-4 bg-accent text-white rounded-xl text-center font-bold shadow-lg hover:bg-primary-dark transition-all">{{ ctaLabel('plus') }}</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref } from 'vue'
|
|
import { useAuth } from '../composables/useAuth.js'
|
|
|
|
const { isAuthenticated, userTier } = useAuth()
|
|
|
|
const cadence = ref('monthly')
|
|
|
|
const PRICES = {
|
|
monthly: { basic: '£0.99', plus: '£2.49', pro: '£3.99' },
|
|
annual: { basic: '£9.90', plus: '£24.90', pro: '£39.90' },
|
|
}
|
|
const PRICE_SUFFIX = { monthly: '/mo', annual: '/yr' }
|
|
|
|
function ctaHref(tier) {
|
|
if (tier === 'free') {
|
|
return isAuthenticated.value ? '/dashboard' : '/register'
|
|
}
|
|
if (!isAuthenticated.value) {
|
|
return '/register?tier=' + tier + '&cadence=' + cadence.value
|
|
}
|
|
if (userTier.value === tier) {
|
|
return '/billing/portal'
|
|
}
|
|
return '/billing/checkout/' + tier + '/' + cadence.value
|
|
}
|
|
|
|
function ctaLabel(tier) {
|
|
if (tier === 'free') {
|
|
return isAuthenticated.value ? 'Go to dashboard' : 'Start free'
|
|
}
|
|
if (isAuthenticated.value && userTier.value === tier) {
|
|
return 'Manage subscription'
|
|
}
|
|
return {
|
|
basic: 'Choose Daily',
|
|
plus: 'Choose Smart',
|
|
pro: 'Choose Pro',
|
|
}[tier]
|
|
}
|
|
</script>
|