Files
cursa-corbins-2026/src/app/page.tsx
T
2026-06-17 15:09:20 +02:00

786 lines
34 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import { useState, useRef, useEffect } from 'react'
import Image from 'next/image'
import { useSearchParams } from 'next/navigation'
import { Suspense } from 'react'
import { Ruler, ChevronDown, Check, AlertCircle, Loader2 } from 'lucide-react'
import Header from '@/components/Header'
import Footer from '@/components/Footer'
import CountdownBanner from '@/components/CountdownBanner'
import TrustSection from '@/components/TrustSection'
import SizeGuideModal from '@/components/SizeGuideModal'
import clsx from 'clsx'
// ─── Config ───────────────────────────────────────────────────────────────────
const PRODUCTS = {
samarreta: {
id: 'samarreta',
name: 'Samarreta',
fullName: 'Samarreta Cursa de la Cirera 2026',
price: 10.0,
image: '/assets/Samarreta.jpeg',
features: ['Teixit tècnic Dry Fit', 'Running Mesh Yarn', 'Samarreta Fullprint', 'Mànica recta'],
hasTshirt: true,
hasSocks: false,
badge: null,
},
mitjons: {
id: 'mitjons',
name: 'Mitjons',
fullName: 'Mitjons Cursa de la Cirera 2026',
price: 10.0,
image: '/assets/Mitjons.jpeg',
features: ['Blood Bros Socks', 'Alta qualitat', 'Disseny exclusiu', 'Talles XXSXL'],
hasTshirt: false,
hasSocks: true,
badge: null,
},
pack: {
id: 'pack',
name: 'Pack Complet',
fullName: 'Pack Samarreta + Mitjons',
price: 18.5,
image: '/assets/Pack Samarreta+Mitjons.jpeg',
features: ['Samarreta + Mitjons', 'Estalvia 1,50€', 'Tot l\'equipament', 'Millor preu'],
hasTshirt: true,
hasSocks: true,
badge: 'OFERTA',
},
} as const
type ProductId = keyof typeof PRODUCTS
const TSHIRT_SIZES = ['S', 'M', 'L', 'XL', 'XXL', '3XL']
const SOCK_SIZES = [
{ label: 'XXS', range: '32-34' },
{ label: 'XS', range: '35-37' },
{ label: 'S', range: '38-39' },
{ label: 'M', range: '40-42' },
{ label: 'L', range: '43-44' },
{ label: 'XL', range: '45-47' },
]
const PROVINCES = [
'Lleida', 'Barcelona', 'Girona', 'Tarragona',
'Aragó', 'Madrid', 'Valencia', 'Altres',
]
interface FormData {
nom: string
cognoms: string
telefon: string
email: string
adreca: string
codiPostal: string
poblacio: string
provincia: string
}
const EMPTY_FORM: FormData = {
nom: '', cognoms: '', telefon: '', email: '',
adreca: '', codiPostal: '', poblacio: '', provincia: '',
}
// ─── Cancelled Banner ─────────────────────────────────────────────────────────
function CancelledBanner() {
const params = useSearchParams()
const [visible, setVisible] = useState(false)
useEffect(() => {
if (params.get('cancelled') === 'true') setVisible(true)
}, [params])
if (!visible) return null
return (
<div className="max-w-3xl mx-auto px-4 sm:px-6 mt-6">
<div className="flex items-start gap-3 bg-yellow-500/10 border border-yellow-500/30 rounded-xl p-4">
<AlertCircle className="text-yellow-400 shrink-0 mt-0.5" size={18} />
<div>
<p className="text-yellow-300 font-semibold text-sm">Pagament cancel·lat</p>
<p className="text-yellow-400/70 text-xs mt-0.5">
Has cancel·lat el pagament. Torna a omplir el formulari quan vulguis per completar la teva comanda.
</p>
</div>
<button
onClick={() => setVisible(false)}
className="ml-auto text-yellow-500 hover:text-yellow-300 transition-colors shrink-0"
>
</button>
</div>
</div>
)
}
// ─── Main Page ────────────────────────────────────────────────────────────────
export default function HomePage() {
const [selectedProduct, setSelectedProduct] = useState<ProductId | null>(null)
const [sizeTshirt, setSizeTshirt] = useState('')
const [sizeSocks, setSizeSocks] = useState('')
const [formData, setFormData] = useState<FormData>(EMPTY_FORM)
const [shipping, setShipping] = useState<'correos' | 'recollida' | null>(null)
const [sizeGuide, setSizeGuide] = useState<'samarreta' | 'mitjons' | null>(null)
const [loading, setLoading] = useState(false)
const [errors, setErrors] = useState<Partial<Record<keyof FormData | 'product' | 'sizes' | 'shipping', string>>>({})
const sizesRef = useRef<HTMLDivElement>(null)
const formRef = useRef<HTMLDivElement>(null)
const shippingRef = useRef<HTMLDivElement>(null)
const summaryRef = useRef<HTMLDivElement>(null)
const product = selectedProduct ? PRODUCTS[selectedProduct] : null
const shippingCost = shipping === 'correos' ? 7.99 : 0
const total = product ? product.price + shippingCost : 0
function scrollTo(ref: React.RefObject<HTMLDivElement>) {
setTimeout(() => ref.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }), 100)
}
function handleSelectProduct(id: ProductId) {
setSelectedProduct(id)
setSizeTshirt('')
setSizeSocks('')
setShipping(null)
setErrors({})
scrollTo(sizesRef)
}
function handleSizeTshirt(s: string) {
setSizeTshirt(s)
if (!product?.hasSocks) scrollTo(formRef)
else if (sizeSocks) scrollTo(formRef)
}
function handleSizeSocks(s: string) {
setSizeSocks(s)
if (!product?.hasTshirt) scrollTo(formRef)
else if (sizeTshirt) scrollTo(formRef)
}
function handleField(key: keyof FormData, value: string) {
setFormData(prev => ({ ...prev, [key]: value }))
if (errors[key]) setErrors(prev => ({ ...prev, [key]: '' }))
}
function handleShipping(v: 'correos' | 'recollida') {
setShipping(v)
scrollTo(summaryRef)
}
function validate(): boolean {
const newErrors: typeof errors = {}
if (!selectedProduct) newErrors.product = 'Selecciona un producte'
if (product?.hasTshirt && !sizeTshirt) newErrors.sizes = 'Selecciona la talla de la samarreta'
if (product?.hasSocks && !sizeSocks) newErrors.sizes = (newErrors.sizes ? newErrors.sizes + ' i els ' : '') + 'Selecciona la talla dels mitjons'
if (!formData.nom.trim()) newErrors.nom = 'El nom és obligatori'
if (!formData.cognoms.trim()) newErrors.cognoms = 'Els cognoms són obligatoris'
if (!formData.telefon.trim()) newErrors.telefon = 'El telèfon és obligatori'
if (!formData.email.trim() || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email))
newErrors.email = 'Introdueix un email vàlid'
if (!shipping) newErrors.shipping = 'Selecciona una opció de lliurament'
if (shipping === 'correos') {
if (!formData.adreca.trim()) newErrors.adreca = "L'adreça és obligatòria per a enviament"
if (!formData.codiPostal.trim()) newErrors.codiPostal = 'El codi postal és obligatori'
if (!formData.poblacio.trim()) newErrors.poblacio = 'La població és obligatòria'
if (!formData.provincia.trim()) newErrors.provincia = 'La província és obligatòria'
}
setErrors(newErrors)
return Object.keys(newErrors).length === 0
}
async function handlePay() {
if (!validate()) {
const firstError = document.querySelector('[data-error]')
firstError?.scrollIntoView({ behavior: 'smooth', block: 'center' })
return
}
setLoading(true)
try {
const res = await fetch('/api/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
product: selectedProduct,
sizeTshirt: sizeTshirt || null,
sizeSocks: sizeSocks || null,
shipping,
...formData,
}),
})
const data = await res.json()
if (!res.ok) throw new Error(data.error ?? 'Error desconegut')
window.location.href = data.url
} catch (err: unknown) {
const message = err instanceof Error ? err.message : 'Error inesperat. Torna-ho a intentar.'
setErrors({ product: message })
setLoading(false)
}
}
const needsTshirtSize = product?.hasTshirt ?? false
const needsSockSize = product?.hasSocks ?? false
const sizesComplete = (!needsTshirtSize || sizeTshirt) && (!needsSockSize || sizeSocks)
const formComplete =
formData.nom && formData.cognoms && formData.telefon && formData.email &&
(shipping !== 'correos' || (formData.adreca && formData.codiPostal && formData.poblacio && formData.provincia))
const canPay = selectedProduct && sizesComplete && formComplete && shipping
return (
<>
<Header />
<CountdownBanner />
<main className="min-h-screen">
<Suspense fallback={null}>
<CancelledBanner />
</Suspense>
{/* ─── Hero ─────────────────────────────────────────────── */}
<section className="relative overflow-hidden">
<div
className="absolute inset-0 pointer-events-none"
style={{
background:
'radial-gradient(ellipse 80% 60% at 70% 40%, rgba(0,200,220,0.07) 0%, transparent 60%), radial-gradient(ellipse 60% 50% at 30% 60%, rgba(232,64,64,0.05) 0%, transparent 50%)',
}}
/>
<div className="max-w-6xl mx-auto px-4 sm:px-6 py-12 sm:py-20 grid lg:grid-cols-2 gap-10 items-center">
{/* Text side */}
<div className="order-2 lg:order-1">
<p className="section-title">Blood Bros Sport presenta</p>
<h1 className="text-5xl sm:text-6xl lg:text-7xl font-black text-white leading-none mb-4">
Cursa<br />
<span className="text-teal">de la</span><br />
Cirera
</h1>
<p className="text-xl sm:text-2xl font-black text-slate-400 mb-6">
Corbins 2026
</p>
<p className="text-slate-500 text-base mb-8 max-w-md leading-relaxed">
L'equipament oficial de la Cursa. Samarreta tècnica Fullprint i mitjons exclusius Blood Bros Socks.
Edició limitada. Comandes fins al 25 de juny.
</p>
<div className="flex flex-wrap gap-3">
<button
onClick={() => document.getElementById('productes')?.scrollIntoView({ behavior: 'smooth' })}
className="btn-pay w-auto px-8 py-3 text-base"
>
Compra ara
</button>
<div className="flex items-center gap-2 px-4 py-3 bg-dark-card border border-dark-border rounded-xl">
<Check size={16} className="text-teal" />
<span className="text-sm text-slate-400">Pagament segur via Stripe</span>
</div>
</div>
</div>
{/* Image side */}
<div className="order-1 lg:order-2 flex justify-center">
<div className="relative w-72 sm:w-96">
<div
className="absolute inset-0 rounded-3xl blur-3xl opacity-30"
style={{ background: 'linear-gradient(135deg, #00C8DC, #E84040)' }}
/>
<Image
src="/assets/Pack Samarreta+Mitjons.jpeg"
alt="Pack Samarreta + Mitjons Cursa de la Cirera 2026"
width={420}
height={560}
className="relative z-10 float-anim object-contain w-full"
priority
/>
</div>
</div>
</div>
</section>
{/* ─── STEP 1: PRODUCTES ────────────────────────────────── */}
<section id="productes" className="max-w-6xl mx-auto px-4 sm:px-6 pb-16">
<div className="mb-8">
<p className="section-title">Pas 1 de 4 · Producte</p>
<h2 className="text-3xl sm:text-4xl font-black text-white">
Quin equipament vols?
</h2>
{errors.product && (
<p data-error className="text-cherry text-sm mt-2 flex items-center gap-1">
<AlertCircle size={14} /> {errors.product}
</p>
)}
</div>
<div className="grid sm:grid-cols-3 gap-5">
{(Object.values(PRODUCTS) as typeof PRODUCTS[keyof typeof PRODUCTS][]).map((p) => (
<button
key={p.id}
onClick={() => handleSelectProduct(p.id as ProductId)}
className={clsx('product-card text-left', { selected: selectedProduct === p.id })}
>
{p.badge && (
<div className="absolute top-4 right-4">
<span className="badge badge-red">{p.badge}</span>
</div>
)}
<div className="relative mb-4 rounded-xl overflow-hidden bg-dark aspect-square">
<Image
src={p.image}
alt={p.fullName}
fill
className="object-cover transition-transform duration-500 group-hover:scale-105"
sizes="(max-width: 640px) 90vw, 30vw"
/>
{selectedProduct === p.id && (
<div className="absolute inset-0 bg-teal/10 flex items-center justify-center">
<div className="w-10 h-10 rounded-full bg-teal flex items-center justify-center">
<Check size={20} className="text-dark" style={{ color: '#000' }} />
</div>
</div>
)}
</div>
<h3 className="text-white font-black text-lg mb-1">{p.name}</h3>
<div className="text-2xl font-black text-teal mb-3">
{p.price.toFixed(2).replace('.', ',')}€
</div>
{p.id === 'pack' && (
<p className="text-slate-500 text-xs mb-2 line-through">vs 20,00€ per separat</p>
)}
<ul className="space-y-1">
{p.features.map((f) => (
<li key={f} className="flex items-center gap-2 text-slate-500 text-xs">
<span className="w-1 h-1 rounded-full bg-teal shrink-0" />
{f}
</li>
))}
</ul>
</button>
))}
</div>
</section>
{/* ─── STEP 2: TALLES ───────────────────────────────────── */}
<div ref={sizesRef} />
{selectedProduct && (
<section className="max-w-6xl mx-auto px-4 sm:px-6 pb-16 animate-fade-up">
<div className="mb-8">
<p className="section-title">Pas 2 de 4 · Talles</p>
<h2 className="text-3xl sm:text-4xl font-black text-white">
La teva talla
</h2>
{errors.sizes && (
<p data-error className="text-cherry text-sm mt-2 flex items-center gap-1">
<AlertCircle size={14} /> {errors.sizes}
</p>
)}
</div>
<div className={clsx('grid gap-8', product?.hasTshirt && product?.hasSocks ? 'sm:grid-cols-2' : 'sm:grid-cols-1 max-w-md')}>
{/* Tshirt sizes */}
{product?.hasTshirt && (
<div>
<div className="flex items-center justify-between mb-4">
<h3 className="text-white font-bold">Samarreta</h3>
<button
onClick={() => setSizeGuide('samarreta')}
className="flex items-center gap-1.5 text-teal text-xs hover:underline"
>
<Ruler size={14} />
Guia de talles
</button>
</div>
<div className="flex flex-wrap gap-2">
{TSHIRT_SIZES.map((s) => (
<button
key={s}
onClick={() => handleSizeTshirt(s)}
className={clsx('size-btn', { selected: sizeTshirt === s })}
>
{s}
</button>
))}
</div>
{sizeTshirt && (
<p className="text-teal text-xs mt-3 flex items-center gap-1">
<Check size={12} /> Talla {sizeTshirt} seleccionada
</p>
)}
</div>
)}
{/* Socks sizes */}
{product?.hasSocks && (
<div>
<div className="flex items-center justify-between mb-4">
<h3 className="text-white font-bold">Mitjons</h3>
<button
onClick={() => setSizeGuide('mitjons')}
className="flex items-center gap-1.5 text-teal text-xs hover:underline"
>
<Ruler size={14} />
Guia de talles
</button>
</div>
<div className="flex flex-wrap gap-2">
{SOCK_SIZES.map(({ label, range }) => (
<button
key={label}
onClick={() => handleSizeSocks(label)}
className={clsx('size-btn flex flex-col items-center', { selected: sizeSocks === label })}
>
<span>{label}</span>
<span className="text-[10px] opacity-60">{range}</span>
</button>
))}
</div>
{sizeSocks && (
<p className="text-teal text-xs mt-3 flex items-center gap-1">
<Check size={12} /> Talla {sizeSocks} seleccionada
</p>
)}
</div>
)}
</div>
</section>
)}
{/* ─── STEP 3: DADES ────────────────────────────────────── */}
<div ref={formRef} />
{selectedProduct && sizesComplete && (
<section className="max-w-6xl mx-auto px-4 sm:px-6 pb-16 animate-fade-up">
<div className="mb-8">
<p className="section-title">Pas 3 de 4 · Les teves dades</p>
<h2 className="text-3xl sm:text-4xl font-black text-white">
On t'enviem la comanda?
</h2>
</div>
<div className="max-w-2xl">
<div className="grid sm:grid-cols-2 gap-4">
{/* Nom */}
<div>
<label className="form-label">Nom *</label>
<input
type="text"
className={clsx('input-field', errors.nom && 'border-cherry')}
placeholder="El teu nom"
value={formData.nom}
onChange={(e) => handleField('nom', e.target.value)}
autoComplete="given-name"
/>
{errors.nom && (
<p data-error className="text-cherry text-xs mt-1">{errors.nom}</p>
)}
</div>
{/* Cognoms */}
<div>
<label className="form-label">Cognoms *</label>
<input
type="text"
className={clsx('input-field', errors.cognoms && 'border-cherry')}
placeholder="Els teus cognoms"
value={formData.cognoms}
onChange={(e) => handleField('cognoms', e.target.value)}
autoComplete="family-name"
/>
{errors.cognoms && (
<p data-error className="text-cherry text-xs mt-1">{errors.cognoms}</p>
)}
</div>
{/* Telèfon */}
<div>
<label className="form-label">Telèfon mòbil *</label>
<input
type="tel"
className={clsx('input-field', errors.telefon && 'border-cherry')}
placeholder="6XX XXX XXX"
value={formData.telefon}
onChange={(e) => handleField('telefon', e.target.value)}
autoComplete="tel"
/>
{errors.telefon && (
<p data-error className="text-cherry text-xs mt-1">{errors.telefon}</p>
)}
</div>
{/* Email */}
<div>
<label className="form-label">Correu electrònic *</label>
<input
type="email"
className={clsx('input-field', errors.email && 'border-cherry')}
placeholder="tu@exemple.com"
value={formData.email}
onChange={(e) => handleField('email', e.target.value)}
autoComplete="email"
/>
{errors.email && (
<p data-error className="text-cherry text-xs mt-1">{errors.email}</p>
)}
</div>
{/* Adreça */}
<div className="sm:col-span-2">
<label className="form-label">
Adreça postal
<span className="text-slate-600 normal-case font-normal ml-1">(per enviament a domicili)</span>
</label>
<input
type="text"
className={clsx('input-field', errors.adreca && 'border-cherry')}
placeholder="Carrer, número, pis..."
value={formData.adreca}
onChange={(e) => handleField('adreca', e.target.value)}
autoComplete="street-address"
/>
{errors.adreca && (
<p data-error className="text-cherry text-xs mt-1">{errors.adreca}</p>
)}
</div>
{/* CP */}
<div>
<label className="form-label">Codi postal</label>
<input
type="text"
className={clsx('input-field', errors.codiPostal && 'border-cherry')}
placeholder="25XXX"
value={formData.codiPostal}
onChange={(e) => handleField('codiPostal', e.target.value)}
maxLength={5}
autoComplete="postal-code"
/>
{errors.codiPostal && (
<p data-error className="text-cherry text-xs mt-1">{errors.codiPostal}</p>
)}
</div>
{/* Població */}
<div>
<label className="form-label">Població</label>
<input
type="text"
className={clsx('input-field', errors.poblacio && 'border-cherry')}
placeholder="Corbins, Lleida..."
value={formData.poblacio}
onChange={(e) => handleField('poblacio', e.target.value)}
autoComplete="address-level2"
/>
{errors.poblacio && (
<p data-error className="text-cherry text-xs mt-1">{errors.poblacio}</p>
)}
</div>
{/* Província */}
<div className="sm:col-span-2">
<label className="form-label">Província</label>
<select
className={clsx('input-field', errors.provincia && 'border-cherry')}
value={formData.provincia}
onChange={(e) => handleField('provincia', e.target.value)}
>
<option value="">Selecciona una província...</option>
{PROVINCES.map((p) => (
<option key={p} value={p}>{p}</option>
))}
</select>
{errors.provincia && (
<p data-error className="text-cherry text-xs mt-1">{errors.provincia}</p>
)}
</div>
</div>
{formComplete && (
<div className="mt-4 flex items-center gap-2 text-teal text-sm">
<Check size={16} />
<span>Dades correctes. Ara selecciona com vols rebre la comanda.</span>
</div>
)}
</div>
</section>
)}
{/* ─── STEP 4: ENVIAMENT ────────────────────────────────── */}
<div ref={shippingRef} />
{selectedProduct && sizesComplete && formData.nom && (
<section className="max-w-6xl mx-auto px-4 sm:px-6 pb-16 animate-fade-up">
<div className="mb-8">
<p className="section-title">Pas 4 de 4 · Lliurament</p>
<h2 className="text-3xl sm:text-4xl font-black text-white">
Com vols rebre-ho?
</h2>
{errors.shipping && (
<p data-error className="text-cherry text-sm mt-2 flex items-center gap-1">
<AlertCircle size={14} /> {errors.shipping}
</p>
)}
</div>
<div className="flex flex-col sm:flex-row gap-4 max-w-2xl">
{/* Correos Express */}
<button
onClick={() => handleShipping('correos')}
className={clsx('shipping-card text-left', { selected: shipping === 'correos' })}
>
<div className="flex items-start justify-between mb-3">
<div className="text-2xl">🚚</div>
{shipping === 'correos' && (
<div className="w-5 h-5 rounded-full bg-teal flex items-center justify-center shrink-0">
<Check size={12} style={{ color: '#000' }} />
</div>
)}
</div>
<h3 className="text-white font-bold mb-1">Correos Express</h3>
<p className="text-slate-500 text-sm mb-3">Enviament a casa teva en 24-48h des del moment que t&apos;ho enviem. Primer haurem de fabricar-te la comanda.</p>
<div className="text-cherry font-black text-xl">+7,99</div>
<p className="text-slate-600 text-xs mt-1">Lliurament previst: 29 Juny 3 Juliol</p>
</button>
{/* Recollida */}
<button
onClick={() => handleShipping('recollida')}
className={clsx('shipping-card text-left', { selected: shipping === 'recollida' })}
>
<div className="flex items-start justify-between mb-3">
<div className="text-2xl">📍</div>
{shipping === 'recollida' && (
<div className="w-5 h-5 rounded-full bg-teal flex items-center justify-center shrink-0">
<Check size={12} style={{ color: '#000' }} />
</div>
)}
</div>
<h3 className="text-white font-bold mb-1">Recollida Corbins / Lleida</h3>
<p className="text-slate-500 text-sm mb-3">Ens posarem en contacte amb tu per coordinar l&apos;entrega. Primer haurem de fabricar-te la comanda.</p>
<div className="text-teal font-black text-xl">Gratuït</div>
<p className="text-slate-600 text-xs mt-1">Lliurament previst: 29 Juny 3 Juliol</p>
</button>
</div>
</section>
)}
{/* ─── RESUM I PAGAMENT ─────────────────────────────────── */}
<div ref={summaryRef} />
{selectedProduct && sizesComplete && formData.nom && shipping && (
<section className="max-w-6xl mx-auto px-4 sm:px-6 pb-20 animate-fade-up">
<div className="mb-8">
<p className="section-title">Resum de la comanda</p>
<h2 className="text-3xl sm:text-4xl font-black text-white">
Llest per pagar?
</h2>
</div>
<div className="max-w-lg">
<div className="bg-dark-card border border-dark-border rounded-2xl overflow-hidden">
{/* Summary items */}
<div className="p-6 space-y-4">
<div className="flex items-center gap-4">
<div className="w-16 h-16 rounded-xl overflow-hidden bg-dark shrink-0">
<Image
src={product!.image}
alt={product!.fullName}
width={64}
height={64}
className="w-full h-full object-cover"
/>
</div>
<div className="flex-1">
<div className="text-white font-bold text-sm">{product!.fullName}</div>
<div className="text-slate-500 text-xs mt-0.5">
{sizeTshirt && `Samarreta: ${sizeTshirt}`}
{sizeTshirt && sizeSocks && ' · '}
{sizeSocks && `Mitjons: ${sizeSocks}`}
</div>
</div>
<div className="text-white font-bold">
{product!.price.toFixed(2).replace('.', ',')}
</div>
</div>
<div className="flex items-center justify-between text-sm">
<span className="text-slate-400">
{shipping === 'correos' ? 'Correos Express' : 'Recollida Corbins/Lleida'}
</span>
<span className={shipping === 'correos' ? 'text-white' : 'text-teal'}>
{shipping === 'correos' ? '7,99€' : 'Gratuït'}
</span>
</div>
<div className="border-t border-dark-border pt-4 flex items-center justify-between">
<span className="text-white font-black text-lg">TOTAL</span>
<span className="text-teal font-black text-3xl">
{total.toFixed(2).replace('.', ',')}
</span>
</div>
</div>
{/* Client summary */}
<div className="border-t border-dark-border px-6 py-4 bg-dark/40">
<p className="text-slate-500 text-xs mb-2 uppercase tracking-wider">Comanda per a</p>
<p className="text-white text-sm font-semibold">{formData.nom} {formData.cognoms}</p>
<p className="text-slate-500 text-xs">{formData.email} · {formData.telefon}</p>
{shipping === 'correos' && formData.adreca && (
<p className="text-slate-500 text-xs mt-1">
{formData.adreca}, {formData.codiPostal} {formData.poblacio}
</p>
)}
</div>
{/* Pay button */}
<div className="p-6 border-t border-dark-border">
<button
onClick={handlePay}
disabled={!canPay || loading}
className="btn-pay"
>
{loading ? (
<span className="flex items-center justify-center gap-2">
<Loader2 size={20} className="animate-spin" />
Preparant pagament...
</span>
) : (
<span className="flex items-center justify-center gap-2">
Pagar {total.toFixed(2).replace('.', ',')} amb Stripe
<svg viewBox="0 0 60 25" fill="currentColor" className="h-5 opacity-80">
<path d="M59.64 14.28h-8.06c.19 1.93 1.6 2.55 3.2 2.55 1.64 0 2.96-.37 4.05-.95v3.32a12.08 12.08 0 0 1-4.56.83c-4.06 0-6.8-2.22-6.8-7 0-4.6 2.7-7.1 6.3-7.1 3.54 0 5.96 2.38 5.96 7.35zm-8.12-2.3h4.12c-.1-1.81-.98-2.59-2-2.59-1.01 0-1.99.68-2.12 2.59zM44.9 25l1.59-8.6a13.83 13.83 0 0 1-3.5.48c-3.36 0-4.76-2.28-4.76-6.31 0-4.48 2.45-7.45 6.43-7.45 1.32 0 2.47.23 3.45.65l.6-3.15A17.1 17.1 0 0 0 44.5 0C39.29 0 33.5 3.71 33.5 10.72c0 5.72 2.91 9.06 8.42 9.06a14 14 0 0 0 3.81-.52L44.9 25zm-25.08-3.84c.79 0 1.5-.18 2.12-.54L20.6 25c-.74.28-1.74.48-2.86.48C13.38 25.48 11 22.9 11 18.7c0-5.5 3.1-8.05 7.09-8.05 2.9 0 5.1 1.3 6.1 3.67L22 13.2l.54-2.86H18.3L15.4 25h4.42zm-3-7.03c0 2.14 1.07 3.09 2.76 3.09.4 0 .78-.1 1.12-.26L20.5 14h-.21c-1.82 0-3.47 1-3.47 4.13zM10.04 7.75a3.6 3.6 0 0 1-1.4-.28L7 18.73H2.86L5.9 3h4.32l-.53 2.83c.74-.87 1.83-2.28 3.26-2.83h.3l-1.3 5.31c-.5-.39-1.25-.56-1.91-.56z" />
</svg>
</span>
)}
</button>
<p className="text-center text-slate-600 text-xs mt-3">
Seràs redirigit a Stripe per completar el pagament de forma segura
</p>
</div>
</div>
</div>
</section>
)}
<TrustSection />
{/* CTA if nothing selected yet */}
{!selectedProduct && (
<div className="text-center py-8 pb-16">
<button
onClick={() => document.getElementById('productes')?.scrollIntoView({ behavior: 'smooth' })}
className="flex items-center gap-2 mx-auto text-teal hover:text-white transition-colors text-sm"
>
<ChevronDown size={18} className="animate-bounce" />
Veure productes
</button>
</div>
)}
</main>
<Footer />
{/* Size guide modal */}
{sizeGuide && (
<SizeGuideModal type={sizeGuide} onClose={() => setSizeGuide(null)} />
)}
</>
)
}