Compare commits

..

2 Commits

5 changed files with 1778497 additions and 74 deletions

1428368
public/aerox.obj Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,6 @@ import { useRouter } from 'next/navigation';
import Image from 'next/image';
import HeroBackground3D from '@/components/ui/3d/HeroBackground3D';
import CardIcon3D from '@/components/ui/3d/CardIcon3D';
import { users } from '@/data/mockData';
import { Zap, ArrowRight, Bike, Wallet, Leaf, ShieldCheck, MapPin, Truck, ChevronRight, BarChart3, BatteryCharging, Clock, Activity } from 'lucide-react';
export default function LandingPage() {
@@ -73,7 +72,7 @@ export default function LandingPage() {
</div>
<div className="flex items-center gap-4">
<button
onClick={() => router.push('/login')}
onClick={() => router.push('/coming-soon')}
className={`px-5 py-2.5 rounded-full font-semibold transition-all flex items-center gap-2 ${scrolled
? 'bg-accent text-white hover:bg-accent-dark shadow-md hover:shadow-lg'
: 'bg-white text-accent hover:bg-slate-100 shadow-xl'
@@ -86,28 +85,30 @@ export default function LandingPage() {
</header>
{/* Hero Section */}
<section className="relative pt-32 pb-20 lg:pt-48 lg:pb-32 overflow-hidden bg-slate-900">
<section className="relative pt-32 pb-24 lg:pt-48 lg:pb-40 overflow-hidden bg-slate-900 min-h-[88vh] lg:min-h-screen flex items-center">
<div className="absolute inset-0 bg-gradient-to-br from-slate-900 via-slate-800 to-accent/20 z-0" />
<div className="absolute inset-0 bg-[url('/noise.png')] opacity-20 mix-blend-overlay z-0"></div>
<HeroBackground3D />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10 text-center">
<div className="inline-flex items-center gap-2 px-4 py-2 bg-white/10 backdrop-blur-md border border-white/20 rounded-full text-white text-sm font-medium mb-8">
<Leaf className="w-4 h-4 text-emerald-400" /> Bangladesh&apos;s Leading EV Platform
</div>
<h1 className="text-4xl sm:text-5xl lg:text-7xl font-extrabold text-white mb-6 leading-tight tracking-tight">
Ditch Petrol. <br className="hidden sm:block" />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-accent via-emerald-400 to-accent-light">Go Electric</span> with JAIBEN.
</h1>
<p className="text-slate-300 text-lg sm:text-xl max-w-2xl mx-auto mb-10">
The largest EV Tech platform enabling the Gig Economy. Rent, Rent-to-Own, or Invest in EVs. Join the sustainable mobility revolution today.
</p>
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
<button
onClick={() => router.push('/coming-soon')}
className="w-full sm:w-auto px-8 py-4 bg-accent text-white rounded-full font-bold text-lg hover:bg-accent-dark transition-all transform hover:scale-105 shadow-xl shadow-accent/30 flex items-center justify-center gap-2"
>
Get Started Now <ArrowRight className="w-5 h-5" />
</button>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10 w-full text-center lg:text-left">
<div className="max-w-2xl lg:max-w-xl">
<div className="inline-flex items-center gap-2 px-4 py-2 bg-white/10 backdrop-blur-md border border-white/20 rounded-full text-white text-sm font-medium mb-8">
<Leaf className="w-4 h-4 text-emerald-400" /> Bangladesh&apos;s Leading EV Platform
</div>
<h1 className="text-4xl sm:text-5xl lg:text-7xl font-extrabold text-white mb-6 leading-tight tracking-tight drop-shadow-2xl">
Ditch Petrol. <br className="hidden sm:block" />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-accent via-emerald-400 to-accent-light">Go Electric</span> with JAIBEN.
</h1>
<p className="text-slate-300 text-lg sm:text-xl mb-10 drop-shadow-lg">
The largest EV Tech platform enabling the Gig Economy. Rent, Rent-to-Own, or Invest in EVs. Join the sustainable mobility revolution today.
</p>
<div className="flex flex-col sm:flex-row items-center lg:items-start justify-center lg:justify-start gap-4">
<button
onClick={() => router.push('/coming-soon')}
className="w-full sm:w-auto px-8 py-4 bg-accent text-white rounded-full font-bold text-lg hover:bg-accent-dark transition-all transform hover:scale-105 shadow-xl shadow-accent/30 flex items-center justify-center gap-2"
>
Get Started Now <ArrowRight className="w-5 h-5" />
</button>
</div>
</div>
</div>

View File

@@ -1,65 +1,192 @@
'use client';
import { useRef } from 'react';
import { Canvas, useFrame } from '@react-three/fiber';
import { Float, Box, Cylinder } from '@react-three/drei';
import * as THREE from 'three';
function StylizedScooter() {
import { Suspense, useMemo, useRef } from 'react';
import { Canvas, useFrame, useLoader } from '@react-three/fiber';
import {
Float,
ContactShadows,
Environment,
Lightformer,
Sparkles,
Html,
useProgress,
} from '@react-three/drei';
import * as THREE from 'three';
import { OBJLoader } from 'three-stdlib';
const ACCENT = '#00bc84';
const ACCENT_BRIGHT = '#10b981';
function Loader() {
const { progress } = useProgress();
return (
<Html center>
<div className="flex flex-col items-center gap-3">
<div className="w-12 h-12 border-4 border-emerald-500/30 border-t-emerald-500 rounded-full animate-spin" />
<p className="text-emerald-400 text-sm font-semibold">{Math.round(progress)}%</p>
</div>
</Html>
);
}
function AeroxModel() {
const obj = useLoader(OBJLoader, '/uploads_files_3634924_uploads_files_850048_aerox.obj');
useLoader.preload(OBJLoader, '/uploads_files_3634924_uploads_files_850048_aerox.obj');
const group = useRef<THREE.Group>(null);
useFrame((state, delta) => {
if (group.current) {
group.current.rotation.y += delta * 0.2;
group.current.rotation.z = Math.sin(state.clock.elapsedTime * 0.5) * 0.05;
}
const model = useMemo(() => {
const clone = obj.clone(true);
// Center + reorient + scale. OBJ length is along Z; rotate so length lies along X.
const box = new THREE.Box3().setFromObject(clone);
const size = new THREE.Vector3();
box.getSize(size);
const center = new THREE.Vector3();
box.getCenter(center);
const maxDim = Math.max(size.x, size.y, size.z);
const scale = 3.2 / maxDim;
clone.position.sub(center.multiplyScalar(scale));
clone.scale.setScalar(scale);
// Lay the scooter along X and stand it upright (Y up).
clone.rotation.y = -Math.PI / 2;
clone.rotation.z = 0;
// Apply a cohesive, premium material across all meshes.
const bodyMat = new THREE.MeshStandardMaterial({
color: ACCENT,
roughness: 0.2,
metalness: 0.7,
});
const accentMat = new THREE.MeshStandardMaterial({
color: '#e2e8f0',
roughness: 0.1,
metalness: 0.9,
});
const tireMat = new THREE.MeshStandardMaterial({
color: '#111111',
roughness: 0.9,
metalness: 0.1,
});
clone.traverse((child) => {
if (child instanceof THREE.Mesh) {
child.castShadow = true;
child.receiveShadow = true;
// Heuristic: pick material by geometry size.
const b = new THREE.Box3().setFromObject(child);
const s = new THREE.Vector3();
b.getSize(s);
const maxLocal = Math.max(s.x, s.y, s.z);
if (maxLocal < 0.35) child.material = accentMat;
else if (s.y < 0.25 && maxLocal > 0.5) child.material = tireMat;
else child.material = bodyMat;
}
});
return clone;
}, [obj]);
useFrame((state) => {
if (!group.current) return;
const t = state.clock.elapsedTime;
// Continuous turntable so all sides are appreciated.
group.current.rotation.y = t * 0.4;
group.current.position.y = Math.sin(t * 1.5) * 0.08;
});
return (
<Float speed={2} rotationIntensity={0.5} floatIntensity={1.5}>
<group ref={group} position={[2, 0, -2]} scale={1.2} rotation={[0.2, -0.5, 0]}>
{/* Main Deck */}
<Box args={[4, 0.3, 1]} position={[0, 0, 0]}>
<meshStandardMaterial color="#334155" roughness={0.4} metalness={0.6} />
</Box>
{/* Front Wheel */}
<Cylinder args={[0.8, 0.8, 0.4, 32]} rotation={[Math.PI / 2, 0, 0]} position={[2, 0, 0]}>
<meshStandardMaterial color="#f97316" roughness={0.2} metalness={0.8} />
</Cylinder>
{/* Rear Wheel */}
<Cylinder args={[0.8, 0.8, 0.4, 32]} rotation={[Math.PI / 2, 0, 0]} position={[-2, 0, 0]}>
<meshStandardMaterial color="#f97316" roughness={0.2} metalness={0.8} />
</Cylinder>
{/* Steering Column */}
<Cylinder args={[0.1, 0.1, 3, 16]} position={[1.5, 1.5, 0]} rotation={[0, 0, -0.2]}>
<meshStandardMaterial color="#cbd5e1" roughness={0.3} metalness={0.9} />
</Cylinder>
{/* Handlebars */}
<Box args={[0.2, 1.5, 0.2]} position={[1.2, 3, 0]} rotation={[Math.PI / 2, 0, 0]}>
<meshStandardMaterial color="#0f172a" roughness={0.8} />
</Box>
{/* Accent / Battery block */}
<Box args={[1.5, 0.6, 1.1]} position={[-0.5, 0.4, 0]}>
<meshStandardMaterial color="#00bc84" emissive="#00bc84" emissiveIntensity={0.5} roughness={0.2} />
</Box>
<Float speed={1.2} rotationIntensity={0.1} floatIntensity={0.3}>
<group ref={group} rotation={[0, -0.5, 0]} position={[2.5, -0.1, -1]}>
<primitive object={model} />
</group>
</Float>
);
}
function Crystal({ position, color, scale = 1 }: { position: [number, number, number]; color: string; scale?: number }) {
const ref = useRef<THREE.Mesh>(null);
useFrame((state, delta) => {
if (ref.current) {
ref.current.rotation.y += delta * 0.5;
ref.current.position.y = position[1] + Math.sin(state.clock.elapsedTime + position[0]) * 0.3;
}
});
return (
<Float speed={3} rotationIntensity={1} floatIntensity={2}>
<mesh ref={ref} position={position} scale={scale}>
<octahedronGeometry args={[0.4, 0]} />
<meshStandardMaterial color={color} emissive={color} emissiveIntensity={0.3} roughness={0.1} metalness={0.3} flatShading />
</mesh>
</Float>
);
}
function Floor() {
return (
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, -1.1, 0]} receiveShadow>
<circleGeometry args={[9, 64]} />
<meshStandardMaterial color="#0f172a" roughness={0.6} metalness={0.2} transparent opacity={0.4} />
</mesh>
);
}
function Scene() {
return (
<>
<ambientLight intensity={0.4} />
<directionalLight position={[6, 8, 5]} intensity={1.4} color="#f8fafc" castShadow />
<directionalLight position={[-8, -4, -6]} intensity={0.6} color={ACCENT_BRIGHT} />
<pointLight position={[0, 3, 4]} intensity={0.8} color={ACCENT_BRIGHT} />
<spotLight position={[0, 10, 0]} angle={0.5} intensity={1.2} color="#ffffff" penumbra={1} />
<Suspense fallback={<Loader />}>
<AeroxModel />
</Suspense>
<Crystal position={[-3.8, 1.6, -1]} color={ACCENT_BRIGHT} scale={1.1} />
<Crystal position={[3.8, 1.2, -0.5]} color={ACCENT} scale={0.8} />
<Crystal position={[3.4, -0.8, 0.6]} color="#34d399" scale={0.6} />
<Crystal position={[-4.0, -0.4, 0.8]} color="#34d399" scale={0.7} />
<Sparkles count={90} scale={[11, 7, 7]} size={3} speed={0.4} color={ACCENT_BRIGHT} opacity={0.7} />
<ContactShadows position={[0, -1.06, 0]} opacity={0.55} scale={14} blur={2.5} far={4} color={ACCENT} />
<Floor />
<Environment resolution={256}>
<Lightformer intensity={2} position={[0, 5, 5]} scale={[10, 5, 1]} color="#ffffff" />
<Lightformer intensity={1.2} position={[-5, 2, 3]} scale={[8, 6, 1]} color={ACCENT_BRIGHT} />
<Lightformer intensity={0.8} position={[5, 1, 3]} scale={[8, 6, 1]} color="#0ea5e9" />
<Lightformer intensity={0.6} position={[0, -5, 2]} scale={[10, 5, 1]} color="#1e293b" />
</Environment>
</>
);
}
function Rig({ children }: { children: React.ReactNode }) {
useFrame((state) => {
const { camera, pointer } = state;
camera.position.x += (pointer.x * 1.4 - camera.position.x) * 0.04;
camera.position.y += (-pointer.y * 0.7 + 0.4 - camera.position.y) * 0.04;
camera.lookAt(0, 0, 0);
});
return <>{children}</>;
}
export default function HeroBackground3D() {
return (
<div className="absolute inset-0 pointer-events-none z-0 overflow-hidden">
<Canvas camera={{ position: [0, 0, 8], fov: 45 }}>
<ambientLight intensity={0.5} />
<directionalLight position={[10, 10, 5]} intensity={1} />
<directionalLight position={[-10, -10, -5]} intensity={0.5} color="#00bc84" />
<StylizedScooter />
<div className="absolute inset-0 z-0 overflow-hidden">
<Canvas
shadows
dpr={[1, 2]}
camera={{ position: [0, 0.4, 8], fov: 42 }}
gl={{ antialias: true, alpha: true, powerPreference: 'high-performance' }}
onCreated={({ gl }) => gl.setClearColor(0x000000, 0)}
>
<Rig>
<Scene />
</Rig>
</Canvas>
<div className="pointer-events-none absolute inset-0 bg-gradient-to-r from-slate-900/70 via-slate-900/20 to-transparent lg:from-slate-900/80 lg:via-slate-900/30 lg:to-transparent" />
</div>
);
}

View File

@@ -0,0 +1,52 @@
'use client';
import { Canvas, useFrame } from '@react-three/fiber';
import { Float, Box, Sphere } from '@react-three/drei';
import { useRef } from 'react';
import * as THREE from 'three';
function RotatingLogo({ isScrolled }: { isScrolled: boolean }) {
const group = useRef<THREE.Group>(null);
useFrame((state, delta) => {
if (group.current) {
group.current.rotation.y += delta;
group.current.rotation.x = Math.sin(state.clock.elapsedTime * 2) * 0.1;
}
});
const color = isScrolled ? '#00bc84' : '#10b981';
return (
<Float speed={3} rotationIntensity={0.5} floatIntensity={2}>
<group ref={group} scale={1.2}>
{/* Abstract "J" shape for Jaiben */}
<Box args={[0.25, 1.2, 0.25]} position={[0.2, 0, 0]}>
<meshStandardMaterial color={color} roughness={0.1} metalness={0.8} />
</Box>
<Box args={[0.8, 0.25, 0.25]} position={[-0.075, -0.475, 0]}>
<meshStandardMaterial color={color} roughness={0.1} metalness={0.8} />
</Box>
<Sphere args={[0.2, 32, 32]} position={[-0.3, 0.5, 0]}>
<meshStandardMaterial color="#f97316" roughness={0.2} metalness={0.5} emissive="#f97316" emissiveIntensity={0.2} />
</Sphere>
</group>
</Float>
);
}
export default function Logo3D({ scrolled = false, className = "w-12 h-12" }: { scrolled?: boolean, className?: string }) {
return (
<div className={`${className} pointer-events-none relative z-10 flex-shrink-0`}>
<Canvas
camera={{ position: [0, 0, 3.5], fov: 50 }}
gl={{ alpha: true, antialias: true }}
>
<ambientLight intensity={0.6} />
<directionalLight position={[2, 5, 2]} intensity={1.5} />
<directionalLight position={[-2, -2, 2]} intensity={0.5} color="#00bc84" />
<RotatingLogo isScrolled={scrolled} />
</Canvas>
</div>
);
}