- Extract LandingNav, LiveTicker, StatsRow, VerdictCard, and HeroSearch into reusable landing components - Implement responsive two-layout strategy: mobile stacked (hero search + verdict card + CTA) vs desktop inline pill input with verdict card sidebar - Add serif/mono font tokens and live-dot pulse animation to CSS - Move verdict card above search input on mobile, to right sidebar on desktop - Replace hero "fill up now" mockup with dynamic VerdictCard showing top stations, pricing, and recommendation - Simplify navigation with uppercase tracking, add Fleet anchor, and gate CTA by auth state - Lazy-load LeafletMap with defineAsyncComponent to reduce initial bundle - Relocate SearchBar below hero on search attempt for persistent filter UI - Add meta description for SEO
74 lines
3.0 KiB
Vue
74 lines
3.0 KiB
Vue
<template>
|
|
<div
|
|
:class="{ 'max-w-md mx-auto': variant === 'full' }"
|
|
class="bg-zinc-50 border border-zinc-300 rounded-3xl p-5 md:p-6 shadow-[0_20px_40px_-20px_rgba(74,63,59,0.25)]"
|
|
>
|
|
<!-- Header -->
|
|
<div class="flex items-center justify-between gap-3 mb-5">
|
|
<div class="min-w-0">
|
|
<p class="font-mono text-[10px] uppercase tracking-widest text-zinc-500 mb-1 truncate">
|
|
Today near {{ postcode }}
|
|
</p>
|
|
<h3
|
|
:class="variant === 'compact' ? 'text-3xl' : 'text-4xl'"
|
|
class="font-serif text-accent leading-none"
|
|
>
|
|
{{ verdict }}
|
|
</h3>
|
|
</div>
|
|
<span class="inline-flex items-center gap-1.5 shrink-0 font-mono text-[10px] uppercase tracking-widest text-zinc-600 bg-white border border-zinc-300 rounded-full px-2.5 py-1">
|
|
<span class="size-1.5 rounded-full bg-status-good live-dot"></span>
|
|
Live
|
|
</span>
|
|
</div>
|
|
|
|
<!-- Station rows -->
|
|
<ul class="space-y-2">
|
|
<li
|
|
v-for="(station, idx) in stations"
|
|
:key="station.name + idx"
|
|
class="flex items-center gap-3 bg-white border border-zinc-300 rounded-xl px-3 py-2.5"
|
|
>
|
|
<span class="font-mono text-xs text-zinc-500 tabular-nums">{{ String(idx + 1).padStart(2, '0') }}</span>
|
|
<div class="flex-1 min-w-0">
|
|
<p class="font-semibold text-sm text-zinc-800 truncate">{{ station.name }}</p>
|
|
<p class="font-mono text-[11px] text-zinc-500 mt-0.5">{{ station.distance }}</p>
|
|
</div>
|
|
<span
|
|
v-if="station.tag"
|
|
class="font-mono text-[9px] uppercase tracking-widest bg-accent text-white rounded-full px-2 py-0.5"
|
|
>
|
|
{{ station.tag }}
|
|
</span>
|
|
<span class="font-mono font-medium text-sm text-zinc-900 tabular-nums">{{ station.price }}</span>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Footer -->
|
|
<p v-if="moreCount" class="font-mono text-[11px] text-zinc-500 mt-4 text-center">
|
|
+ {{ moreCount }} more stations within {{ radiusMiles }} miles
|
|
</p>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
defineProps({
|
|
postcode: { type: String, default: 'SW1A 1AA' },
|
|
verdict: { type: String, default: 'Fill up today' },
|
|
stations: {
|
|
type: Array,
|
|
default: () => [
|
|
{ name: 'Tesco Victoria', distance: '0.4 mi', price: '142.9p', tag: 'Cheapest' },
|
|
{ name: "Sainsbury's Nine Elms", distance: '1.4 mi', price: '143.9p', tag: null },
|
|
],
|
|
},
|
|
moreCount: { type: Number, default: 21 },
|
|
radiusMiles: { type: Number, default: 5 },
|
|
variant: {
|
|
type: String,
|
|
default: 'full',
|
|
validator: (v) => ['full', 'compact'].includes(v),
|
|
},
|
|
})
|
|
</script>
|