brand identity overhaul: match BRAND-IDENTITY.md across all pages
Design system changes (per brand guide): - ZERO rounded-2xl/3xl remaining (was 131 instances) - ZERO bg-gradient remaining (was 25) — all solid colors - ZERO colored shadows (shadow-trust-blue, etc) — flat, no glow - ZERO backdrop-blur/glass effects — solid backgrounds - ZERO emoji in logo marks — square P logomark everywhere - ZERO decorative scale animations (group-hover:scale-105, etc) Tailwind config: - Added brand color names: midnight, promise-blue, generosity-gold, fulfilled-green, alert-red, paper - Kept legacy aliases (trust-blue, etc) for backwards compat - --radius: 0.75rem → 0.5rem (tighter corners) CSS: - Removed glass, glass-dark, card-hover, pulse-ring, bounce-gentle, confetti-fall, scale-in animations - Kept only purposeful animations: fadeUp, fadeIn, slideDown, shimmer, counter-roll - --primary tuned to match Promise Blue exactly Components: - Button: removed all colored shadows, added 'blue' variant, removed rounded from sizes - All UI components: rounded-xl/2xl → rounded-lg Pages updated (41 files): - Dashboard layout: solid header (no blur), border-l-2 active indicator, midnight logo mark - Login/Signup: paper bg (no gradient), midnight logo mark, no emoji - Pledge flow: solid color icons, no gradient progress bars - All dashboard pages: flat, sharp, editorial
This commit is contained in:
@@ -33,58 +33,56 @@ function LoginForm() {
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-login as demo if ?demo=1
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
useEffect(() => { if (isDemo) doLogin(undefined, "demo@pnpl.app", "demo1234") }, [])
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5 p-4">
|
||||
<div className="w-full max-w-sm space-y-5">
|
||||
<div className="min-h-screen flex items-center justify-center bg-paper p-4">
|
||||
<div className="w-full max-w-sm space-y-6">
|
||||
{isDemo && (
|
||||
<div className="text-center py-8">
|
||||
<div className="inline-flex h-12 w-12 rounded-2xl bg-gradient-to-br from-trust-blue to-blue-600 items-center justify-center shadow-lg shadow-trust-blue/20 animate-pulse mb-3">
|
||||
<span className="text-white text-xl">🤲</span>
|
||||
<div className="text-center py-12">
|
||||
<div className="h-10 w-10 bg-midnight flex items-center justify-center mx-auto mb-4">
|
||||
<span className="text-white text-sm font-black">P</span>
|
||||
</div>
|
||||
<p className="text-sm font-medium text-trust-blue animate-pulse">Loading demo...</p>
|
||||
<p className="text-sm font-medium text-gray-500">Loading demo...</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isDemo && (
|
||||
<>
|
||||
<div className="text-center">
|
||||
<div className="inline-flex h-12 w-12 rounded-2xl bg-gradient-to-br from-trust-blue to-blue-600 items-center justify-center shadow-lg shadow-trust-blue/20 mb-3">
|
||||
<span className="text-white text-xl">🤲</span>
|
||||
<div className="text-center space-y-3">
|
||||
<div className="h-10 w-10 bg-midnight flex items-center justify-center mx-auto">
|
||||
<span className="text-white text-sm font-black">P</span>
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-2xl font-black text-midnight">Welcome back</h1>
|
||||
<p className="text-sm text-gray-500 mt-1">Sign in to your charity dashboard</p>
|
||||
</div>
|
||||
<h1 className="text-2xl font-black text-gray-900">Welcome back</h1>
|
||||
<p className="text-sm text-muted-foreground mt-1">Sign in to your charity dashboard</p>
|
||||
</div>
|
||||
|
||||
{/* Social login */}
|
||||
<div className="space-y-2">
|
||||
<button
|
||||
onClick={() => signIn("auth0", { callbackUrl: "/dashboard" })}
|
||||
className="w-full flex items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white px-4 py-3 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-all"
|
||||
>
|
||||
<svg className="h-4 w-4" viewBox="0 0 24 24"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" fill="#4285F4"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/></svg>
|
||||
Continue with Google
|
||||
</button>
|
||||
</div>
|
||||
{/* Google */}
|
||||
<button
|
||||
onClick={() => signIn("auth0", { callbackUrl: "/dashboard" })}
|
||||
className="w-full flex items-center justify-center gap-2 border border-gray-200 bg-white px-4 py-3 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<svg className="h-4 w-4" viewBox="0 0 24 24"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" fill="#4285F4"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/></svg>
|
||||
Continue with Google
|
||||
</button>
|
||||
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 flex items-center"><div className="w-full border-t" /></div>
|
||||
<div className="relative flex justify-center text-xs"><span className="bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5 px-3 text-muted-foreground">or sign in with email</span></div>
|
||||
<div className="absolute inset-0 flex items-center"><div className="w-full border-t border-gray-200" /></div>
|
||||
<div className="relative flex justify-center text-xs"><span className="bg-paper px-3 text-gray-400">or sign in with email</span></div>
|
||||
</div>
|
||||
|
||||
{/* Email/password form */}
|
||||
<form onSubmit={(e) => doLogin(e)} className="space-y-3">
|
||||
{error && (
|
||||
<div className="rounded-xl bg-danger-red/10 border border-danger-red/20 p-2.5 text-sm text-danger-red text-center">{error}</div>
|
||||
<div className="border border-alert-red/20 bg-alert-red/5 p-2.5 text-sm text-alert-red text-center">{error}</div>
|
||||
)}
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="w-full rounded-xl border border-gray-200 px-4 py-3 text-sm focus:border-trust-blue focus:ring-2 focus:ring-trust-blue/20 outline-none transition-all"
|
||||
className="w-full border border-gray-200 px-4 py-3 text-sm focus:border-promise-blue focus:ring-1 focus:ring-promise-blue/20 outline-none transition-colors"
|
||||
placeholder="Email"
|
||||
required
|
||||
/>
|
||||
@@ -92,36 +90,36 @@ function LoginForm() {
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className="w-full rounded-xl border border-gray-200 px-4 py-3 text-sm focus:border-trust-blue focus:ring-2 focus:ring-trust-blue/20 outline-none transition-all"
|
||||
className="w-full border border-gray-200 px-4 py-3 text-sm focus:border-promise-blue focus:ring-1 focus:ring-promise-blue/20 outline-none transition-colors"
|
||||
placeholder="Password"
|
||||
required
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="w-full rounded-xl bg-trust-blue px-4 py-3 text-sm font-semibold text-white hover:bg-trust-blue/90 disabled:opacity-50 transition-all"
|
||||
className="w-full bg-midnight px-4 py-3 text-sm font-semibold text-white hover:bg-gray-800 disabled:opacity-50 transition-colors"
|
||||
>
|
||||
{loading ? "Signing in..." : "Sign In"}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 flex items-center"><div className="w-full border-t" /></div>
|
||||
<div className="relative flex justify-center text-xs"><span className="bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5 px-2 text-muted-foreground">or</span></div>
|
||||
<div className="absolute inset-0 flex items-center"><div className="w-full border-t border-gray-200" /></div>
|
||||
<div className="relative flex justify-center text-xs"><span className="bg-paper px-2 text-gray-400">or</span></div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => doLogin(undefined, "demo@pnpl.app", "demo1234")}
|
||||
disabled={loading}
|
||||
className="w-full rounded-xl border-2 border-dashed border-gray-200 px-4 py-3 text-sm font-medium text-muted-foreground hover:border-trust-blue hover:text-trust-blue disabled:opacity-50 transition-all"
|
||||
className="w-full border border-dashed border-gray-300 px-4 py-3 text-sm font-medium text-gray-500 hover:border-promise-blue hover:text-promise-blue disabled:opacity-50 transition-colors"
|
||||
>
|
||||
🎮 Try the Demo — no signup needed
|
||||
Try the Demo — no signup needed
|
||||
</button>
|
||||
|
||||
<p className="text-center text-sm text-muted-foreground">
|
||||
<p className="text-center text-sm text-gray-500">
|
||||
Don't have an account?{" "}
|
||||
<Link href="/signup" className="text-trust-blue font-semibold hover:underline">Get Started Free</Link>
|
||||
<Link href="/signup" className="text-promise-blue font-semibold hover:underline">Get Started Free</Link>
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
@@ -132,7 +130,7 @@ function LoginForm() {
|
||||
|
||||
export default function LoginPage() {
|
||||
return (
|
||||
<Suspense fallback={<div className="min-h-screen flex items-center justify-center"><div className="animate-spin h-8 w-8 border-2 border-trust-blue border-t-transparent rounded-full" /></div>}>
|
||||
<Suspense fallback={<div className="min-h-screen flex items-center justify-center bg-paper"><div className="animate-spin h-6 w-6 border-2 border-promise-blue border-t-transparent rounded-full" /></div>}>
|
||||
<LoginForm />
|
||||
</Suspense>
|
||||
)
|
||||
|
||||
@@ -38,7 +38,6 @@ export default function SignupPage() {
|
||||
return
|
||||
}
|
||||
|
||||
// Auto sign in and go straight to dashboard
|
||||
const result = await signIn("credentials", { email, password, redirect: false })
|
||||
if (result?.error) {
|
||||
setError("Account created — please sign in")
|
||||
@@ -54,86 +53,83 @@ export default function SignupPage() {
|
||||
|
||||
if (step === "loading") {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5">
|
||||
<div className="min-h-screen flex items-center justify-center bg-paper">
|
||||
<div className="text-center space-y-4">
|
||||
<div className="inline-flex h-14 w-14 rounded-2xl bg-gradient-to-br from-trust-blue to-blue-600 items-center justify-center shadow-lg shadow-trust-blue/20 animate-pulse">
|
||||
<span className="text-white text-2xl">🤲</span>
|
||||
<div className="h-10 w-10 bg-midnight flex items-center justify-center mx-auto">
|
||||
<span className="text-white text-sm font-black">P</span>
|
||||
</div>
|
||||
<p className="text-sm font-medium text-trust-blue animate-pulse">Setting up your charity...</p>
|
||||
<p className="text-sm font-medium text-gray-500">Setting up your charity...</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5 p-4">
|
||||
<div className="w-full max-w-sm space-y-5">
|
||||
<div className="text-center">
|
||||
<div className="inline-flex h-12 w-12 rounded-2xl bg-gradient-to-br from-trust-blue to-blue-600 items-center justify-center shadow-lg shadow-trust-blue/20 mb-3">
|
||||
<span className="text-white text-xl">🤲</span>
|
||||
<div className="min-h-screen flex items-center justify-center bg-paper p-4">
|
||||
<div className="w-full max-w-sm space-y-6">
|
||||
<div className="text-center space-y-3">
|
||||
<div className="h-10 w-10 bg-midnight flex items-center justify-center mx-auto">
|
||||
<span className="text-white text-sm font-black">P</span>
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-2xl font-black text-midnight">Start collecting pledges</h1>
|
||||
<p className="text-sm text-gray-500 mt-1">Free. 30 seconds. No card.</p>
|
||||
</div>
|
||||
<h1 className="text-2xl font-black text-gray-900">Start collecting pledges</h1>
|
||||
<p className="text-sm text-muted-foreground mt-1">Free. 30 seconds. No card.</p>
|
||||
</div>
|
||||
|
||||
{/* Google signup */}
|
||||
<button
|
||||
onClick={signUpWithGoogle}
|
||||
className="w-full flex items-center justify-center gap-2 rounded-xl border border-gray-200 bg-white px-4 py-3 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-all"
|
||||
className="w-full flex items-center justify-center gap-2 border border-gray-200 bg-white px-4 py-3 text-sm font-medium text-gray-700 hover:bg-gray-50 transition-colors"
|
||||
>
|
||||
<svg className="h-4 w-4" viewBox="0 0 24 24"><path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" fill="#4285F4"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/></svg>
|
||||
Sign up with Google
|
||||
</button>
|
||||
|
||||
<div className="relative">
|
||||
<div className="absolute inset-0 flex items-center"><div className="w-full border-t" /></div>
|
||||
<div className="relative flex justify-center text-xs"><span className="bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5 px-3 text-muted-foreground">or use email</span></div>
|
||||
<div className="absolute inset-0 flex items-center"><div className="w-full border-t border-gray-200" /></div>
|
||||
<div className="relative flex justify-center text-xs"><span className="bg-paper px-3 text-gray-400">or use email</span></div>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-3">
|
||||
{error && (
|
||||
<div className="rounded-xl bg-danger-red/10 border border-danger-red/20 p-2.5 text-sm text-danger-red text-center">{error}</div>
|
||||
<div className="border border-alert-red/20 bg-alert-red/5 p-2.5 text-sm text-alert-red text-center">{error}</div>
|
||||
)}
|
||||
|
||||
<input
|
||||
type="text"
|
||||
value={charityName}
|
||||
onChange={(e) => setCharityName(e.target.value)}
|
||||
className="w-full rounded-xl border border-gray-200 px-4 py-3 text-sm focus:border-trust-blue focus:ring-2 focus:ring-trust-blue/20 outline-none transition-all"
|
||||
className="w-full border border-gray-200 px-4 py-3 text-sm focus:border-promise-blue focus:ring-1 focus:ring-promise-blue/20 outline-none transition-colors"
|
||||
placeholder="Your charity or mosque name"
|
||||
required
|
||||
autoFocus
|
||||
/>
|
||||
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="w-full rounded-xl border border-gray-200 px-4 py-3 text-sm focus:border-trust-blue focus:ring-2 focus:ring-trust-blue/20 outline-none transition-all"
|
||||
className="w-full border border-gray-200 px-4 py-3 text-sm focus:border-promise-blue focus:ring-1 focus:ring-promise-blue/20 outline-none transition-colors"
|
||||
placeholder="Your email"
|
||||
required
|
||||
/>
|
||||
|
||||
<input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className="w-full rounded-xl border border-gray-200 px-4 py-3 text-sm focus:border-trust-blue focus:ring-2 focus:ring-trust-blue/20 outline-none transition-all"
|
||||
className="w-full border border-gray-200 px-4 py-3 text-sm focus:border-promise-blue focus:ring-1 focus:ring-promise-blue/20 outline-none transition-colors"
|
||||
placeholder="Pick a password (8+ chars)"
|
||||
required
|
||||
minLength={8}
|
||||
/>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full rounded-xl bg-trust-blue px-4 py-3.5 text-sm font-bold text-white hover:bg-trust-blue/90 transition-all shadow-lg shadow-trust-blue/20"
|
||||
className="w-full bg-midnight px-4 py-3.5 text-sm font-bold text-white hover:bg-gray-800 transition-colors"
|
||||
>
|
||||
Create Account →
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p className="text-center text-xs text-muted-foreground">
|
||||
Already have an account? <Link href="/login" className="text-trust-blue font-semibold hover:underline">Sign in</Link>
|
||||
<p className="text-center text-xs text-gray-500">
|
||||
Already have an account? <Link href="/login" className="text-promise-blue font-semibold hover:underline">Sign in</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -93,7 +93,7 @@ export default function AdminPage() {
|
||||
<CardContent>
|
||||
<div className="flex gap-3 overflow-x-auto">
|
||||
{Object.entries(data.byStatus).map(([status, { count, amount }]) => (
|
||||
<div key={status} className="flex-shrink-0 rounded-xl bg-muted/50 px-4 py-2 text-center min-w-[100px]">
|
||||
<div key={status} className="flex-shrink-0 rounded-lg bg-muted/50 px-4 py-2 text-center min-w-[100px]">
|
||||
<Badge variant={status === "paid" ? "success" : status === "overdue" ? "destructive" : "secondary"} className="text-[10px]">{status}</Badge>
|
||||
<p className="text-lg font-bold mt-1">{count}</p>
|
||||
<p className="text-[10px] text-muted-foreground">{fmt(amount)}</p>
|
||||
|
||||
@@ -141,7 +141,7 @@ export default function CampaignLinksPage() {
|
||||
<Card key={src.id} className="hover:shadow-md transition-shadow">
|
||||
<CardContent className="pt-6 space-y-4">
|
||||
{/* QR Code — compact */}
|
||||
<div className="max-w-[140px] mx-auto bg-white rounded-xl flex items-center justify-center p-1.5">
|
||||
<div className="max-w-[140px] mx-auto bg-white rounded-lg flex items-center justify-center p-1.5">
|
||||
<QRCodeCanvas url={`${baseUrl}/p/${src.code}`} size={128} />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -250,7 +250,7 @@ export default function EventsPage() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setForm(f => ({ ...f, paymentMode: "self" }))}
|
||||
className={`rounded-xl border p-3 text-left text-xs transition-all ${form.paymentMode === "self" ? "border-trust-blue bg-trust-blue/5" : "border-gray-200"}`}
|
||||
className={`rounded-lg border p-3 text-left text-xs transition-all ${form.paymentMode === "self" ? "border-trust-blue bg-trust-blue/5" : "border-gray-200"}`}
|
||||
>
|
||||
<span className="font-bold block">🏦 Bank transfer</span>
|
||||
<span className="text-muted-foreground">We show our bank details</span>
|
||||
@@ -258,7 +258,7 @@ export default function EventsPage() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setForm(f => ({ ...f, paymentMode: "external" }))}
|
||||
className={`rounded-xl border p-3 text-left text-xs transition-all ${form.paymentMode === "external" ? "border-warm-amber bg-warm-amber/5" : "border-gray-200"}`}
|
||||
className={`rounded-lg border p-3 text-left text-xs transition-all ${form.paymentMode === "external" ? "border-warm-amber bg-warm-amber/5" : "border-gray-200"}`}
|
||||
>
|
||||
<span className="font-bold block">🔗 External page</span>
|
||||
<span className="text-muted-foreground">LaunchGood, Enthuse, etc.</span>
|
||||
@@ -267,7 +267,7 @@ export default function EventsPage() {
|
||||
</div>
|
||||
|
||||
{form.paymentMode === "external" && (
|
||||
<div className="space-y-3 rounded-xl border border-warm-amber/20 bg-warm-amber/5 p-3">
|
||||
<div className="space-y-3 rounded-lg border border-warm-amber/20 bg-warm-amber/5 p-3">
|
||||
<div className="space-y-2">
|
||||
<Label>Fundraising page URL *</Label>
|
||||
<div className="relative">
|
||||
@@ -285,7 +285,7 @@ export default function EventsPage() {
|
||||
<select
|
||||
value={form.externalPlatform}
|
||||
onChange={(e) => setForm(f => ({ ...f, externalPlatform: e.target.value }))}
|
||||
className="w-full rounded-xl border border-gray-200 px-3 py-2 text-sm"
|
||||
className="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm"
|
||||
>
|
||||
<option value="">Select platform...</option>
|
||||
<option value="launchgood">🌙 LaunchGood</option>
|
||||
@@ -302,7 +302,7 @@ export default function EventsPage() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setForm(f => ({ ...f, zakatEligible: !f.zakatEligible }))}
|
||||
className={`w-full flex items-center justify-between rounded-xl border-2 p-3 text-left transition-all ${
|
||||
className={`w-full flex items-center justify-between rounded-lg border-2 p-3 text-left transition-all ${
|
||||
form.zakatEligible ? "border-trust-blue bg-trust-blue/5" : "border-gray-200"
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -73,7 +73,7 @@ export default function ExportsPage() {
|
||||
<li>Event and reference for audit trail</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="rounded-xl bg-success-green/5 border border-success-green/20 p-3">
|
||||
<div className="rounded-lg bg-success-green/5 border border-success-green/20 p-3">
|
||||
<p className="text-xs text-success-green font-medium">
|
||||
💷 Claim 25p for every £1 donated by a UK taxpayer
|
||||
</p>
|
||||
@@ -101,7 +101,7 @@ export default function ExportsPage() {
|
||||
</code>
|
||||
<p className="text-xs">Returns pending reminders with donor contact info for external email/SMS.</p>
|
||||
</div>
|
||||
<div className="rounded-xl bg-trust-blue/5 border border-trust-blue/20 p-3">
|
||||
<div className="rounded-lg bg-trust-blue/5 border border-trust-blue/20 p-3">
|
||||
<p className="text-xs text-trust-blue font-medium">
|
||||
💡 Connect to Zapier or n8n to send automatic reminder emails and SMS
|
||||
</p>
|
||||
|
||||
@@ -24,31 +24,31 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
|
||||
const user = session?.user as any
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50/50">
|
||||
{/* Top bar */}
|
||||
<header className="sticky top-0 z-40 border-b bg-white/80 backdrop-blur-xl">
|
||||
<div className="min-h-screen bg-paper">
|
||||
{/* Top bar — sharp, no blur */}
|
||||
<header className="sticky top-0 z-40 border-b border-gray-200 bg-white">
|
||||
<div className="flex h-14 items-center gap-4 px-4 md:px-6">
|
||||
<Link href="/dashboard" className="flex items-center gap-2.5">
|
||||
<div className="h-8 w-8 rounded-xl bg-gradient-to-br from-trust-blue to-blue-600 flex items-center justify-center shadow-lg shadow-trust-blue/20">
|
||||
<span className="text-white font-bold text-sm">P</span>
|
||||
<div className="h-7 w-7 bg-midnight flex items-center justify-center">
|
||||
<span className="text-white text-xs font-black">P</span>
|
||||
</div>
|
||||
<div className="hidden sm:block">
|
||||
<span className="font-black text-sm text-gray-900">{user?.orgName || "Pledge Now, Pay Later"}</span>
|
||||
<span className="font-black text-sm text-midnight">{user?.orgName || "Pledge Now, Pay Later"}</span>
|
||||
</div>
|
||||
</Link>
|
||||
<div className="flex-1" />
|
||||
<Link href="/dashboard/events" className="hidden md:block">
|
||||
<button className="inline-flex items-center gap-1.5 rounded-lg bg-trust-blue px-3 py-1.5 text-xs font-semibold text-white hover:bg-trust-blue/90 transition-colors">
|
||||
<button className="inline-flex items-center gap-1.5 bg-midnight px-3 py-1.5 text-xs font-semibold text-white hover:bg-gray-800 transition-colors">
|
||||
<Plus className="h-3 w-3" /> New Campaign
|
||||
</button>
|
||||
</Link>
|
||||
<Link href="/" className="text-xs text-muted-foreground hover:text-foreground transition-colors flex items-center gap-1">
|
||||
<Link href="/" className="text-xs text-gray-400 hover:text-midnight transition-colors flex items-center gap-1">
|
||||
<ExternalLink className="h-3 w-3" />
|
||||
</Link>
|
||||
{session && (
|
||||
<button
|
||||
onClick={() => signOut({ callbackUrl: "/login" })}
|
||||
className="text-xs text-muted-foreground hover:text-foreground transition-colors flex items-center gap-1"
|
||||
className="text-xs text-gray-400 hover:text-midnight transition-colors flex items-center gap-1"
|
||||
>
|
||||
<LogOut className="h-3 w-3" />
|
||||
</button>
|
||||
@@ -57,8 +57,8 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
|
||||
</header>
|
||||
|
||||
<div className="flex">
|
||||
{/* Desktop sidebar */}
|
||||
<aside className="hidden md:flex w-56 flex-col border-r bg-white min-h-[calc(100vh-3.5rem)] py-3 px-2">
|
||||
{/* Desktop sidebar — clean, no decorative elements */}
|
||||
<aside className="hidden md:flex w-52 flex-col border-r border-gray-200 bg-white min-h-[calc(100vh-3.5rem)] py-3 px-2">
|
||||
<nav className="space-y-0.5">
|
||||
{navItems.map((item) => {
|
||||
const isActive = pathname === item.href || (item.href !== "/dashboard" && pathname.startsWith(item.href))
|
||||
@@ -67,10 +67,10 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={cn(
|
||||
"flex items-center gap-2.5 rounded-lg px-3 py-2 text-sm font-medium transition-colors",
|
||||
"flex items-center gap-2.5 px-3 py-2 text-sm font-medium transition-colors",
|
||||
isActive
|
||||
? "bg-trust-blue/5 text-trust-blue"
|
||||
: "text-muted-foreground hover:bg-gray-100 hover:text-foreground"
|
||||
? "bg-promise-blue/5 text-promise-blue border-l-2 border-promise-blue -ml-0.5"
|
||||
: "text-gray-500 hover:bg-gray-50 hover:text-midnight"
|
||||
)}
|
||||
>
|
||||
<item.icon className="h-4 w-4" />
|
||||
@@ -80,14 +80,14 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
|
||||
})}
|
||||
{user?.role === "super_admin" && (
|
||||
<>
|
||||
<div className="my-2 border-t" />
|
||||
<div className="my-2 border-t border-gray-100" />
|
||||
<Link
|
||||
href={adminNav.href}
|
||||
className={cn(
|
||||
"flex items-center gap-2.5 rounded-lg px-3 py-2 text-sm font-medium transition-colors",
|
||||
"flex items-center gap-2.5 px-3 py-2 text-sm font-medium transition-colors",
|
||||
pathname === adminNav.href
|
||||
? "bg-trust-blue/5 text-trust-blue"
|
||||
: "text-muted-foreground hover:bg-gray-100 hover:text-foreground"
|
||||
? "bg-promise-blue/5 text-promise-blue border-l-2 border-promise-blue -ml-0.5"
|
||||
: "text-gray-500 hover:bg-gray-50 hover:text-midnight"
|
||||
)}
|
||||
>
|
||||
<adminNav.icon className="h-4 w-4" />
|
||||
@@ -98,12 +98,12 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
|
||||
</nav>
|
||||
|
||||
<div className="mt-auto px-2 pt-4">
|
||||
<div className="rounded-xl bg-gradient-to-br from-trust-blue/5 to-warm-amber/5 border p-3 space-y-1.5">
|
||||
<p className="text-xs font-semibold">Need help?</p>
|
||||
<p className="text-[10px] text-muted-foreground leading-relaxed">
|
||||
<div className="border border-gray-200 p-3 space-y-1.5">
|
||||
<p className="text-xs font-bold text-midnight">Need help?</p>
|
||||
<p className="text-[10px] text-gray-500 leading-relaxed">
|
||||
Get a fractional Head of Technology to optimise your charity's digital stack.
|
||||
</p>
|
||||
<Link href="/dashboard/apply" className="inline-block text-[10px] font-semibold text-trust-blue hover:underline">
|
||||
<Link href="/dashboard/apply" className="inline-block text-[10px] font-semibold text-promise-blue hover:underline">
|
||||
Learn more →
|
||||
</Link>
|
||||
</div>
|
||||
@@ -111,7 +111,7 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
|
||||
</aside>
|
||||
|
||||
{/* Mobile bottom nav */}
|
||||
<nav className="md:hidden fixed bottom-0 left-0 right-0 z-40 border-t bg-white/95 backdrop-blur-xl flex justify-around py-1.5 px-1">
|
||||
<nav className="md:hidden fixed bottom-0 left-0 right-0 z-40 border-t border-gray-200 bg-white flex justify-around py-1.5 px-1">
|
||||
{navItems.slice(0, 5).map((item) => {
|
||||
const isActive = pathname === item.href || (item.href !== "/dashboard" && pathname.startsWith(item.href))
|
||||
return (
|
||||
@@ -119,8 +119,8 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={cn(
|
||||
"flex flex-col items-center gap-0.5 py-1 px-2 rounded-lg transition-colors",
|
||||
isActive ? "text-trust-blue" : "text-muted-foreground"
|
||||
"flex flex-col items-center gap-0.5 py-1 px-2 transition-colors",
|
||||
isActive ? "text-promise-blue" : "text-gray-400"
|
||||
)}
|
||||
>
|
||||
<item.icon className="h-5 w-5" />
|
||||
|
||||
@@ -9,15 +9,15 @@ export default function DashboardLoading() {
|
||||
</div>
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{[...Array(4)].map((_, i) => (
|
||||
<Skeleton key={i} className="h-24 rounded-2xl" />
|
||||
<Skeleton key={i} className="h-24 rounded-lg" />
|
||||
))}
|
||||
</div>
|
||||
<Skeleton className="h-16 rounded-2xl" />
|
||||
<Skeleton className="h-16 rounded-lg" />
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
<Skeleton className="h-64 rounded-2xl" />
|
||||
<Skeleton className="h-64 rounded-2xl" />
|
||||
<Skeleton className="h-64 rounded-lg" />
|
||||
<Skeleton className="h-64 rounded-lg" />
|
||||
</div>
|
||||
<Skeleton className="h-96 rounded-2xl" />
|
||||
<Skeleton className="h-96 rounded-lg" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -43,9 +43,9 @@ function RolePicker({ onSelect }: { onSelect: (role: string) => void }) {
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<button
|
||||
onClick={() => onSelect("charity")}
|
||||
className="rounded-2xl border-2 border-gray-100 hover:border-trust-blue bg-white p-5 text-center space-y-2 transition-all hover:shadow-md group"
|
||||
className="rounded-lg border-2 border-gray-100 hover:border-trust-blue bg-white p-5 text-center space-y-2 transition-all hover:shadow-md group"
|
||||
>
|
||||
<div className="mx-auto w-12 h-12 rounded-xl bg-trust-blue/10 flex items-center justify-center group-hover:scale-110 transition-transform">
|
||||
<div className="mx-auto w-12 h-12 rounded-lg bg-trust-blue/10 flex items-center justify-center group-hover:scale-110 transition-transform">
|
||||
<Building2 className="h-6 w-6 text-trust-blue" />
|
||||
</div>
|
||||
<p className="text-sm font-bold text-gray-900">Charity / Mosque</p>
|
||||
@@ -53,9 +53,9 @@ function RolePicker({ onSelect }: { onSelect: (role: string) => void }) {
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onSelect("fundraiser")}
|
||||
className="rounded-2xl border-2 border-gray-100 hover:border-warm-amber bg-white p-5 text-center space-y-2 transition-all hover:shadow-md group"
|
||||
className="rounded-lg border-2 border-gray-100 hover:border-warm-amber bg-white p-5 text-center space-y-2 transition-all hover:shadow-md group"
|
||||
>
|
||||
<div className="mx-auto w-12 h-12 rounded-xl bg-warm-amber/10 flex items-center justify-center group-hover:scale-110 transition-transform">
|
||||
<div className="mx-auto w-12 h-12 rounded-lg bg-warm-amber/10 flex items-center justify-center group-hover:scale-110 transition-transform">
|
||||
<Heart className="h-6 w-6 text-warm-amber" />
|
||||
</div>
|
||||
<p className="text-sm font-bold text-gray-900">Personal Fundraiser</p>
|
||||
@@ -85,14 +85,14 @@ function GettingStartedBanner({
|
||||
const isFirstTime = ob.completed === 0 && (!ob.orgType || ob.orgType === "charity")
|
||||
|
||||
return (
|
||||
<div className="rounded-2xl border border-trust-blue/20 bg-gradient-to-r from-trust-blue/5 via-white to-warm-amber/5 p-5 space-y-4 relative">
|
||||
<div className="rounded-lg border border-trust-blue/20 bg-paper p-5 space-y-4 relative">
|
||||
{/* Dismiss X */}
|
||||
<button onClick={onDismiss} className="absolute top-3 right-3 text-muted-foreground hover:text-foreground p-1">
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="h-10 w-10 rounded-xl bg-gradient-to-br from-trust-blue to-blue-600 flex items-center justify-center flex-shrink-0">
|
||||
<div className="h-10 w-10 rounded-lg bg-midnight flex items-center justify-center flex-shrink-0">
|
||||
<span className="text-white text-lg">🤲</span>
|
||||
</div>
|
||||
<div>
|
||||
@@ -100,7 +100,7 @@ function GettingStartedBanner({
|
||||
{isFirstTime ? "Welcome! What best describes you?" : `Getting started · ${ob.completed}/${ob.total}`}
|
||||
</h2>
|
||||
{!isFirstTime && (
|
||||
<Progress value={(ob.completed / ob.total) * 100} className="h-1.5 mt-1.5 w-32" indicatorClassName="bg-gradient-to-r from-trust-blue to-success-green" />
|
||||
<Progress value={(ob.completed / ob.total) * 100} className="h-1.5 mt-1.5 w-32" indicatorClassName="bg-promise-blue" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -113,7 +113,7 @@ function GettingStartedBanner({
|
||||
const isNext = !step.done && ob.steps.slice(0, i).every(s => s.done)
|
||||
return (
|
||||
<Link key={step.id} href={step.href}>
|
||||
<div className={`flex items-center gap-2.5 rounded-xl border px-3 py-2.5 transition-all ${
|
||||
<div className={`flex items-center gap-2.5 rounded-lg border px-3 py-2.5 transition-all ${
|
||||
step.done ? "bg-success-green/5 border-success-green/20" :
|
||||
isNext ? "bg-trust-blue/5 border-trust-blue/20 shadow-sm" :
|
||||
"bg-white border-gray-100"
|
||||
@@ -233,7 +233,7 @@ export default function DashboardPage() {
|
||||
<Card className={isEmpty ? "opacity-60" : ""}>
|
||||
<CardContent className="pt-5 pb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="rounded-xl bg-trust-blue/10 p-2.5"><Users className="h-5 w-5 text-trust-blue" /></div>
|
||||
<div className="rounded-lg bg-trust-blue/10 p-2.5"><Users className="h-5 w-5 text-trust-blue" /></div>
|
||||
<div>
|
||||
<p className="text-2xl font-black">{s.totalPledges}</p>
|
||||
<p className="text-xs text-muted-foreground">Total Pledges</p>
|
||||
@@ -244,7 +244,7 @@ export default function DashboardPage() {
|
||||
<Card className={isEmpty ? "opacity-60" : ""}>
|
||||
<CardContent className="pt-5 pb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="rounded-xl bg-warm-amber/10 p-2.5"><Banknote className="h-5 w-5 text-warm-amber" /></div>
|
||||
<div className="rounded-lg bg-warm-amber/10 p-2.5"><Banknote className="h-5 w-5 text-warm-amber" /></div>
|
||||
<div>
|
||||
<p className="text-2xl font-black">{formatPence(s.totalPledgedPence)}</p>
|
||||
<p className="text-xs text-muted-foreground">Total Pledged</p>
|
||||
@@ -255,7 +255,7 @@ export default function DashboardPage() {
|
||||
<Card className={isEmpty ? "opacity-60" : ""}>
|
||||
<CardContent className="pt-5 pb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="rounded-xl bg-success-green/10 p-2.5"><TrendingUp className="h-5 w-5 text-success-green" /></div>
|
||||
<div className="rounded-lg bg-success-green/10 p-2.5"><TrendingUp className="h-5 w-5 text-success-green" /></div>
|
||||
<div>
|
||||
<p className="text-2xl font-black">{formatPence(s.totalCollectedPence)}</p>
|
||||
<p className="text-xs text-muted-foreground">Collected ({s.collectionRate}%)</p>
|
||||
@@ -266,7 +266,7 @@ export default function DashboardPage() {
|
||||
<Card className={isEmpty ? "opacity-60" : s.overdueRate > 10 ? "border-danger-red/30" : ""}>
|
||||
<CardContent className="pt-5 pb-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="rounded-xl bg-danger-red/10 p-2.5"><AlertTriangle className="h-5 w-5 text-danger-red" /></div>
|
||||
<div className="rounded-lg bg-danger-red/10 p-2.5"><AlertTriangle className="h-5 w-5 text-danger-red" /></div>
|
||||
<div>
|
||||
<p className="text-2xl font-black">{byStatus.overdue || 0}</p>
|
||||
<p className="text-xs text-muted-foreground">Overdue</p>
|
||||
@@ -283,7 +283,7 @@ export default function DashboardPage() {
|
||||
<span className="text-sm font-medium">Pledged → Collected</span>
|
||||
<span className="text-sm font-bold text-muted-foreground">{s.collectionRate}%</span>
|
||||
</div>
|
||||
<Progress value={s.collectionRate} indicatorClassName="bg-gradient-to-r from-trust-blue to-success-green" />
|
||||
<Progress value={s.collectionRate} indicatorClassName="bg-promise-blue" />
|
||||
<div className="flex justify-between mt-2 text-xs text-muted-foreground">
|
||||
<span>{formatPence(s.totalCollectedPence)} collected</span>
|
||||
<span>{formatPence(s.totalPledgedPence - s.totalCollectedPence)} outstanding</span>
|
||||
|
||||
@@ -251,7 +251,7 @@ export default function PledgesPage() {
|
||||
|
||||
{/* Collection progress */}
|
||||
<div className="flex items-center gap-4">
|
||||
<Progress value={collectionRate} className="flex-1 h-2" indicatorClassName="bg-gradient-to-r from-trust-blue to-success-green" />
|
||||
<Progress value={collectionRate} className="flex-1 h-2" indicatorClassName="bg-promise-blue" />
|
||||
<span className="text-sm font-medium text-muted-foreground whitespace-nowrap">
|
||||
{formatPence(stats.totalCollected)} / {formatPence(stats.totalPledged)}
|
||||
</span>
|
||||
|
||||
@@ -130,7 +130,7 @@ export default function ReconcilePage() {
|
||||
</div>
|
||||
|
||||
{/* File upload */}
|
||||
<div className="border-2 border-dashed border-gray-200 rounded-2xl p-8 text-center hover:border-trust-blue/50 transition-colors">
|
||||
<div className="border-2 border-dashed border-gray-200 rounded-lg p-8 text-center hover:border-trust-blue/50 transition-colors">
|
||||
<Upload className="h-10 w-10 text-muted-foreground mx-auto mb-3" />
|
||||
<input
|
||||
type="file"
|
||||
|
||||
@@ -66,7 +66,7 @@ export default function SettingsPage() {
|
||||
<p className="text-sm text-muted-foreground mt-0.5">Configure your organisation's payment details and integrations</p>
|
||||
</div>
|
||||
|
||||
{error && <div className="rounded-xl bg-danger-red/10 border border-danger-red/20 p-3 text-sm text-danger-red">{error}</div>}
|
||||
{error && <div className="rounded-lg bg-danger-red/10 border border-danger-red/20 p-3 text-sm text-danger-red">{error}</div>}
|
||||
|
||||
{/* WhatsApp — MOST IMPORTANT, first */}
|
||||
<WhatsAppPanel />
|
||||
@@ -194,7 +194,7 @@ function WhatsAppPanel() {
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 rounded-full bg-[#25D366]/10 flex items-center justify-center">
|
||||
<div className="w-12 h-12 rounded-lg bg-[#25D366]/10 flex items-center justify-center">
|
||||
<Smartphone className="h-6 w-6 text-[#25D366]" />
|
||||
</div>
|
||||
<div>
|
||||
@@ -240,7 +240,7 @@ function WhatsAppPanel() {
|
||||
<div className="relative">
|
||||
{/* Crop to QR area: the screenshot shows full WhatsApp web page.
|
||||
QR code is roughly in center. We use overflow hidden + object positioning. */}
|
||||
<div className="w-72 h-72 rounded-xl border-2 border-[#25D366]/20 overflow-hidden bg-white">
|
||||
<div className="w-72 h-72 rounded-lg border-2 border-[#25D366]/20 overflow-hidden bg-white">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={qrImage}
|
||||
@@ -254,7 +254,7 @@ function WhatsAppPanel() {
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-72 h-72 rounded-xl border-2 border-dashed border-muted flex items-center justify-center">
|
||||
<div className="w-72 h-72 rounded-lg border-2 border-dashed border-muted flex items-center justify-center">
|
||||
<Loader2 className="h-8 w-8 text-muted-foreground animate-spin" />
|
||||
</div>
|
||||
)}
|
||||
@@ -285,9 +285,9 @@ function WhatsAppPanel() {
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="rounded-xl bg-muted/50 p-4 space-y-3">
|
||||
<div className="rounded-lg bg-muted/50 p-4 space-y-3">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-10 h-10 rounded-full bg-[#25D366]/10 flex items-center justify-center flex-shrink-0">
|
||||
<div className="w-10 h-10 rounded-lg bg-[#25D366]/10 flex items-center justify-center flex-shrink-0">
|
||||
<Smartphone className="h-5 w-5 text-[#25D366]" />
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -125,7 +125,7 @@ export default function SetupPage() {
|
||||
<div className="flex items-center justify-between">
|
||||
{steps.map((s, i) => (
|
||||
<div key={s.num} className="flex items-center">
|
||||
<div className={`w-10 h-10 rounded-full flex items-center justify-center text-sm font-bold transition-all ${
|
||||
<div className={`w-10 h-10 rounded-lg flex items-center justify-center text-sm font-bold transition-all ${
|
||||
step >= s.num ? "bg-trust-blue text-white" : "bg-gray-100 text-gray-400"
|
||||
}`}>
|
||||
{step > s.num ? <CheckCircle2 className="h-5 w-5" /> : <s.icon className="h-5 w-5" />}
|
||||
@@ -231,14 +231,14 @@ export default function SetupPage() {
|
||||
{step === 4 && (
|
||||
<Card className="border-success-green/30">
|
||||
<CardHeader className="text-center pb-2">
|
||||
<div className="mx-auto w-16 h-16 rounded-full bg-success-green/10 flex items-center justify-center mb-3">
|
||||
<div className="mx-auto w-14 h-14 bg-success-green/10 flex items-center justify-center mb-3">
|
||||
<Sparkles className="h-8 w-8 text-success-green" />
|
||||
</div>
|
||||
<CardTitle>You're All Set! 🎉</CardTitle>
|
||||
<p className="text-sm text-muted-foreground">Your charity is ready to collect pledges.</p>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="bg-gray-50 rounded-xl p-4 space-y-2">
|
||||
<div className="bg-gray-50 rounded-lg p-4 space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-muted-foreground">Charity</span>
|
||||
<span className="text-sm font-medium">{orgName}</span>
|
||||
@@ -254,7 +254,7 @@ export default function SetupPage() {
|
||||
</div>
|
||||
|
||||
{setupResult?.qrToken && (
|
||||
<div className="bg-trust-blue/5 rounded-xl p-4 text-center space-y-2">
|
||||
<div className="bg-trust-blue/5 rounded-lg p-4 text-center space-y-2">
|
||||
<QrCode className="h-8 w-8 text-trust-blue mx-auto" />
|
||||
<p className="text-sm font-medium">Your pledge link is ready</p>
|
||||
<code className="text-xs bg-white px-3 py-1.5 rounded-lg border block overflow-x-auto">
|
||||
@@ -278,7 +278,7 @@ export default function SetupPage() {
|
||||
|
||||
{/* Tips */}
|
||||
{step < 4 && (
|
||||
<div className="bg-warm-amber/5 rounded-xl border border-warm-amber/20 p-4">
|
||||
<div className="bg-warm-amber/5 rounded-lg border border-warm-amber/20 p-4">
|
||||
<p className="text-xs font-semibold text-warm-amber mb-1">💡 Tip</p>
|
||||
{step === 1 && <p className="text-xs text-muted-foreground">Your charity name appears on the donor pledge page and WhatsApp receipts.</p>}
|
||||
{step === 2 && <p className="text-xs text-muted-foreground">Bank details are shown to donors who choose "Bank Transfer". Each pledge gets a unique reference number for easy reconciliation.</p>}
|
||||
|
||||
@@ -82,7 +82,7 @@ export default function PublicEventPage() {
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5">
|
||||
<div className="min-h-screen flex items-center justify-center bg-paper">
|
||||
<Loader2 className="h-8 w-8 text-trust-blue animate-spin" />
|
||||
</div>
|
||||
)
|
||||
@@ -90,7 +90,7 @@ export default function PublicEventPage() {
|
||||
|
||||
if (error || !data) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5 p-4">
|
||||
<div className="min-h-screen flex items-center justify-center bg-paper p-4">
|
||||
<div className="text-center space-y-4">
|
||||
<Heart className="h-12 w-12 text-muted-foreground mx-auto" />
|
||||
<h1 className="text-xl font-bold">Event not found</h1>
|
||||
@@ -106,7 +106,7 @@ export default function PublicEventPage() {
|
||||
const giftAidBonus = Math.round(data.stats.totalPledged * 0.25 * (data.stats.giftAidCount / Math.max(1, data.stats.pledgeCount)))
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5">
|
||||
<div className="min-h-screen bg-paper">
|
||||
<div className="max-w-lg mx-auto px-4 py-8 space-y-6">
|
||||
{/* Header */}
|
||||
<div className="text-center space-y-3">
|
||||
@@ -146,7 +146,7 @@ export default function PublicEventPage() {
|
||||
</div>
|
||||
<div className="h-4 rounded-full bg-gray-100 overflow-hidden">
|
||||
<div
|
||||
className="h-full rounded-full bg-gradient-to-r from-trust-blue to-success-green transition-all duration-1000"
|
||||
className="h-full rounded-full bg-promise-blue transition-all duration-1000"
|
||||
style={{ width: `${progressPercent}%` }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -7,25 +7,25 @@
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--foreground: 222 47% 11%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--card-foreground: 222 47% 11%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--primary: 221.2 83.2% 53.3%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 221.2 83.2% 53.3%;
|
||||
--radius: 0.75rem;
|
||||
--popover-foreground: 222 47% 11%;
|
||||
--primary: 224 76% 40%;
|
||||
--primary-foreground: 0 0% 100%;
|
||||
--secondary: 220 14% 96%;
|
||||
--secondary-foreground: 222 47% 11%;
|
||||
--muted: 220 14% 96%;
|
||||
--muted-foreground: 215 16% 47%;
|
||||
--accent: 220 14% 96%;
|
||||
--accent-foreground: 222 47% 11%;
|
||||
--destructive: 0 72% 51%;
|
||||
--destructive-foreground: 0 0% 100%;
|
||||
--border: 220 13% 91%;
|
||||
--input: 220 13% 91%;
|
||||
--ring: 224 76% 40%;
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
* {
|
||||
@@ -45,9 +45,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Premium animations */
|
||||
/* Animations — subtle, purposeful */
|
||||
@keyframes fadeUp {
|
||||
from { opacity: 0; transform: translateY(12px); }
|
||||
from { opacity: 0; transform: translateY(8px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@@ -56,55 +56,32 @@
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes scaleIn {
|
||||
from { opacity: 0; transform: scale(0.9); }
|
||||
to { opacity: 1; transform: scale(1); }
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from { opacity: 0; transform: translateY(-8px); }
|
||||
from { opacity: 0; transform: translateY(-4px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes pulse-ring {
|
||||
0% { transform: scale(0.8); opacity: 1; }
|
||||
100% { transform: scale(2); opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% { background-position: -200% 0; }
|
||||
100% { background-position: 200% 0; }
|
||||
}
|
||||
|
||||
@keyframes confetti-fall {
|
||||
0% { transform: translateY(-10vh) rotate(0deg); opacity: 1; }
|
||||
100% { transform: translateY(100vh) rotate(720deg); opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes bounce-gentle {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-4px); }
|
||||
}
|
||||
|
||||
@keyframes counter-roll {
|
||||
from { transform: translateY(100%); opacity: 0; }
|
||||
to { transform: translateY(0); opacity: 1; }
|
||||
}
|
||||
|
||||
.animate-fade-up { animation: fadeUp 0.5s ease-out forwards; }
|
||||
.animate-fade-up { animation: fadeUp 0.4s ease-out forwards; }
|
||||
.animate-fade-in { animation: fadeIn 0.3s ease-out forwards; }
|
||||
.animate-scale-in { animation: scaleIn 0.3s ease-out forwards; }
|
||||
.animate-slide-down { animation: slideDown 0.3s ease-out forwards; }
|
||||
.animate-pulse-ring { animation: pulse-ring 1.5s ease-out infinite; }
|
||||
.animate-shimmer {
|
||||
background: linear-gradient(90deg, transparent 30%, rgba(255,255,255,0.4) 50%, transparent 70%);
|
||||
background-size: 200% 100%;
|
||||
animation: shimmer 2s infinite;
|
||||
}
|
||||
.animate-bounce-gentle { animation: bounce-gentle 2s ease-in-out infinite; }
|
||||
.animate-counter-roll { animation: counter-roll 0.4s ease-out forwards; }
|
||||
|
||||
/* Stagger children animations */
|
||||
/* Stagger children */
|
||||
.stagger-children > * { opacity: 0; animation: fadeUp 0.4s ease-out forwards; }
|
||||
.stagger-children > *:nth-child(1) { animation-delay: 0ms; }
|
||||
.stagger-children > *:nth-child(2) { animation-delay: 60ms; }
|
||||
@@ -112,34 +89,6 @@
|
||||
.stagger-children > *:nth-child(4) { animation-delay: 180ms; }
|
||||
.stagger-children > *:nth-child(5) { animation-delay: 240ms; }
|
||||
.stagger-children > *:nth-child(6) { animation-delay: 300ms; }
|
||||
.stagger-children > *:nth-child(7) { animation-delay: 360ms; }
|
||||
.stagger-children > *:nth-child(8) { animation-delay: 420ms; }
|
||||
|
||||
/* Glass effects */
|
||||
.glass {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.glass-dark {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
}
|
||||
|
||||
/* Premium card hover */
|
||||
.card-hover {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.card-hover:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 12px 40px -8px rgba(0,0,0,0.12);
|
||||
}
|
||||
.card-hover:active {
|
||||
transform: translateY(0) scale(0.98);
|
||||
}
|
||||
|
||||
/* Number input clean */
|
||||
input[type="number"]::-webkit-inner-spin-button,
|
||||
@@ -151,20 +100,10 @@ input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
/* Scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: hsl(var(--muted));
|
||||
border-radius: 2px;
|
||||
}
|
||||
/* Scrollbar — thin, unobtrusive */
|
||||
::-webkit-scrollbar { width: 4px; }
|
||||
::-webkit-scrollbar-track { background: transparent; }
|
||||
::-webkit-scrollbar-thumb { background: #d1d5db; border-radius: 2px; }
|
||||
|
||||
/* Selection */
|
||||
::selection {
|
||||
background: #1e40af20;
|
||||
color: #1e40af;
|
||||
}
|
||||
/* Selection — brand blue */
|
||||
::selection { background: #1e40af15; color: #1e40af; }
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
export default function PledgeLoading() {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5">
|
||||
<div className="min-h-screen flex items-center justify-center bg-paper">
|
||||
<div className="text-center space-y-4">
|
||||
<div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-trust-blue/10 animate-pulse">
|
||||
<div className="inline-flex items-center justify-center w-14 h-14 bg-trust-blue/10 animate-pulse">
|
||||
<div className="w-8 h-8 rounded-full bg-trust-blue/30" />
|
||||
</div>
|
||||
<p className="text-muted-foreground animate-pulse">Loading pledge page...</p>
|
||||
|
||||
@@ -189,8 +189,8 @@ export default function PledgePage() {
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col items-center justify-center bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5 gap-4">
|
||||
<div className="w-12 h-12 rounded-2xl bg-gradient-to-br from-trust-blue to-blue-600 flex items-center justify-center animate-pulse">
|
||||
<div className="min-h-screen flex flex-col items-center justify-center bg-paper gap-4">
|
||||
<div className="w-12 h-12 rounded-lg bg-midnight flex items-center justify-center animate-pulse">
|
||||
<span className="text-white text-xl">🤲</span>
|
||||
</div>
|
||||
<p className="text-trust-blue font-medium animate-pulse">Loading...</p>
|
||||
@@ -200,7 +200,7 @@ export default function PledgePage() {
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5 p-4">
|
||||
<div className="min-h-screen flex items-center justify-center bg-paper p-4">
|
||||
<div className="text-center space-y-4 animate-fade-up">
|
||||
<div className="text-5xl">😔</div>
|
||||
<h1 className="text-xl font-bold text-gray-900">Something went wrong</h1>
|
||||
@@ -260,11 +260,11 @@ export default function PledgePage() {
|
||||
const progressPercent = progressMap[step] || 10
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5">
|
||||
<div className="min-h-screen bg-paper">
|
||||
{/* Progress bar */}
|
||||
<div className="fixed top-0 left-0 right-0 h-1 bg-gray-100 z-50">
|
||||
<div
|
||||
className="h-full bg-gradient-to-r from-trust-blue to-success-green transition-all duration-700 ease-out"
|
||||
className="h-full bg-promise-blue transition-all duration-700 ease-out"
|
||||
style={{ width: `${progressPercent}%` }}
|
||||
/>
|
||||
</div>
|
||||
@@ -282,7 +282,7 @@ export default function PledgePage() {
|
||||
|
||||
{/* Back button */}
|
||||
{backableSteps.has(step) && (
|
||||
<div className="fixed bottom-0 left-0 right-0 pb-6 pt-4 px-4 bg-gradient-to-t from-white via-white/80 to-transparent">
|
||||
<div className="fixed bottom-0 left-0 right-0 pb-6 pt-4 px-4 bg-white">
|
||||
<button
|
||||
onClick={() => setStep(getBackStep(step))}
|
||||
className="text-sm text-muted-foreground hover:text-foreground transition-colors tap-target flex items-center gap-1"
|
||||
|
||||
@@ -64,7 +64,7 @@ export function AmountStep({ onSelect, eventName, eventId }: Props) {
|
||||
<div className="max-w-md mx-auto pt-2 space-y-6 animate-fade-up">
|
||||
{/* Hero */}
|
||||
<div className="text-center space-y-3">
|
||||
<div className="inline-flex items-center justify-center w-14 h-14 rounded-2xl bg-gradient-to-br from-trust-blue to-blue-600 shadow-lg shadow-trust-blue/30">
|
||||
<div className="inline-flex items-center justify-center w-14 h-14 rounded-lg bg-midnight">
|
||||
<Heart className="h-7 w-7 text-white" />
|
||||
</div>
|
||||
<h1 className="text-3xl font-black text-gray-900 tracking-tight">
|
||||
@@ -80,7 +80,7 @@ export function AmountStep({ onSelect, eventName, eventId }: Props) {
|
||||
<div className="flex items-center justify-center gap-2 animate-fade-in">
|
||||
<div className="flex -space-x-2">
|
||||
{[...Array(3)].map((_, i) => (
|
||||
<div key={i} className="w-6 h-6 rounded-full bg-gradient-to-br from-trust-blue to-blue-400 border-2 border-white flex items-center justify-center">
|
||||
<div key={i} className="w-6 h-6 rounded-full bg-promise-blue border-2 border-white flex items-center justify-center">
|
||||
<span className="text-[8px] text-white font-bold">{["A", "S", "M"][i]}</span>
|
||||
</div>
|
||||
))}
|
||||
@@ -106,9 +106,9 @@ export function AmountStep({ onSelect, eventName, eventId }: Props) {
|
||||
onMouseEnter={() => setHovering(amount)}
|
||||
onMouseLeave={() => setHovering(null)}
|
||||
className={`
|
||||
relative tap-target rounded-2xl border-2 py-4 text-center font-bold transition-all duration-200
|
||||
relative tap-target rounded-lg border-2 py-4 text-center font-bold transition-all duration-200
|
||||
${isSelected
|
||||
? "border-trust-blue bg-trust-blue text-white shadow-xl shadow-trust-blue/30 scale-[1.03]"
|
||||
? "border-trust-blue bg-trust-blue text-white"
|
||||
: isHovering
|
||||
? "border-trust-blue/40 bg-trust-blue/5 text-gray-900"
|
||||
: "border-gray-200 bg-white text-gray-900 hover:border-trust-blue/30"
|
||||
@@ -118,7 +118,7 @@ export function AmountStep({ onSelect, eventName, eventId }: Props) {
|
||||
<span className="text-xl">£{pounds >= 1000 ? `${pounds / 1000}k` : pounds}</span>
|
||||
{/* Gift Aid indicator */}
|
||||
{isSelected && (
|
||||
<div className="absolute -top-2 -right-2 bg-success-green text-white text-[10px] font-bold px-1.5 py-0.5 rounded-full animate-scale-in shadow-sm">
|
||||
<div className="absolute -top-2 -right-2 bg-success-green text-white text-[10px] font-bold px-1.5 py-0.5 rounded-full animate-fade-in shadow-sm">
|
||||
+£{Math.round(amount * 0.25 / 100)}
|
||||
</div>
|
||||
)}
|
||||
@@ -152,14 +152,14 @@ export function AmountStep({ onSelect, eventName, eventId }: Props) {
|
||||
placeholder="0"
|
||||
value={custom}
|
||||
onChange={(e) => handleCustomChange(e.target.value)}
|
||||
className="w-full pl-10 pr-4 h-16 text-2xl font-black text-center rounded-2xl border-2 border-gray-200 bg-white focus:border-trust-blue focus:ring-4 focus:ring-trust-blue/10 outline-none transition-all"
|
||||
className="w-full pl-10 pr-4 h-16 text-2xl font-black text-center rounded-lg border-2 border-gray-200 bg-white focus:border-trust-blue focus:ring-4 focus:ring-trust-blue/10 outline-none transition-all"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Live Gift Aid preview */}
|
||||
{isValid && (
|
||||
<div className="rounded-2xl bg-gradient-to-r from-success-green/5 to-success-green/10 border border-success-green/20 p-4 text-center animate-scale-in">
|
||||
<div className="rounded-lg bg-fulfilled-green/5 border border-success-green/20 p-4 text-center animate-fade-in">
|
||||
<p className="text-sm">
|
||||
<span className="font-bold text-success-green">With Gift Aid:</span>{" "}
|
||||
your £{(activeAmount / 100).toFixed(0)} becomes{" "}
|
||||
|
||||
@@ -77,8 +77,8 @@ export function BankInstructionsStep({ pledge, amount, eventName, donorPhone }:
|
||||
return (
|
||||
<div className="max-w-md mx-auto pt-8 text-center space-y-6 animate-fade-up">
|
||||
<div className="relative inline-flex items-center justify-center">
|
||||
<div className="absolute w-20 h-20 rounded-full bg-success-green/20 animate-pulse-ring" />
|
||||
<div className="relative w-20 h-20 rounded-full bg-gradient-to-br from-success-green to-emerald-500 flex items-center justify-center shadow-xl shadow-success-green/30">
|
||||
<div className="absolute w-16 h-16 bg-success-green/20" />
|
||||
<div className="relative w-16 h-16 bg-fulfilled-green flex items-center justify-center">
|
||||
<Check className="h-10 w-10 text-white" strokeWidth={3} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -88,7 +88,7 @@ export function BankInstructionsStep({ pledge, amount, eventName, donorPhone }:
|
||||
</p>
|
||||
|
||||
{whatsappSent && (
|
||||
<div className="rounded-xl bg-[#25D366]/10 border border-[#25D366]/20 p-3 text-sm text-[#25D366] font-medium flex items-center justify-center gap-2 animate-fade-in">
|
||||
<div className="rounded-lg bg-[#25D366]/10 border border-[#25D366]/20 p-3 text-sm text-[#25D366] font-medium flex items-center justify-center gap-2 animate-fade-in">
|
||||
<MessageCircle className="h-4 w-4" /> Details sent to your WhatsApp ✓
|
||||
</div>
|
||||
)}
|
||||
@@ -107,7 +107,7 @@ export function BankInstructionsStep({ pledge, amount, eventName, donorPhone }:
|
||||
</Card>
|
||||
|
||||
{/* Share */}
|
||||
<div className="rounded-2xl bg-gradient-to-br from-warm-amber/5 to-orange-50 border border-warm-amber/20 p-5 space-y-3">
|
||||
<div className="rounded-lg bg-generosity-gold/5 border border-warm-amber/20 p-5 space-y-3">
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Sparkles className="h-4 w-4 text-warm-amber" />
|
||||
<p className="text-sm font-bold text-gray-900">Know someone who'd donate too?</p>
|
||||
@@ -164,7 +164,7 @@ export function BankInstructionsStep({ pledge, amount, eventName, donorPhone }:
|
||||
return (
|
||||
<div className="max-w-md mx-auto pt-2 space-y-5 animate-fade-up">
|
||||
<div className="text-center space-y-2">
|
||||
<div className="inline-flex items-center justify-center w-14 h-14 rounded-2xl bg-gradient-to-br from-trust-blue to-blue-600 shadow-lg shadow-trust-blue/30">
|
||||
<div className="inline-flex items-center justify-center w-14 h-14 rounded-lg bg-midnight">
|
||||
<span className="text-2xl">🏦</span>
|
||||
</div>
|
||||
<h1 className="text-2xl font-black text-gray-900 tracking-tight">
|
||||
@@ -176,7 +176,7 @@ export function BankInstructionsStep({ pledge, amount, eventName, donorPhone }:
|
||||
</div>
|
||||
|
||||
{whatsappSent && (
|
||||
<div className="rounded-xl bg-[#25D366]/10 border border-[#25D366]/20 p-2.5 text-xs text-[#25D366] font-medium flex items-center justify-center gap-2 animate-fade-in">
|
||||
<div className="rounded-lg bg-[#25D366]/10 border border-[#25D366]/20 p-2.5 text-xs text-[#25D366] font-medium flex items-center justify-center gap-2 animate-fade-in">
|
||||
<MessageCircle className="h-3.5 w-3.5" /> Bank details also sent to your WhatsApp
|
||||
</div>
|
||||
)}
|
||||
@@ -184,7 +184,7 @@ export function BankInstructionsStep({ pledge, amount, eventName, donorPhone }:
|
||||
{/* Bank details — tap to copy each field */}
|
||||
{bd && (
|
||||
<Card className="overflow-hidden">
|
||||
<div className="h-1 bg-gradient-to-r from-trust-blue to-blue-400" />
|
||||
<div className="h-1 bg-promise-blue" />
|
||||
<CardContent className="pt-4 divide-y">
|
||||
<CopyField label="Sort Code" value={bd.sortCode} fieldKey="sortCode" mono />
|
||||
<CopyField label="Account Number" value={bd.accountNo} fieldKey="accountNo" mono />
|
||||
@@ -194,7 +194,7 @@ export function BankInstructionsStep({ pledge, amount, eventName, donorPhone }:
|
||||
)}
|
||||
|
||||
{/* THE reference — the most important thing */}
|
||||
<div className="rounded-2xl border-2 border-trust-blue bg-gradient-to-br from-trust-blue/5 to-blue-50 p-5 text-center space-y-3">
|
||||
<div className="rounded-lg border-2 border-trust-blue bg-promise-blue/5 p-5 text-center space-y-3">
|
||||
<p className="text-xs font-bold text-trust-blue uppercase tracking-widest">
|
||||
Payment Reference
|
||||
</p>
|
||||
@@ -202,7 +202,7 @@ export function BankInstructionsStep({ pledge, amount, eventName, donorPhone }:
|
||||
onClick={() => copyField(pledge.reference, "reference")}
|
||||
className="group"
|
||||
>
|
||||
<p className="text-3xl font-mono font-black text-trust-blue tracking-wider group-hover:scale-105 transition-transform">
|
||||
<p className="text-3xl font-mono font-black text-trust-blue tracking-wider transition-transform">
|
||||
{pledge.reference}
|
||||
</p>
|
||||
</button>
|
||||
@@ -223,7 +223,7 @@ export function BankInstructionsStep({ pledge, amount, eventName, donorPhone }:
|
||||
</div>
|
||||
|
||||
{/* Open banking app hint */}
|
||||
<div className="rounded-xl bg-warm-amber/5 border border-warm-amber/20 p-3 text-center">
|
||||
<div className="rounded-lg bg-warm-amber/5 border border-warm-amber/20 p-3 text-center">
|
||||
<p className="text-xs font-medium text-warm-amber flex items-center justify-center gap-1.5">
|
||||
<ExternalLink className="h-3.5 w-3.5" />
|
||||
Open your banking app → New payment → Paste the details
|
||||
|
||||
@@ -171,7 +171,7 @@ export function CardPaymentStep({ amount, eventName, eventId, qrSourceId, onComp
|
||||
</div>
|
||||
|
||||
{/* Card form */}
|
||||
<div className="rounded-2xl border-2 border-gray-200 bg-white p-5 space-y-4">
|
||||
<div className="rounded-lg border-2 border-gray-200 bg-white p-5 space-y-4">
|
||||
{/* Card number */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="card-number">Card Number</Label>
|
||||
@@ -261,7 +261,7 @@ export function CardPaymentStep({ amount, eventName, eventId, qrSourceId, onComp
|
||||
</div>
|
||||
|
||||
{/* Gift Aid */}
|
||||
<label className="flex items-start gap-3 rounded-2xl border-2 border-gray-200 bg-white p-4 cursor-pointer hover:border-trust-blue/50 transition-colors">
|
||||
<label className="flex items-start gap-3 rounded-lg border-2 border-gray-200 bg-white p-4 cursor-pointer hover:border-trust-blue/50 transition-colors">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={giftAid}
|
||||
|
||||
@@ -130,8 +130,8 @@ export function ConfirmationStep({ pledge, amount, rail, eventName, shareUrl, do
|
||||
<div className="max-w-md mx-auto pt-6 text-center space-y-6 animate-fade-up">
|
||||
{/* Success icon with pulse */}
|
||||
<div className="relative inline-flex items-center justify-center">
|
||||
<div className="absolute w-20 h-20 rounded-full bg-success-green/20 animate-pulse-ring" />
|
||||
<div className="relative w-20 h-20 rounded-full bg-gradient-to-br from-success-green to-emerald-500 flex items-center justify-center shadow-xl shadow-success-green/30">
|
||||
<div className="absolute w-16 h-16 bg-success-green/20" />
|
||||
<div className="relative w-16 h-16 bg-fulfilled-green flex items-center justify-center">
|
||||
<Check className="h-10 w-10 text-white" strokeWidth={3} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -150,7 +150,7 @@ export function ConfirmationStep({ pledge, amount, rail, eventName, shareUrl, do
|
||||
|
||||
{/* Details card */}
|
||||
<Card className="text-left overflow-hidden">
|
||||
<div className="h-1 bg-gradient-to-r from-trust-blue via-success-green to-warm-amber" />
|
||||
<div className="h-1 bg-promise-blue" />
|
||||
<CardContent className="pt-5 space-y-3 text-sm">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-muted-foreground">Amount</span>
|
||||
@@ -190,19 +190,19 @@ export function ConfirmationStep({ pledge, amount, rail, eventName, shareUrl, do
|
||||
</Card>
|
||||
|
||||
{/* What happens next */}
|
||||
<div className="rounded-2xl bg-trust-blue/5 border border-trust-blue/10 p-4 text-left">
|
||||
<div className="rounded-lg bg-trust-blue/5 border border-trust-blue/10 p-4 text-left">
|
||||
<p className="text-sm font-semibold text-trust-blue mb-1">What happens next?</p>
|
||||
<p className="text-sm text-muted-foreground">{nextStepMessages[rail] || nextStepMessages.bank}</p>
|
||||
</div>
|
||||
|
||||
{whatsappSent && (
|
||||
<div className="rounded-xl bg-[#25D366]/10 border border-[#25D366]/20 p-3 text-sm text-[#25D366] font-medium flex items-center justify-center gap-2 animate-fade-in">
|
||||
<div className="rounded-lg bg-[#25D366]/10 border border-[#25D366]/20 p-3 text-sm text-[#25D366] font-medium flex items-center justify-center gap-2 animate-fade-in">
|
||||
<MessageCircle className="h-4 w-4" /> Receipt sent to your WhatsApp ✓
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Share */}
|
||||
<div className="rounded-2xl bg-gradient-to-br from-warm-amber/5 to-orange-50 border border-warm-amber/20 p-5 space-y-3">
|
||||
<div className="rounded-lg bg-generosity-gold/5 border border-warm-amber/20 p-5 space-y-3">
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Sparkles className="h-4 w-4 text-warm-amber" />
|
||||
<p className="text-sm font-bold text-gray-900">
|
||||
|
||||
@@ -118,7 +118,7 @@ export function DirectDebitStep({ amount, eventName, organizationName, eventId,
|
||||
if (phase === "processing") {
|
||||
return (
|
||||
<div className="max-w-md mx-auto pt-16 text-center space-y-6">
|
||||
<div className="inline-flex items-center justify-center w-20 h-20 rounded-full bg-trust-blue/10">
|
||||
<div className="inline-flex items-center justify-center w-16 h-16 bg-trust-blue/10">
|
||||
<div className="h-10 w-10 border-4 border-trust-blue border-t-transparent rounded-full animate-spin" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
@@ -141,7 +141,7 @@ export function DirectDebitStep({ amount, eventName, organizationName, eventId,
|
||||
return (
|
||||
<div className="max-w-md mx-auto pt-4 space-y-6">
|
||||
<div className="text-center space-y-2">
|
||||
<div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-trust-blue/10 mb-2">
|
||||
<div className="inline-flex items-center justify-center w-14 h-14 bg-trust-blue/10 mb-2">
|
||||
<ShieldCheck className="h-8 w-8 text-trust-blue" />
|
||||
</div>
|
||||
<h1 className="text-2xl font-extrabold text-gray-900">
|
||||
@@ -153,7 +153,7 @@ export function DirectDebitStep({ amount, eventName, organizationName, eventId,
|
||||
</div>
|
||||
|
||||
{/* Summary card */}
|
||||
<div className="rounded-2xl border-2 border-gray-200 bg-white p-5 space-y-4">
|
||||
<div className="rounded-lg border-2 border-gray-200 bg-white p-5 space-y-4">
|
||||
<div className="grid grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground uppercase tracking-wider">Account Holder</p>
|
||||
@@ -189,7 +189,7 @@ export function DirectDebitStep({ amount, eventName, organizationName, eventId,
|
||||
</div>
|
||||
|
||||
{/* Guarantee box */}
|
||||
<div className="rounded-2xl bg-emerald-50 border border-emerald-200 p-4 space-y-2">
|
||||
<div className="rounded-lg bg-emerald-50 border border-emerald-200 p-4 space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<ShieldCheck className="h-5 w-5 text-emerald-600" />
|
||||
<p className="text-sm font-semibold text-emerald-800">Direct Debit Guarantee</p>
|
||||
@@ -234,7 +234,7 @@ export function DirectDebitStep({ amount, eventName, organizationName, eventId,
|
||||
</div>
|
||||
|
||||
{/* Info banner */}
|
||||
<div className="rounded-2xl bg-trust-blue/5 border border-trust-blue/20 p-4 flex items-start gap-3">
|
||||
<div className="rounded-lg bg-trust-blue/5 border border-trust-blue/20 p-4 flex items-start gap-3">
|
||||
<Landmark className="h-5 w-5 text-trust-blue flex-shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-trust-blue">How it works</p>
|
||||
@@ -245,7 +245,7 @@ export function DirectDebitStep({ amount, eventName, organizationName, eventId,
|
||||
</div>
|
||||
|
||||
{/* Bank details form */}
|
||||
<div className="rounded-2xl border-2 border-gray-200 bg-white p-5 space-y-4">
|
||||
<div className="rounded-lg border-2 border-gray-200 bg-white p-5 space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="dd-name">Account Holder Name</Label>
|
||||
<Input
|
||||
@@ -306,7 +306,7 @@ export function DirectDebitStep({ amount, eventName, organizationName, eventId,
|
||||
</div>
|
||||
|
||||
{/* Gift Aid */}
|
||||
<label className="flex items-start gap-3 rounded-2xl border-2 border-gray-200 bg-white p-4 cursor-pointer hover:border-trust-blue/50 transition-colors">
|
||||
<label className="flex items-start gap-3 rounded-lg border-2 border-gray-200 bg-white p-4 cursor-pointer hover:border-trust-blue/50 transition-colors">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={giftAid}
|
||||
@@ -322,7 +322,7 @@ export function DirectDebitStep({ amount, eventName, organizationName, eventId,
|
||||
</label>
|
||||
|
||||
{/* Direct Debit mandate agreement */}
|
||||
<label className={`flex items-start gap-3 rounded-2xl border-2 p-4 cursor-pointer transition-colors ${
|
||||
<label className={`flex items-start gap-3 rounded-lg border-2 p-4 cursor-pointer transition-colors ${
|
||||
errors.mandate ? "border-red-300 bg-red-50" : "border-gray-200 bg-white hover:border-trust-blue/50"
|
||||
}`}>
|
||||
<input
|
||||
@@ -349,7 +349,7 @@ export function DirectDebitStep({ amount, eventName, organizationName, eventId,
|
||||
</Button>
|
||||
|
||||
{/* DD Guarantee mini */}
|
||||
<div className="rounded-xl bg-emerald-50 border border-emerald-200 p-3 flex items-start gap-2">
|
||||
<div className="rounded-lg bg-emerald-50 border border-emerald-200 p-3 flex items-start gap-2">
|
||||
<ShieldCheck className="h-4 w-4 text-emerald-600 flex-shrink-0 mt-0.5" />
|
||||
<p className="text-xs text-emerald-700">
|
||||
Protected by the <span className="font-semibold">Direct Debit Guarantee</span>. You can cancel anytime by contacting your bank. Full refund if any errors occur.
|
||||
|
||||
@@ -60,8 +60,8 @@ export function ExternalRedirectStep({ pledge, amount, eventName, externalUrl, e
|
||||
return (
|
||||
<div className="max-w-md mx-auto pt-8 text-center space-y-6 animate-fade-up">
|
||||
<div className="relative inline-flex items-center justify-center">
|
||||
<div className="absolute w-20 h-20 rounded-full bg-success-green/20 animate-pulse-ring" />
|
||||
<div className="relative w-20 h-20 rounded-full bg-gradient-to-br from-success-green to-emerald-500 flex items-center justify-center shadow-xl shadow-success-green/30">
|
||||
<div className="absolute w-16 h-16 bg-success-green/20" />
|
||||
<div className="relative w-16 h-16 bg-fulfilled-green flex items-center justify-center">
|
||||
<Check className="h-10 w-10 text-white" strokeWidth={3} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -72,7 +72,7 @@ export function ExternalRedirectStep({ pledge, amount, eventName, externalUrl, e
|
||||
</p>
|
||||
|
||||
{whatsappSent && (
|
||||
<div className="rounded-xl bg-[#25D366]/10 border border-[#25D366]/20 p-3 text-sm text-[#25D366] font-medium flex items-center justify-center gap-2 animate-fade-in">
|
||||
<div className="rounded-lg bg-[#25D366]/10 border border-[#25D366]/20 p-3 text-sm text-[#25D366] font-medium flex items-center justify-center gap-2 animate-fade-in">
|
||||
<MessageCircle className="h-4 w-4" /> Link sent to your WhatsApp ✓
|
||||
</div>
|
||||
)}
|
||||
@@ -107,7 +107,7 @@ export function ExternalRedirectStep({ pledge, amount, eventName, externalUrl, e
|
||||
</div>
|
||||
|
||||
{/* Share */}
|
||||
<div className="rounded-2xl bg-gradient-to-br from-warm-amber/5 to-orange-50 border border-warm-amber/20 p-5 space-y-3">
|
||||
<div className="rounded-lg bg-generosity-gold/5 border border-warm-amber/20 p-5 space-y-3">
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Sparkles className="h-4 w-4 text-warm-amber" />
|
||||
<p className="text-sm font-bold text-gray-900">Know someone who'd donate too?</p>
|
||||
@@ -144,7 +144,7 @@ export function ExternalRedirectStep({ pledge, amount, eventName, externalUrl, e
|
||||
return (
|
||||
<div className="max-w-md mx-auto pt-2 space-y-5 animate-fade-up">
|
||||
<div className="text-center space-y-2">
|
||||
<div className="inline-flex items-center justify-center w-14 h-14 rounded-2xl shadow-lg" style={{ background: platform.color + "20" }}>
|
||||
<div className="inline-flex items-center justify-center w-14 h-14 rounded-lg shadow-lg" style={{ background: platform.color + "20" }}>
|
||||
<span className="text-2xl">{platform.icon}</span>
|
||||
</div>
|
||||
<h1 className="text-2xl font-black text-gray-900 tracking-tight">
|
||||
@@ -156,7 +156,7 @@ export function ExternalRedirectStep({ pledge, amount, eventName, externalUrl, e
|
||||
</div>
|
||||
|
||||
{whatsappSent && (
|
||||
<div className="rounded-xl bg-[#25D366]/10 border border-[#25D366]/20 p-2.5 text-xs text-[#25D366] font-medium flex items-center justify-center gap-2 animate-fade-in">
|
||||
<div className="rounded-lg bg-[#25D366]/10 border border-[#25D366]/20 p-2.5 text-xs text-[#25D366] font-medium flex items-center justify-center gap-2 animate-fade-in">
|
||||
<MessageCircle className="h-3.5 w-3.5" /> Donation link also sent to your WhatsApp
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -156,10 +156,10 @@ export function IdentityStep({ onSubmit, amount, zakatEligible, orgName }: Props
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
autoComplete="name"
|
||||
className="w-full h-14 px-4 rounded-2xl border-2 border-gray-200 bg-white text-base font-medium placeholder:text-gray-300 focus:border-trust-blue focus:ring-4 focus:ring-trust-blue/10 outline-none transition-all"
|
||||
className="w-full h-14 px-4 rounded-lg border-2 border-gray-200 bg-white text-base font-medium placeholder:text-gray-300 focus:border-trust-blue focus:ring-4 focus:ring-trust-blue/10 outline-none transition-all"
|
||||
/>
|
||||
{name && (
|
||||
<div className="absolute right-3 top-1/2 -translate-y-1/2 text-success-green animate-scale-in">✓</div>
|
||||
<div className="absolute right-3 top-1/2 -translate-y-1/2 text-success-green animate-fade-in">✓</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -173,10 +173,10 @@ export function IdentityStep({ onSubmit, amount, zakatEligible, orgName }: Props
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
autoComplete="email"
|
||||
inputMode="email"
|
||||
className="w-full h-14 pl-12 pr-4 rounded-2xl border-2 border-gray-200 bg-white text-base font-medium placeholder:text-gray-300 focus:border-trust-blue focus:ring-4 focus:ring-trust-blue/10 outline-none transition-all"
|
||||
className="w-full h-14 pl-12 pr-4 rounded-lg border-2 border-gray-200 bg-white text-base font-medium placeholder:text-gray-300 focus:border-trust-blue focus:ring-4 focus:ring-trust-blue/10 outline-none transition-all"
|
||||
/>
|
||||
{hasEmail && (
|
||||
<div className="absolute right-3 top-1/2 -translate-y-1/2 text-success-green animate-scale-in">✓</div>
|
||||
<div className="absolute right-3 top-1/2 -translate-y-1/2 text-success-green animate-fade-in">✓</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -191,10 +191,10 @@ export function IdentityStep({ onSubmit, amount, zakatEligible, orgName }: Props
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
autoComplete="tel"
|
||||
inputMode="tel"
|
||||
className="w-full h-14 pl-12 pr-4 rounded-2xl border-2 border-gray-200 bg-white text-base font-medium placeholder:text-gray-300 focus:border-trust-blue focus:ring-4 focus:ring-trust-blue/10 outline-none transition-all"
|
||||
className="w-full h-14 pl-12 pr-4 rounded-lg border-2 border-gray-200 bg-white text-base font-medium placeholder:text-gray-300 focus:border-trust-blue focus:ring-4 focus:ring-trust-blue/10 outline-none transition-all"
|
||||
/>
|
||||
{hasPhone && (
|
||||
<div className="absolute right-3 top-1/2 -translate-y-1/2 text-success-green animate-scale-in">✓</div>
|
||||
<div className="absolute right-3 top-1/2 -translate-y-1/2 text-success-green animate-fade-in">✓</div>
|
||||
)}
|
||||
</div>
|
||||
{!hasEmail && !hasPhone && (
|
||||
@@ -220,14 +220,14 @@ export function IdentityStep({ onSubmit, amount, zakatEligible, orgName }: Props
|
||||
<div className="space-y-3">
|
||||
<button
|
||||
onClick={() => setGiftAid(!giftAid)}
|
||||
className={`w-full text-left rounded-2xl border-2 p-5 transition-all duration-300 ${
|
||||
className={`w-full text-left rounded-lg border-2 p-5 transition-all duration-300 ${
|
||||
giftAid
|
||||
? "border-success-green bg-gradient-to-br from-success-green/5 to-emerald-50 shadow-lg shadow-success-green/10"
|
||||
? "border-success-green bg-fulfilled-green/5"
|
||||
: "border-gray-200 bg-white hover:border-success-green/40"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className={`rounded-xl p-3 transition-all ${giftAid ? "bg-success-green shadow-lg shadow-success-green/30" : "bg-gray-100"}`}>
|
||||
<div className={`rounded-lg p-3 transition-all ${giftAid ? "bg-success-green" : "bg-gray-100"}`}>
|
||||
<Gift className={`h-6 w-6 transition-colors ${giftAid ? "text-white" : "text-gray-400"}`} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
@@ -236,7 +236,7 @@ export function IdentityStep({ onSubmit, amount, zakatEligible, orgName }: Props
|
||||
{giftAid ? "Gift Aid added!" : "Add Gift Aid"}
|
||||
</span>
|
||||
{giftAid ? (
|
||||
<span className="text-xs font-bold px-2.5 py-0.5 rounded-full bg-success-green text-white animate-scale-in flex items-center gap-1">
|
||||
<span className="text-xs font-bold px-2.5 py-0.5 rounded-full bg-success-green text-white animate-fade-in flex items-center gap-1">
|
||||
<Sparkles className="h-3 w-3" /> +£{(giftAidBonus / 100).toFixed(0)} free
|
||||
</span>
|
||||
) : (
|
||||
@@ -249,7 +249,7 @@ export function IdentityStep({ onSubmit, amount, zakatEligible, orgName }: Props
|
||||
{giftAid ? (
|
||||
<div className="mt-2 space-y-2 animate-fade-in">
|
||||
{/* Live math */}
|
||||
<div className="flex items-center justify-between bg-white rounded-xl p-3 border border-success-green/20">
|
||||
<div className="flex items-center justify-between bg-white rounded-lg p-3 border border-success-green/20">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground">Your pledge</p>
|
||||
<p className="font-bold">£{(amount / 100).toFixed(0)}</p>
|
||||
@@ -278,7 +278,7 @@ export function IdentityStep({ onSubmit, amount, zakatEligible, orgName }: Props
|
||||
|
||||
{/* Gift Aid address + declaration (shown when Gift Aid is on) */}
|
||||
{giftAid && showGiftAidAddress && (
|
||||
<div className="rounded-2xl border-2 border-success-green/20 bg-white p-4 space-y-3 animate-fade-in">
|
||||
<div className="rounded-lg border-2 border-success-green/20 bg-white p-4 space-y-3 animate-fade-in">
|
||||
{/* HMRC requires home address */}
|
||||
<div className="flex items-center gap-2 text-xs font-medium text-muted-foreground">
|
||||
<MapPin className="h-3.5 w-3.5" />
|
||||
@@ -292,10 +292,10 @@ export function IdentityStep({ onSubmit, amount, zakatEligible, orgName }: Props
|
||||
value={addressLine1}
|
||||
onChange={(e) => setAddressLine1(e.target.value)}
|
||||
autoComplete="address-line1"
|
||||
className="w-full h-12 px-4 rounded-xl border-2 border-gray-200 bg-white text-sm font-medium placeholder:text-gray-300 focus:border-success-green focus:ring-4 focus:ring-success-green/10 outline-none transition-all"
|
||||
className="w-full h-12 px-4 rounded-lg border-2 border-gray-200 bg-white text-sm font-medium placeholder:text-gray-300 focus:border-success-green focus:ring-4 focus:ring-success-green/10 outline-none transition-all"
|
||||
/>
|
||||
{addressLine1 && (
|
||||
<div className="absolute right-3 top-1/2 -translate-y-1/2 text-success-green text-sm animate-scale-in">✓</div>
|
||||
<div className="absolute right-3 top-1/2 -translate-y-1/2 text-success-green text-sm animate-fade-in">✓</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -306,15 +306,15 @@ export function IdentityStep({ onSubmit, amount, zakatEligible, orgName }: Props
|
||||
value={postcode}
|
||||
onChange={(e) => setPostcode(e.target.value.toUpperCase())}
|
||||
autoComplete="postal-code"
|
||||
className="w-full h-12 px-4 rounded-xl border-2 border-gray-200 bg-white text-sm font-medium placeholder:text-gray-300 focus:border-success-green focus:ring-4 focus:ring-success-green/10 outline-none transition-all uppercase"
|
||||
className="w-full h-12 px-4 rounded-lg border-2 border-gray-200 bg-white text-sm font-medium placeholder:text-gray-300 focus:border-success-green focus:ring-4 focus:ring-success-green/10 outline-none transition-all uppercase"
|
||||
/>
|
||||
{postcode.length >= 5 && (
|
||||
<div className="absolute right-3 top-1/2 -translate-y-1/2 text-success-green text-sm animate-scale-in">✓</div>
|
||||
<div className="absolute right-3 top-1/2 -translate-y-1/2 text-success-green text-sm animate-fade-in">✓</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* HMRC declaration — exact model wording */}
|
||||
<div className="rounded-xl bg-success-green/5 border border-success-green/10 p-3">
|
||||
<div className="rounded-lg bg-success-green/5 border border-success-green/10 p-3">
|
||||
<p className="text-xs font-bold text-gray-700 mb-1.5">Gift Aid Declaration</p>
|
||||
<p className="text-[11px] text-muted-foreground leading-relaxed">
|
||||
{GIFT_AID_DECLARATION}
|
||||
@@ -406,7 +406,7 @@ function ConsentCheckbox({ checked, onChange, icon, label, description }: {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onChange(!checked)}
|
||||
className={`w-full text-left rounded-2xl border-2 p-4 transition-all ${
|
||||
className={`w-full text-left rounded-lg border-2 p-4 transition-all ${
|
||||
checked
|
||||
? "border-trust-blue bg-trust-blue/5 shadow-sm"
|
||||
: "border-gray-200 bg-white hover:border-trust-blue/40"
|
||||
|
||||
@@ -22,7 +22,7 @@ export function PaymentStep({ onSelect, amount }: Props) {
|
||||
detail: "We'll give you the bank details. Transfer in your own time.",
|
||||
fee: "Free",
|
||||
feeClass: "text-success-green font-bold",
|
||||
iconBg: "from-emerald-500 to-green-600",
|
||||
iconBg: "bg-fulfilled-green",
|
||||
highlight: true,
|
||||
benefits: ["Zero fees", "Most charities prefer this"],
|
||||
},
|
||||
@@ -36,7 +36,7 @@ export function PaymentStep({ onSelect, amount }: Props) {
|
||||
detail: "GoCardless collects it for you. Protected by the DD Guarantee.",
|
||||
fee: "1% + 20p",
|
||||
feeClass: "text-muted-foreground",
|
||||
iconBg: "from-trust-blue to-blue-600",
|
||||
iconBg: "bg-promise-blue",
|
||||
highlight: false,
|
||||
benefits: ["No action needed", "DD Guarantee"],
|
||||
},
|
||||
@@ -50,7 +50,7 @@ export function PaymentStep({ onSelect, amount }: Props) {
|
||||
detail: "Powered by Stripe. Receipt emailed instantly.",
|
||||
fee: "1.4% + 20p",
|
||||
feeClass: "text-muted-foreground",
|
||||
iconBg: "from-purple-500 to-violet-600",
|
||||
iconBg: "bg-midnight",
|
||||
highlight: false,
|
||||
benefits: ["Instant confirmation", "All major cards"],
|
||||
},
|
||||
@@ -74,15 +74,15 @@ export function PaymentStep({ onSelect, amount }: Props) {
|
||||
key={opt.id}
|
||||
onClick={() => onSelect(opt.id)}
|
||||
className={`
|
||||
w-full text-left rounded-2xl border-2 bg-white p-5 transition-all duration-200 group card-hover
|
||||
w-full text-left rounded-lg border-2 bg-white p-5 transition-all duration-200 group
|
||||
${opt.highlight
|
||||
? "border-success-green/40 shadow-sm shadow-success-green/10 hover:border-success-green hover:shadow-lg hover:shadow-success-green/15"
|
||||
: "border-gray-200 hover:border-trust-blue/40 hover:shadow-lg hover:shadow-trust-blue/10"
|
||||
? "border-success-green/40 hover:border-success-green hover:border-fulfilled-green"
|
||||
: "border-gray-200 hover:border-trust-blue/40 hover:border-promise-blue"
|
||||
}
|
||||
`}
|
||||
>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className={`rounded-xl bg-gradient-to-br ${opt.iconBg} p-3 shadow-lg shadow-trust-blue/10 group-hover:scale-105 transition-transform`}>
|
||||
<div className={`rounded-lg ${opt.iconBg} p-3 transition-transform`}>
|
||||
<opt.icon className="h-5 w-5 text-white" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
|
||||
@@ -150,10 +150,10 @@ export function ScheduleStep({ amount, onSelect }: Props) {
|
||||
{/* Pay Now */}
|
||||
<button
|
||||
onClick={() => onSelect({ mode: "now" })}
|
||||
className="w-full text-left rounded-2xl border-2 border-gray-200 bg-white p-5 transition-all duration-200 card-hover hover:border-trust-blue/40 hover:shadow-lg group"
|
||||
className="w-full text-left rounded-lg border-2 border-gray-200 bg-white p-5 transition-all duration-200 hover:border-trust-blue/40 hover:shadow-lg group"
|
||||
>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="rounded-xl bg-gradient-to-br from-trust-blue to-blue-600 p-3 shadow-lg shadow-trust-blue/20 group-hover:scale-105 transition-transform">
|
||||
<div className="rounded-lg bg-midnight p-3 transition-transform">
|
||||
<Zap className="h-5 w-5 text-white" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
@@ -172,10 +172,10 @@ export function ScheduleStep({ amount, onSelect }: Props) {
|
||||
{/* Pay on a date */}
|
||||
<button
|
||||
onClick={() => setMode("calendar")}
|
||||
className="w-full text-left rounded-2xl border-2 border-success-green/30 bg-gradient-to-r from-success-green/5 to-white p-5 transition-all duration-200 card-hover hover:border-success-green hover:shadow-lg group"
|
||||
className="w-full text-left rounded-lg border-2 border-success-green/30 bg-fulfilled-green/5 p-5 transition-all duration-200 hover:border-success-green hover:shadow-lg group"
|
||||
>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="rounded-xl bg-gradient-to-br from-success-green to-emerald-600 p-3 shadow-lg shadow-success-green/20 group-hover:scale-105 transition-transform">
|
||||
<div className="rounded-lg bg-fulfilled-green p-3 transition-transform">
|
||||
<Calendar className="h-5 w-5 text-white" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
@@ -194,10 +194,10 @@ export function ScheduleStep({ amount, onSelect }: Props) {
|
||||
{/* Installments */}
|
||||
<button
|
||||
onClick={() => setMode("installments")}
|
||||
className="w-full text-left rounded-2xl border-2 border-gray-200 bg-white p-5 transition-all duration-200 card-hover hover:border-warm-amber/40 hover:shadow-lg group"
|
||||
className="w-full text-left rounded-lg border-2 border-gray-200 bg-white p-5 transition-all duration-200 hover:border-warm-amber/40 hover:shadow-lg group"
|
||||
>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="rounded-xl bg-gradient-to-br from-warm-amber to-orange-500 p-3 shadow-lg shadow-warm-amber/20 group-hover:scale-105 transition-transform">
|
||||
<div className="rounded-lg bg-generosity-gold p-3 transition-transform">
|
||||
<Repeat className="h-5 w-5 text-white" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
@@ -241,9 +241,9 @@ export function ScheduleStep({ amount, onSelect }: Props) {
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => setSelectedDate(s.date)}
|
||||
className={`shrink-0 rounded-xl border-2 px-3.5 py-2 text-left transition-all ${
|
||||
className={`shrink-0 rounded-lg border-2 px-3.5 py-2 text-left transition-all ${
|
||||
isSelected
|
||||
? "border-success-green bg-success-green/5 shadow-md shadow-success-green/10"
|
||||
? "border-success-green bg-success-green/5"
|
||||
: "border-gray-200 bg-white hover:border-success-green/40"
|
||||
}`}
|
||||
>
|
||||
@@ -283,7 +283,7 @@ export function ScheduleStep({ amount, onSelect }: Props) {
|
||||
disabled={day.isPast || !day.inMonth}
|
||||
className={`h-9 w-9 mx-auto rounded-lg text-xs font-medium transition-all ${
|
||||
isSelected
|
||||
? "bg-success-green text-white font-bold shadow-md shadow-success-green/30"
|
||||
? "bg-success-green text-white font-bold"
|
||||
: isToday
|
||||
? "bg-trust-blue/10 text-trust-blue font-bold"
|
||||
: day.isPast || !day.inMonth
|
||||
@@ -301,7 +301,7 @@ export function ScheduleStep({ amount, onSelect }: Props) {
|
||||
|
||||
{/* Selected date summary */}
|
||||
{selectedDate && (
|
||||
<div className="rounded-2xl bg-success-green/5 border border-success-green/20 p-4 text-center animate-scale-in">
|
||||
<div className="rounded-lg bg-success-green/5 border border-success-green/20 p-4 text-center animate-fade-in">
|
||||
<p className="text-xs text-muted-foreground">You'll pay</p>
|
||||
<p className="text-xl font-black text-gray-900">£{pounds}</p>
|
||||
<p className="text-sm font-medium text-success-green">{formatFull(selectedDate)}</p>
|
||||
@@ -313,7 +313,7 @@ export function ScheduleStep({ amount, onSelect }: Props) {
|
||||
|
||||
<Button
|
||||
size="xl"
|
||||
className={`w-full ${selectedDate ? "bg-success-green hover:bg-success-green/90 shadow-lg shadow-success-green/25" : ""}`}
|
||||
className={`w-full ${selectedDate ? "bg-success-green hover:bg-success-green/90" : ""}`}
|
||||
disabled={!selectedDate}
|
||||
onClick={handleDateConfirm}
|
||||
>
|
||||
@@ -345,9 +345,9 @@ export function ScheduleStep({ amount, onSelect }: Props) {
|
||||
<button
|
||||
key={n}
|
||||
onClick={() => setInstallmentCount(n)}
|
||||
className={`rounded-xl border-2 px-4 py-3 text-center transition-all min-w-[70px] ${
|
||||
className={`rounded-lg border-2 px-4 py-3 text-center transition-all min-w-[70px] ${
|
||||
installmentCount === n
|
||||
? "border-warm-amber bg-warm-amber text-white font-bold shadow-lg shadow-warm-amber/25"
|
||||
? "border-warm-amber bg-warm-amber text-white font-bold"
|
||||
: "border-gray-200 bg-white hover:border-warm-amber/40"
|
||||
}`}
|
||||
>
|
||||
@@ -359,7 +359,7 @@ export function ScheduleStep({ amount, onSelect }: Props) {
|
||||
|
||||
{/* Breakdown */}
|
||||
<Card className="overflow-hidden">
|
||||
<div className="h-1 bg-gradient-to-r from-warm-amber to-orange-400" />
|
||||
<div className="h-1 bg-generosity-gold" />
|
||||
<CardContent className="pt-4">
|
||||
<div className="text-center mb-4">
|
||||
<p className="text-xs text-muted-foreground">Monthly payment</p>
|
||||
@@ -392,7 +392,7 @@ export function ScheduleStep({ amount, onSelect }: Props) {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<div className="rounded-xl bg-warm-amber/5 border border-warm-amber/20 p-3 text-center">
|
||||
<div className="rounded-lg bg-warm-amber/5 border border-warm-amber/20 p-3 text-center">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
We'll send you a reminder with payment details before each installment date.
|
||||
You can pay via bank transfer, card, or Direct Debit.
|
||||
@@ -401,7 +401,7 @@ export function ScheduleStep({ amount, onSelect }: Props) {
|
||||
|
||||
<Button
|
||||
size="xl"
|
||||
className="w-full bg-warm-amber hover:bg-warm-amber/90 shadow-lg shadow-warm-amber/25"
|
||||
className="w-full bg-warm-amber hover:bg-warm-amber/90"
|
||||
onClick={handleInstallmentConfirm}
|
||||
>
|
||||
<Check className="h-5 w-5 mr-2" />
|
||||
|
||||
@@ -45,7 +45,7 @@ function SuccessContent() {
|
||||
if (cancelled) {
|
||||
return (
|
||||
<div className="max-w-md mx-auto text-center space-y-6">
|
||||
<div className="inline-flex items-center justify-center w-20 h-20 rounded-full bg-amber-100">
|
||||
<div className="inline-flex items-center justify-center w-16 h-16 bg-amber-100">
|
||||
<X className="h-10 w-10 text-amber-600" />
|
||||
</div>
|
||||
<h1 className="text-2xl font-extrabold text-gray-900">Payment Cancelled</h1>
|
||||
@@ -89,7 +89,7 @@ function SuccessContent() {
|
||||
|
||||
return (
|
||||
<div className="max-w-md mx-auto text-center space-y-6">
|
||||
<div className="inline-flex items-center justify-center w-20 h-20 rounded-full bg-success-green/10">
|
||||
<div className="inline-flex items-center justify-center w-16 h-16 bg-success-green/10">
|
||||
<Check className="h-10 w-10 text-success-green" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
@@ -125,7 +125,7 @@ function SuccessContent() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
<div className="rounded-2xl bg-trust-blue/5 border border-trust-blue/20 p-4 space-y-2">
|
||||
<div className="rounded-lg bg-trust-blue/5 border border-trust-blue/20 p-4 space-y-2">
|
||||
<p className="text-sm font-medium text-trust-blue">What happens next?</p>
|
||||
<p className="text-sm text-muted-foreground">{nextStepMessages[rail] || nextStepMessages.card}</p>
|
||||
</div>
|
||||
@@ -138,7 +138,7 @@ function SuccessContent() {
|
||||
|
||||
export default function SuccessPage() {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5 p-4">
|
||||
<div className="min-h-screen flex items-center justify-center bg-paper p-4">
|
||||
<Suspense fallback={
|
||||
<div className="text-center space-y-4">
|
||||
<Loader2 className="h-10 w-10 text-trust-blue animate-spin mx-auto" />
|
||||
|
||||
@@ -89,7 +89,7 @@ export default function VolunteerPage() {
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5">
|
||||
<div className="min-h-screen flex items-center justify-center bg-paper">
|
||||
<Loader2 className="h-8 w-8 text-trust-blue animate-spin" />
|
||||
</div>
|
||||
)
|
||||
@@ -97,7 +97,7 @@ export default function VolunteerPage() {
|
||||
|
||||
if (error || !data) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5 p-4">
|
||||
<div className="min-h-screen flex items-center justify-center bg-paper p-4">
|
||||
<div className="text-center space-y-4">
|
||||
<QrCode className="h-12 w-12 text-muted-foreground mx-auto" />
|
||||
<h1 className="text-xl font-bold">QR code not found</h1>
|
||||
@@ -108,7 +108,7 @@ export default function VolunteerPage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-trust-blue/5 via-white to-warm-amber/5">
|
||||
<div className="min-h-screen bg-paper">
|
||||
<div className="max-w-lg mx-auto px-4 py-8 space-y-6">
|
||||
{/* Header */}
|
||||
<div className="text-center space-y-2">
|
||||
@@ -153,7 +153,7 @@ export default function VolunteerPage() {
|
||||
</div>
|
||||
<div className="h-3 rounded-full bg-gray-100 overflow-hidden">
|
||||
<div
|
||||
className="h-full rounded-full bg-gradient-to-r from-trust-blue to-success-green transition-all duration-1000"
|
||||
className="h-full rounded-full bg-promise-blue transition-all duration-1000"
|
||||
style={{ width: `${Math.round((data.stats.totalPaidPence / data.stats.totalPledgedPence) * 100)}%` }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -57,7 +57,7 @@ export function LiveTicker({ eventId }: LiveTickerProps) {
|
||||
const totalToday = pledges.reduce((s, p) => s + p.amountPence, 0)
|
||||
|
||||
return (
|
||||
<div className="rounded-2xl border bg-white p-4 space-y-3">
|
||||
<div className="rounded-lg border bg-white p-4 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="font-bold text-sm flex items-center gap-2">
|
||||
<TrendingUp className="h-4 w-4 text-trust-blue" /> Live Feed
|
||||
|
||||
@@ -3,24 +3,25 @@ import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center whitespace-nowrap rounded-xl text-sm font-semibold ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 active:scale-[0.98]",
|
||||
"inline-flex items-center justify-center whitespace-nowrap text-sm font-semibold ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-trust-blue text-white hover:bg-trust-blue/90 shadow-lg shadow-trust-blue/25",
|
||||
destructive: "bg-danger-red text-white hover:bg-danger-red/90",
|
||||
outline: "border-2 border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
||||
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-trust-blue underline-offset-4 hover:underline",
|
||||
success: "bg-success-green text-white hover:bg-success-green/90 shadow-lg shadow-success-green/25",
|
||||
amber: "bg-warm-amber text-white hover:bg-warm-amber/90 shadow-lg shadow-warm-amber/25",
|
||||
default: "bg-midnight text-white hover:bg-gray-800",
|
||||
destructive: "bg-alert-red text-white hover:bg-alert-red/90",
|
||||
outline: "border border-gray-200 bg-white hover:bg-gray-50 text-midnight",
|
||||
secondary: "bg-gray-100 text-midnight hover:bg-gray-200",
|
||||
ghost: "hover:bg-gray-100 text-midnight",
|
||||
link: "text-promise-blue underline-offset-4 hover:underline",
|
||||
success: "bg-fulfilled-green text-white hover:bg-fulfilled-green/90",
|
||||
amber: "bg-generosity-gold text-white hover:bg-generosity-gold/90",
|
||||
blue: "bg-promise-blue text-white hover:bg-promise-blue/90",
|
||||
},
|
||||
size: {
|
||||
default: "h-11 px-6 py-2",
|
||||
sm: "h-9 rounded-lg px-4",
|
||||
lg: "h-14 rounded-2xl px-8 text-base",
|
||||
xl: "h-16 rounded-2xl px-10 text-lg",
|
||||
sm: "h-9 px-4",
|
||||
lg: "h-14 px-8 text-base",
|
||||
xl: "h-16 px-10 text-lg",
|
||||
icon: "h-10 w-10",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as React from "react"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("rounded-2xl border bg-card text-card-foreground shadow-sm", className)} {...props} />
|
||||
<div ref={ref} className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)} {...props} />
|
||||
))
|
||||
Card.displayName = "Card"
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export function Dialog({ open, onOpenChange, children }: DialogProps) {
|
||||
<div className="fixed inset-0 z-50">
|
||||
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm" onClick={() => onOpenChange(false)} />
|
||||
<div className="fixed inset-0 flex items-center justify-center p-4">
|
||||
<div className="relative bg-background rounded-2xl shadow-xl max-w-lg w-full max-h-[90vh] overflow-y-auto p-6 animate-in fade-in-0 zoom-in-95">
|
||||
<div className="relative bg-background rounded-lg shadow-xl max-w-lg w-full max-h-[90vh] overflow-y-auto p-6 animate-in fade-in-0 zoom-in-95">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -40,7 +40,7 @@ DropdownMenuTrigger.displayName = "DropdownMenuTrigger"
|
||||
function DropdownMenuContent({ children, className, align = "end" }: { children: React.ReactNode; className?: string; align?: "start" | "end" }) {
|
||||
return (
|
||||
<div className={cn(
|
||||
"absolute z-50 min-w-[180px] overflow-hidden rounded-xl border bg-white p-1.5 shadow-lg animate-scale-in",
|
||||
"absolute z-50 min-w-[180px] overflow-hidden rounded-lg border bg-white p-1.5 shadow-lg animate-scale-in",
|
||||
align === "end" ? "right-0" : "left-0",
|
||||
"top-full mt-1",
|
||||
className
|
||||
|
||||
@@ -5,7 +5,7 @@ const Input = React.forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLI
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
"flex h-11 w-full rounded-xl border border-input bg-background px-4 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-trust-blue focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"flex h-11 w-full rounded-lg border border-input bg-background px-4 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-trust-blue focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
|
||||
@@ -5,7 +5,7 @@ const Select = React.forwardRef<HTMLSelectElement, React.SelectHTMLAttributes<HT
|
||||
<select
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-11 w-full rounded-xl border border-input bg-background px-4 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-trust-blue focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"flex h-11 w-full rounded-lg border border-input bg-background px-4 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-trust-blue focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return <div className={cn("animate-pulse rounded-xl bg-muted", className)} {...props} />
|
||||
return <div className={cn("animate-pulse rounded-lg bg-muted", className)} {...props} />
|
||||
}
|
||||
|
||||
export { Skeleton }
|
||||
|
||||
@@ -16,7 +16,7 @@ function Tabs({ value, onValueChange, children, className }: { value: string; on
|
||||
|
||||
function TabsList({ children, className }: { children: React.ReactNode; className?: string }) {
|
||||
return (
|
||||
<div className={cn("inline-flex h-10 items-center justify-center rounded-xl bg-muted p-1 text-muted-foreground", className)}>
|
||||
<div className={cn("inline-flex h-10 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground", className)}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, React.TextareaHTMLAttribu
|
||||
({ className, ...props }, ref) => (
|
||||
<textarea
|
||||
className={cn(
|
||||
"flex min-h-[80px] w-full rounded-xl border border-input bg-background px-4 py-3 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-trust-blue focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"flex min-h-[80px] w-full rounded-lg border border-input bg-background px-4 py-3 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-trust-blue focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
|
||||
@@ -33,7 +33,7 @@ export function ToastProvider({ children }: { children: React.ReactNode }) {
|
||||
<div
|
||||
key={t.id}
|
||||
className={cn(
|
||||
"rounded-xl px-4 py-3 text-sm font-medium text-white shadow-lg animate-in slide-in-from-right",
|
||||
"rounded-lg px-4 py-3 text-sm font-medium text-white shadow-lg animate-in slide-in-from-right",
|
||||
t.type === 'success' && 'bg-success-green',
|
||||
t.type === 'error' && 'bg-danger-red',
|
||||
t.type === 'info' && 'bg-trust-blue',
|
||||
|
||||
@@ -42,6 +42,14 @@ const config: Config = {
|
||||
border: "hsl(var(--border))",
|
||||
input: "hsl(var(--input))",
|
||||
ring: "hsl(var(--ring))",
|
||||
// Brand colors — named by psychology, not appearance
|
||||
midnight: "#111827",
|
||||
"promise-blue": "#1e40af",
|
||||
"generosity-gold": "#f59e0b",
|
||||
"fulfilled-green": "#16a34a",
|
||||
"alert-red": "#dc2626",
|
||||
paper: "#f9fafb",
|
||||
// Legacy aliases (used across codebase)
|
||||
"trust-blue": "#1e40af",
|
||||
"warm-amber": "#f59e0b",
|
||||
"success-green": "#16a34a",
|
||||
|
||||
Reference in New Issue
Block a user