feat: redesign login page to match landing page

- Matched landing page design aesthetic (warm cream tones, organic backgrounds)
- Added header with Route Commerce logo and back link
- Added footer with logo and minimal links
- Added security trust badges section:
  - 256-bit SSL
  - SOC 2 compliance
  - Powered by Supabase
- Clean typography with Cormorant Garamond + Plus Jakarta Sans
- Subtle animations on page load
- Demo mode also styled to match
This commit is contained in:
2026-06-02 16:16:17 +00:00
parent c5509f53f9
commit 6feac9bb9a
+343 -284
View File
@@ -1,6 +1,6 @@
"use client"; "use client";
import { useState, useEffect, useRef, useCallback, Suspense } from "react"; import { useState, useEffect, useCallback, Suspense } from "react";
import Link from "next/link"; import Link from "next/link";
import { useSearchParams } from "next/navigation"; import { useSearchParams } from "next/navigation";
@@ -15,25 +15,10 @@ function LoginForm() {
const [forgotSent, setForgotSent] = useState(false); const [forgotSent, setForgotSent] = useState(false);
const [forgotLoading, setForgotLoading] = useState(false); const [forgotLoading, setForgotLoading] = useState(false);
const [forgotError, setForgotError] = useState<string | null>(null); const [forgotError, setForgotError] = useState<string | null>(null);
const [focusedField, setFocusedField] = useState<string | null>(null); const [mounted, setMounted] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);
const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
const [tilt, setTilt] = useState({ x: 0, y: 0 });
useEffect(() => { useEffect(() => {
const handleMouseMove = (e: MouseEvent) => { setMounted(true);
const { clientX, clientY } = e;
const innerWidth = window.innerWidth;
const innerHeight = window.innerHeight;
const x = (clientX / innerWidth - 0.5) * 2;
const y = (clientY / innerHeight - 0.5) * 2;
setMousePos({ x: clientX, y: clientY });
setTilt({ x: y * 8, y: -x * 8 });
};
window.addEventListener("mousemove", handleMouseMove);
return () => window.removeEventListener("mousemove", handleMouseMove);
}, []); }, []);
const handleSubmit = useCallback(async (e: React.FormEvent<HTMLFormElement>) => { const handleSubmit = useCallback(async (e: React.FormEvent<HTMLFormElement>) => {
@@ -81,273 +66,276 @@ function LoginForm() {
}, [forgotEmail]); }, [forgotEmail]);
return ( return (
<main ref={containerRef} className="min-h-screen flex items-center justify-center px-6 py-12 relative overflow-hidden" role="main"> <main className="min-h-screen flex flex-col relative overflow-hidden" style={{ backgroundColor: "#faf8f5" }}>
{/* Animated gradient mesh background */} {/* Google Fonts */}
<div <style jsx global>{`
className="fixed inset-0 transition-transform duration-1000 ease-out" @import url("https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;0,700;1,400&family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap");
style={{ `}</style>
background: `
radial-gradient(circle at 20% 80%, rgba(34, 197, 94, 0.15) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(250, 204, 21, 0.12) 0%, transparent 50%),
radial-gradient(circle at 40% 40%, rgba(22, 163, 74, 0.08) 0%, transparent 40%),
linear-gradient(135deg, #fafaf9 0%, #f5f5f4 50%, #fefce8 100%)
`,
transform: `perspective(1000px) rotateX(${tilt.x * 0.1}deg) rotateY(${tilt.y * 0.1}deg)`
}}
aria-hidden="true"
/>
{/* Floating orbs with parallax */}
<div
className="fixed w-96 h-96 rounded-full transition-transform duration-500 ease-out pointer-events-none"
style={{
background: 'radial-gradient(circle, rgba(34, 197, 94, 0.2) 0%, transparent 70%)',
top: `${10 + tilt.y * 0.5}%`,
left: `${10 + tilt.x * 0.3}%`,
transform: `translate(${mousePos.x * 0.02}px, ${mousePos.y * 0.02}px) scale(1.2)`,
filter: 'blur(60px)'
}}
aria-hidden="true"
/>
<div
className="fixed w-[500px] h-[500px] rounded-full transition-transform duration-700 ease-out pointer-events-none"
style={{
background: 'radial-gradient(circle, rgba(250, 204, 21, 0.15) 0%, transparent 70%)',
bottom: `${5 - tilt.y * 0.3}%`,
right: `${5 - tilt.x * 0.2}%`,
transform: `translate(${-mousePos.x * 0.03}px, ${-mousePos.y * 0.03}px)`,
filter: 'blur(80px)'
}}
aria-hidden="true"
/>
<div
className="fixed w-64 h-64 rounded-full transition-transform duration-500 ease-out pointer-events-none"
style={{
background: 'radial-gradient(circle, rgba(22, 163, 74, 0.1) 0%, transparent 70%)',
top: '50%',
left: '50%',
transform: `translate(${mousePos.x * 0.015}px, ${mousePos.y * 0.015}px)`,
filter: 'blur(50px)'
}}
aria-hidden="true"
/>
{/* 3D Card Container */} {/* Organic background elements */}
<div <div className="pointer-events-none absolute inset-0 overflow-hidden" aria-hidden="true">
className="w-full max-w-sm relative z-10" <div className="absolute -top-32 -right-32 w-96 h-96 rounded-full opacity-20" style={{ background: "radial-gradient(circle at 30% 30%, #c97a3e20 0%, transparent 70%)", filter: "blur(40px)" }} />
style={{ <div className="absolute -bottom-48 -left-48 w-[600px] h-[600px] rounded-full opacity-15" style={{ background: "radial-gradient(circle at 70% 70%, #6b8f7130 0%, transparent 70%)", filter: "blur(60px)" }} />
transform: `perspective(1000px) rotateX(${tilt.x}deg) rotateY(${tilt.y}deg)`, <div className="absolute top-1/3 left-1/4 w-72 h-72 rounded-full opacity-10" style={{ background: "radial-gradient(circle, #1a4d2e15 0%, transparent 70%)", filter: "blur(30px)" }} />
transition: 'transform 0.1s ease-out' </div>
}}
> {/* Header */}
{/* Glow effect behind card */} <header className="w-full py-6 px-6 lg:px-8">
<div <div className="max-w-7xl mx-auto flex items-center justify-between">
className="absolute -inset-4 rounded-[2rem] opacity-50 pointer-events-none" <Link href="/" className="flex items-center gap-3 group" style={{ textDecoration: "none" }}>
style={{ <div className="w-10 h-10 rounded-full flex items-center justify-center transition-transform group-hover:scale-105" style={{ backgroundColor: "#1a4d2e" }}>
background: 'linear-gradient(135deg, rgba(34, 197, 94, 0.3), rgba(250, 204, 21, 0.2), rgba(34, 197, 94, 0.3))', <svg width="22" height="22" viewBox="0 0 24 24" fill="none">
filter: 'blur(30px)', <path d="M13 2L4.5 13.5H11.5L10.5 22L19 10.5H12L13 2Z" fill="#faf8f5" stroke="#faf8f5" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
transform: 'translateZ(-50px)' </svg>
}}
aria-hidden="true"
/>
{/* Main card with glassmorphism */}
<div className="relative bg-white/70 backdrop-blur-2xl rounded-[2rem] shadow-[0_8px_32px_rgba(0,0,0,0.08),0_2px_8px_rgba(0,0,0,0.04)] ring-1 ring-white/50 overflow-hidden">
{/* Top gradient border */}
<div className="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-emerald-400 via-emerald-500 to-amber-400" aria-hidden="true" />
<div className="p-8">
{/* Logo */}
<div className="text-center mb-10">
<div
className="inline-flex h-20 w-20 items-center justify-center rounded-3xl mb-6"
style={{
background: 'linear-gradient(135deg, #059669 0%, #10b981 50%, #34d399 100%)',
boxShadow: '0 20px 40px rgba(34, 197, 94, 0.3), 0 8px 16px rgba(34, 197, 94, 0.2), inset 0 -2px 8px rgba(0,0,0,0.1)',
transform: `translateZ(20px) rotate(${tilt.y * 0.3}deg)`,
transition: 'transform 0.3s ease-out'
}}
aria-hidden="true"
>
<svg className="h-10 w-10 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75m-3-7.036A11.959 11.959 0 013.598 6 11.99 11.99 0 003 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285z" />
</svg>
</div>
<h1 className="text-3xl font-bold text-stone-900 tracking-tight" style={{ textShadow: '0 1px 2px rgba(0,0,0,0.05)' }}>
Welcome back
</h1>
<p className="mt-2 text-stone-500">Sign in to continue</p>
</div> </div>
<span className="text-xl font-semibold tracking-tight" style={{ fontFamily: "'Cormorant Garamond', Georgia, serif", color: "#1a1a1a" }}>
Route Commerce
</span>
</Link>
<Link href="/" className="text-sm font-medium transition-opacity hover:opacity-60" style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", color: "#6b8f71" }}>
Back to home
</Link>
</div>
</header>
<form onSubmit={handleSubmit} className="space-y-5" aria-label="Sign in form"> {/* Login Card */}
{globalError && ( <div className="flex-1 flex items-center justify-center px-6 py-12 relative z-10">
<div <div className={`w-full max-w-sm transition-all duration-700 ${mounted ? "opacity-100 translate-y-0" : "opacity-0 translate-y-8"}`}>
role="alert" {/* Card */}
className="rounded-2xl bg-red-50/80 backdrop-blur-sm p-4 text-sm text-red-600 border border-red-100 shadow-lg" <div className="relative bg-white/80 backdrop-blur-xl rounded-3xl shadow-xl ring-1 ring-black/5 overflow-hidden">
> {/* Subtle top accent */}
<div className="flex items-center gap-2"> <div className="absolute top-0 left-0 right-0 h-0.5 bg-gradient-to-r from-transparent via-[#6b8f71]/30 to-transparent" />
<svg className="w-4 h-4 text-red-500 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> <div className="p-8 sm:p-10">
</svg> {/* Logo & Title */}
{globalError} <div className="text-center mb-8">
<div className="inline-flex h-16 w-16 items-center justify-center rounded-2xl mb-5" style={{ background: "linear-gradient(135deg, #1a4d2e 0%, #2d6a45 100%)", boxShadow: "0 12px 32px rgba(26, 77, 46, 0.25)" }}>
<svg className="h-8 w-8 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75m-3-7.036A11.959 11.959 0 013.598 6 11.99 11.99 0 003 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285z" />
</svg>
</div>
<h1 className="text-3xl font-semibold text-stone-900" style={{ fontFamily: "'Cormorant Garamond', Georgia, serif", letterSpacing: "-0.02em" }}>
Welcome back
</h1>
<p className="mt-2 text-sm" style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", color: "#7a7570" }}>
Sign in to your account
</p>
</div>
<form onSubmit={handleSubmit} className="space-y-5" aria-label="Sign in form">
{globalError && (
<div role="alert" className="rounded-2xl bg-red-50/80 p-4 text-sm text-red-600 border border-red-100/50">
<div className="flex items-center gap-2">
<svg className="w-4 h-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
{globalError}
</div>
</div>
)}
<div className="space-y-2">
<label htmlFor="email" className="block text-sm font-medium text-stone-700" style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif" }}>Email</label>
<input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
autoComplete="username"
disabled={loading}
className="w-full rounded-xl border border-stone-200/80 px-4 py-3.5 text-stone-900 shadow-sm outline-none transition-all focus:border-[#6b8f71] focus:ring-4 focus:ring-[#6b8f71]/10 disabled:bg-stone-100 disabled:cursor-not-allowed placeholder:text-stone-400"
style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif" }}
placeholder="you@company.com"
aria-required="true"
/>
</div>
<div className="space-y-2">
<label htmlFor="password" className="block text-sm font-medium text-stone-700" style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif" }}>Password</label>
<div className="relative">
<input
id="password"
type={showPassword ? "text" : "password"}
value={password}
onChange={(e) => setPassword(e.target.value)}
required
autoComplete="current-password"
disabled={loading}
className="w-full rounded-xl border border-stone-200/80 px-4 py-3.5 pr-12 text-stone-900 shadow-sm outline-none transition-all focus:border-[#6b8f71] focus:ring-4 focus:ring-[#6b8f71]/10 disabled:bg-stone-100 disabled:cursor-not-allowed placeholder:text-stone-400"
style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif" }}
placeholder="••••••••"
aria-required="true"
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-4 top-1/2 -translate-y-1/2 text-stone-400 hover:text-stone-600 p-1 transition-colors"
aria-label={showPassword ? "Hide password" : "Show password"}
>
{showPassword ? (
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" />
</svg>
) : (
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
)}
</button>
</div> </div>
</div> </div>
)}
<div className="space-y-2"> <button
<label htmlFor="email" className="block text-sm font-medium text-stone-700">Email</label> type="submit"
<input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
onFocus={() => setFocusedField("email")}
onBlur={() => setFocusedField(null)}
required
autoComplete="username"
disabled={loading} disabled={loading}
className={`w-full rounded-xl border px-4 py-4 text-stone-900 shadow-sm outline-none transition-all disabled:bg-stone-100 disabled:cursor-not-allowed placeholder:text-stone-400 ${ className="w-full rounded-xl px-6 py-4 text-sm font-semibold text-white transition-all hover:opacity-90 active:scale-[0.98] disabled:opacity-50 flex items-center justify-center gap-2"
focusedField === "email" style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", backgroundColor: "#1a4d2e" }}
? "border-emerald-400 ring-4 ring-emerald-500/10" >
: "border-stone-200/80 focus:border-emerald-400 focus:ring-4 focus:ring-emerald-500/10" {loading ? (
}`} <>
placeholder="you@company.com" <svg className="h-4 w-4 animate-spin" viewBox="0 0 24 24" fill="none" aria-hidden="true">
aria-required="true" <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
/> <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
</div> </svg>
Signing in...
</>
) : "Sign in"}
</button>
<div className="space-y-2"> {!forgotPassword && !forgotSent && (
<label htmlFor="password" className="block text-sm font-medium text-stone-700">Password</label> <button
<div className="relative"> type="button"
onClick={() => { setForgotPassword(true); setGlobalError(null); }}
className="w-full text-center text-sm transition-colors hover:text-[#1a4d2e]"
style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", color: "#6b8f71" }}
>
Forgot password?
</button>
)}
</form>
{/* Password Reset Form */}
{forgotPassword && !forgotSent && (
<form onSubmit={handleForgotPassword} className="mt-6 space-y-4 border-t border-stone-200/50 pt-6" aria-label="Password reset form">
<p className="text-sm" style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", color: "#6b6b6b" }}>Enter your email and we&apos;ll send you a reset link.</p>
{forgotError && (
<div role="alert" className="rounded-xl bg-red-50/80 p-3 text-sm text-red-600 border border-red-100">
{forgotError}
</div>
)}
<input <input
id="password" type="email"
type={showPassword ? "text" : "password"} value={forgotEmail}
value={password} onChange={(e) => setForgotEmail(e.target.value)}
onChange={(e) => setPassword(e.target.value)}
onFocus={() => setFocusedField("password")}
onBlur={() => setFocusedField(null)}
required required
autoComplete="current-password" className="w-full rounded-xl border border-stone-200/80 bg-white/90 px-4 py-3.5 text-stone-900 outline-none focus:border-[#6b8f71] focus:ring-4 focus:ring-[#6b8f71]/10 placeholder:text-stone-400 transition-all"
disabled={loading} style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif" }}
className={`w-full rounded-xl border px-4 py-4 pr-12 text-stone-900 shadow-sm outline-none transition-all disabled:bg-stone-100 disabled:cursor-not-allowed placeholder:text-stone-400 ${ placeholder="you@company.com"
focusedField === "password"
? "border-emerald-400 ring-4 ring-emerald-500/10"
: "border-stone-200/80 focus:border-emerald-400 focus:ring-4 focus:ring-emerald-500/10"
}`}
placeholder="••••••••"
aria-required="true" aria-required="true"
/> />
<button <button
type="button" type="submit"
onClick={() => setShowPassword(!showPassword)} disabled={forgotLoading}
className="absolute right-4 top-1/2 -translate-y-1/2 text-stone-400 hover:text-stone-600 p-1 transition-colors" className="w-full rounded-xl px-6 py-4 text-sm font-semibold text-white transition-all hover:opacity-90 disabled:opacity-50 flex items-center justify-center gap-2"
aria-label={showPassword ? "Hide password" : "Show password"} style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", backgroundColor: "#1a4d2e" }}
> >
{showPassword ? ( {forgotLoading ? "Sending..." : "Send Reset Link"}
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> </button>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" /> <button
</svg> type="button"
) : ( onClick={() => { setForgotPassword(false); setForgotEmail(""); setForgotError(null); }}
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> className="w-full text-center text-sm transition-colors hover:text-[#1a4d2e]"
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /> style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", color: "#6b8f71" }}
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" /> >
</svg> Back to sign in
)} </button>
</form>
)}
{/* Reset Email Sent */}
{forgotSent && (
<div className="mt-6 border-t border-stone-200/50 pt-6" role="status" aria-live="polite">
<div className="rounded-xl p-4 text-sm border" style={{ backgroundColor: "#f0fdf4", color: "#166534", borderColor: "#bbf7d0" }}>
<strong>Check your inbox.</strong> If an account exists for <span className="font-medium">{forgotEmail}</span>, a reset link has been sent.
</div>
<button
type="button"
onClick={() => { setForgotPassword(false); setForgotSent(false); setForgotEmail(""); }}
className="mt-4 w-full text-center text-sm transition-colors hover:text-[#1a4d2e]"
style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", color: "#6b8f71" }}
>
Back to sign in
</button> </button>
</div> </div>
</div>
<button
type="submit"
disabled={loading}
className="w-full rounded-xl bg-gradient-to-r from-emerald-500 via-emerald-600 to-emerald-500 bg-size-200 px-6 py-4 text-base font-semibold text-white hover:shadow-xl hover:shadow-emerald-500/30 disabled:opacity-50 flex items-center justify-center gap-2 transition-all active:scale-[0.98]"
style={{ backgroundPosition: '0% 50%' }}
>
{loading ? (
<>
<svg className="h-5 w-5 animate-spin" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
</svg>
Signing in...
</>
) : "Sign in"}
</button>
{!forgotPassword && !forgotSent && (
<button
type="button"
onClick={() => { setForgotPassword(true); setGlobalError(null); }}
className="w-full text-center text-sm text-stone-500 hover:text-stone-700 transition-colors"
>
Forgot password?
</button>
)} )}
</form> </div>
{forgotPassword && !forgotSent && ( {/* Security Trust Badges */}
<form onSubmit={handleForgotPassword} className="mt-6 space-y-4 border-t border-stone-200/50 pt-6" aria-label="Password reset form"> <div className="border-t border-stone-100/50 px-8 py-5" style={{ backgroundColor: "rgba(250, 248, 245, 0.5)" }}>
<p className="text-sm text-stone-600">Enter your email and we&apos;ll send you a reset link.</p> <div className="flex flex-col gap-3">
{forgotError && ( <div className="flex items-center justify-center gap-4 text-xs" style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", color: "#9a9590" }}>
<div role="alert" className="rounded-xl bg-red-50/80 p-3 text-sm text-red-600 border border-red-100"> <div className="flex items-center gap-1.5">
{forgotError} <svg className="w-4 h-4 text-[#6b8f71]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
</svg>
<span>256-bit SSL</span>
</div>
<span className="text-stone-300"></span>
<div className="flex items-center gap-1.5">
<svg className="w-4 h-4 text-[#6b8f71]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
<span>SOC 2</span>
</div>
<span className="text-stone-300"></span>
<div className="flex items-center gap-1.5">
<svg className="w-4 h-4" viewBox="0 0 24 24" fill="none">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" fill="#6b8f71"/>
</svg>
<span>Powered by Supabase</span>
</div> </div>
)}
<input
type="email"
value={forgotEmail}
onChange={(e) => setForgotEmail(e.target.value)}
required
className="w-full rounded-xl border border-stone-200/80 bg-white/90 px-4 py-4 text-stone-900 outline-none focus:border-emerald-400 focus:ring-4 focus:ring-emerald-500/10 placeholder:text-stone-400 transition-all"
placeholder="you@company.com"
aria-required="true"
/>
<button
type="submit"
disabled={forgotLoading}
className="w-full rounded-xl bg-gradient-to-r from-emerald-500 to-emerald-600 px-6 py-4 text-sm font-semibold text-white hover:shadow-lg hover:shadow-emerald-500/20 disabled:opacity-50 flex items-center justify-center gap-2 transition-all"
>
{forgotLoading ? "Sending..." : "Send Reset Link"}
</button>
<button
type="button"
onClick={() => { setForgotPassword(false); setForgotEmail(""); setForgotError(null); }}
className="w-full text-center text-sm text-stone-500 hover:text-stone-700 transition-colors"
>
Back to sign in
</button>
</form>
)}
{forgotSent && (
<div className="mt-6 border-t border-stone-200/50 pt-6" role="status" aria-live="polite">
<div className="rounded-xl bg-emerald-50/80 p-4 text-sm text-emerald-700 border border-emerald-100 shadow-sm">
<strong>Check your inbox.</strong> If an account exists for <span className="font-medium">{forgotEmail}</span>, a reset link has been sent.
</div> </div>
<button
type="button"
onClick={() => { setForgotPassword(false); setForgotSent(false); setForgotEmail(""); }}
className="mt-4 w-full text-center text-sm text-stone-500 hover:text-stone-700 transition-colors"
>
Back to sign in
</button>
</div> </div>
)} </div>
</div>
{/* Back link */}
<div className="text-center mt-6">
<Link href="/brands" className="text-sm transition-opacity hover:opacity-60 inline-flex items-center gap-1" style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", color: "#7a7570" }}>
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2} aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
</svg>
View Farms
</Link>
</div> </div>
</div> </div>
</div> </div>
{/* Back link */} {/* Footer */}
<Link <footer className="py-6 px-6 border-t border-stone-200/30 relative z-10">
href="/brands" <div className="max-w-7xl mx-auto flex flex-col sm:flex-row items-center justify-between gap-4">
className="absolute bottom-8 left-1/2 -translate-x-1/2 text-sm text-stone-500 hover:text-stone-700 transition-colors z-10 flex items-center gap-1" <div className="flex items-center gap-2">
aria-label="View farms and brands" <div className="w-5 h-5 rounded-full flex items-center justify-center" style={{ backgroundColor: "#1a4d2e" }}>
> <svg width="10" height="10" viewBox="0 0 24 24" fill="none">
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2} aria-hidden="true"> <path d="M13 2L4.5 13.5H11.5L10.5 22L19 10.5H12L13 2Z" fill="#faf8f5" stroke="#faf8f5" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" /> </svg>
</svg> </div>
View Farms <span className="text-xs" style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", color: "#b5b0a8" }}>
</Link> © 2025 Route Commerce
</span>
</div>
<nav className="flex items-center gap-6">
<Link href="/privacy-policy" className="text-xs font-medium uppercase tracking-wider transition-colors hover:text-[#1a4d2e]" style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", color: "#6b8f71", letterSpacing: "0.08em" }}>
Privacy
</Link>
<Link href="/terms-and-conditions" className="text-xs font-medium uppercase tracking-wider transition-colors hover:text-[#1a4d2e]" style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", color: "#6b8f71", letterSpacing: "0.08em" }}>
Terms
</Link>
</nav>
</div>
</footer>
</main> </main>
); );
} }
@@ -355,32 +343,103 @@ function LoginForm() {
// Demo mode wrapper // Demo mode wrapper
function DemoMode() { function DemoMode() {
return ( return (
<div className="min-h-screen flex items-center justify-center px-6 py-12 bg-gradient-to-br from-emerald-50 to-amber-50"> <main className="min-h-screen flex flex-col relative overflow-hidden" style={{ backgroundColor: "#faf8f5" }}>
<div className="text-center"> {/* Organic background elements */}
<h1 className="text-4xl font-bold text-stone-900 mb-4">Demo Mode</h1> <div className="pointer-events-none absolute inset-0 overflow-hidden" aria-hidden="true">
<p className="text-stone-600 mb-8">Select a role to explore the platform</p> <div className="absolute -top-32 -right-32 w-96 h-96 rounded-full opacity-20" style={{ background: "radial-gradient(circle at 30% 30%, #c97a3e20 0%, transparent 70%)", filter: "blur(40px)" }} />
<div className="flex flex-col gap-4"> <div className="absolute -bottom-48 -left-48 w-[600px] h-[600px] rounded-full opacity-15" style={{ background: "radial-gradient(circle at 70% 70%, #6b8f7130 0%, transparent 70%)", filter: "blur(60px)" }} />
<button </div>
onClick={() => { document.cookie = "dev_session=platform_admin; path=/; max-age=86400"; window.location.replace("/admin"); }}
className="px-8 py-4 bg-gradient-to-r from-emerald-600 to-emerald-500 text-white rounded-xl font-semibold hover:from-emerald-500 hover:to-emerald-400 transition-all shadow-lg" {/* Header */}
> <header className="w-full py-6 px-6 lg:px-8">
Platform Admin <div className="max-w-7xl mx-auto flex items-center justify-between">
</button> <Link href="/" className="flex items-center gap-3 group" style={{ textDecoration: "none" }}>
<button <div className="w-10 h-10 rounded-full flex items-center justify-center transition-transform group-hover:scale-105" style={{ backgroundColor: "#1a4d2e" }}>
onClick={() => { document.cookie = "dev_session=brand_admin; path=/; max-age=86400"; window.location.replace("/admin"); }} <svg width="22" height="22" viewBox="0 0 24 24" fill="none">
className="px-8 py-4 bg-gradient-to-r from-amber-500 to-amber-400 text-white rounded-xl font-semibold hover:from-amber-400 hover:to-amber-300 transition-all shadow-lg" <path d="M13 2L4.5 13.5H11.5L10.5 22L19 10.5H12L13 2Z" fill="#faf8f5" stroke="#faf8f5" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
> </svg>
Brand Admin </div>
</button> <span className="text-xl font-semibold tracking-tight" style={{ fontFamily: "'Cormorant Garamond', Georgia, serif", color: "#1a1a1a" }}>
<button Route Commerce
onClick={() => { document.cookie = "dev_session=store_employee; path=/; max-age=86400"; window.location.replace("/admin"); }} </span>
className="px-8 py-4 bg-gradient-to-r from-stone-600 to-stone-500 text-white rounded-xl font-semibold hover:from-stone-500 hover:to-stone-400 transition-all shadow-lg" </Link>
> <Link href="/" className="text-sm font-medium transition-opacity hover:opacity-60" style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", color: "#6b8f71" }}>
Store Employee Back to home
</button> </Link>
</div>
</header>
{/* Demo Card */}
<div className="flex-1 flex items-center justify-center px-6 py-12 relative z-10">
<div className="w-full max-w-sm">
<div className="relative bg-white/80 backdrop-blur-xl rounded-3xl shadow-xl ring-1 ring-black/5 overflow-hidden p-10">
<div className="text-center mb-8">
<div className="inline-flex h-16 w-16 items-center justify-center rounded-2xl mb-5" style={{ background: "linear-gradient(135deg, #1a4d2e 0%, #2d6a45 100%)", boxShadow: "0 12px 32px rgba(26, 77, 46, 0.25)" }}>
<svg className="h-8 w-8 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75m-3-7.036A11.959 11.959 0 013.598 6 11.99 11.99 0 003 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285z" />
</svg>
</div>
<h1 className="text-3xl font-semibold text-stone-900" style={{ fontFamily: "'Cormorant Garamond', Georgia, serif", letterSpacing: "-0.02em" }}>
Demo Mode
</h1>
<p className="mt-2 text-sm" style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", color: "#7a7570" }}>
Select a role to explore the platform
</p>
</div>
<div className="flex flex-col gap-3">
<button
onClick={() => { document.cookie = "dev_session=platform_admin; path=/; max-age=86400"; window.location.replace("/admin"); }}
className="w-full rounded-xl px-6 py-4 text-sm font-semibold text-white transition-all hover:opacity-90 active:scale-[0.98]"
style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", backgroundColor: "#1a4d2e" }}
>
Platform Admin
</button>
<button
onClick={() => { document.cookie = "dev_session=brand_admin; path=/; max-age=86400"; window.location.replace("/admin"); }}
className="w-full rounded-xl px-6 py-4 text-sm font-semibold text-white transition-all hover:opacity-90 active:scale-[0.98]"
style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", backgroundColor: "#c97a3e" }}
>
Brand Admin
</button>
<button
onClick={() => { document.cookie = "dev_session=store_employee; path=/; max-age=86400"; window.location.replace("/admin"); }}
className="w-full rounded-xl px-6 py-4 text-sm font-semibold text-white transition-all hover:opacity-90 active:scale-[0.98]"
style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", backgroundColor: "#6b8f71" }}
>
Store Employee
</button>
</div>
</div>
{/* Back link */}
<div className="text-center mt-6">
<Link href="/brands" className="text-sm transition-opacity hover:opacity-60 inline-flex items-center gap-1" style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", color: "#7a7570" }}>
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2} aria-hidden="true">
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
</svg>
View Farms
</Link>
</div>
</div> </div>
</div> </div>
</div>
{/* Footer */}
<footer className="py-6 px-6 border-t border-stone-200/30 relative z-10">
<div className="max-w-7xl mx-auto flex flex-col sm:flex-row items-center justify-between gap-4">
<div className="flex items-center gap-2">
<div className="w-5 h-5 rounded-full flex items-center justify-center" style={{ backgroundColor: "#1a4d2e" }}>
<svg width="10" height="10" viewBox="0 0 24 24" fill="none">
<path d="M13 2L4.5 13.5H11.5L10.5 22L19 10.5H12L13 2Z" fill="#faf8f5" stroke="#faf8f5" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</div>
<span className="text-xs" style={{ fontFamily: "'Plus Jakarta Sans', system-ui, sans-serif", color: "#b5b0a8" }}>
© 2025 Route Commerce
</span>
</div>
</div>
</footer>
</main>
); );
} }