feat: add robotics components and data structures
- Introduced RobotProductCard component for displaying robot details. - Added WhyUs component highlighting key reasons for choosing our robotics solutions. - Implemented CursorSpotlight for enhanced user interaction. - Created GlassPanel for a stylish UI element. - Developed MotionSection for animated section visibility. - Added PremiumButton for versatile button options. - Established data structures for industries and robots, including detailed specifications and use cases. - Included utility functions for retrieving robots by slug and category.
This commit is contained in:
@@ -0,0 +1,361 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { PremiumButton } from '@/components/ui/PremiumButton';
|
||||
import { CursorSpotlight } from '@/components/ui/CursorSpotlight';
|
||||
import { FEATURED_ROBOTS } from '@/data/robots';
|
||||
|
||||
const HERO_STATS = [
|
||||
{ value: 'Exclusive', label: 'UAE access' },
|
||||
{ value: '2', label: 'Premier brands' },
|
||||
{ value: 'Dubai', label: 'Showroom & demo' },
|
||||
];
|
||||
|
||||
type LabelPos = {
|
||||
text: string;
|
||||
delay: number;
|
||||
top?: string;
|
||||
bottom?: string;
|
||||
left?: string;
|
||||
right?: string;
|
||||
};
|
||||
|
||||
const FLOATING_LABELS: LabelPos[] = [
|
||||
{ text: 'Exclusive UAE Access', top: '10%', left: '5%', delay: 0 },
|
||||
{ text: 'Unitree Robotics', top: '22%', right: '4%', delay: 0.3 },
|
||||
{ text: 'Pudu Service Robots', bottom: '28%', left: '4%', delay: 0.6 },
|
||||
{ text: 'Demo & Sales · Dubai', bottom: '12%', right: '6%', delay: 0.9 },
|
||||
];
|
||||
|
||||
export function Hero3DRobotics() {
|
||||
const wrapRef = useRef<HTMLDivElement | null>(null);
|
||||
const [tilt, setTilt] = useState({ rx: 0, ry: 0 });
|
||||
const [activeIdx, setActiveIdx] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
if (reduce) return;
|
||||
const el = wrapRef.current;
|
||||
if (!el) return;
|
||||
const onMove = (e: MouseEvent) => {
|
||||
const rect = el.getBoundingClientRect();
|
||||
const x = (e.clientX - rect.left) / rect.width - 0.5;
|
||||
const y = (e.clientY - rect.top) / rect.height - 0.5;
|
||||
setTilt({ rx: -y * 9, ry: x * 14 });
|
||||
};
|
||||
const onLeave = () => setTilt({ rx: 0, ry: 0 });
|
||||
el.addEventListener('mousemove', onMove);
|
||||
el.addEventListener('mouseleave', onLeave);
|
||||
return () => {
|
||||
el.removeEventListener('mousemove', onMove);
|
||||
el.removeEventListener('mouseleave', onLeave);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const t = setInterval(() => setActiveIdx((i) => (i + 1) % FEATURED_ROBOTS.length), 5400);
|
||||
return () => clearInterval(t);
|
||||
}, []);
|
||||
|
||||
const robot = FEATURED_ROBOTS[activeIdx];
|
||||
|
||||
return (
|
||||
<section
|
||||
style={{
|
||||
position: 'relative',
|
||||
minHeight: 'min(960px, 100svh)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
overflow: 'hidden',
|
||||
paddingTop: 'clamp(6rem, 12vw, 9rem)',
|
||||
paddingBottom: 'clamp(4rem, 8vw, 6rem)',
|
||||
}}
|
||||
>
|
||||
<div className="hero-gradient" />
|
||||
<div className="grid-overlay" />
|
||||
<CursorSpotlight color="rgba(224, 200, 150, 0.22)" size={620} />
|
||||
|
||||
<div
|
||||
aria-hidden
|
||||
style={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
background:
|
||||
'conic-gradient(from 220deg at 60% 50%, transparent 0deg, rgba(196,162,101,0.06) 25deg, transparent 50deg, rgba(224,200,150,0.05) 180deg, transparent 220deg, rgba(196,162,101,0.05) 320deg, transparent 360deg)',
|
||||
mixBlendMode: 'screen',
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="container-wide" style={{ position: 'relative', display: 'grid', gridTemplateColumns: '1fr', gap: 'clamp(2rem, 5vw, 4rem)', alignItems: 'center', width: '100%' }}>
|
||||
<div className="hero-grid" style={{ display: 'grid', gap: 'clamp(2rem, 6vw, 4rem)', alignItems: 'center' }}>
|
||||
<div className="fade-in-up" style={{ display: 'flex', flexDirection: 'column', gap: '1.75rem' }}>
|
||||
<span
|
||||
className="eyebrow"
|
||||
style={{
|
||||
color: '#e0c896',
|
||||
border: '1px solid rgba(196,162,101,0.35)',
|
||||
background: 'rgba(196,162,101,0.06)',
|
||||
padding: '0.5rem 0.9rem',
|
||||
borderRadius: 999,
|
||||
alignSelf: 'flex-start',
|
||||
gap: '0.625rem',
|
||||
}}
|
||||
>
|
||||
<span aria-hidden style={{ width: 6, height: 6, borderRadius: 999, background: '#e0c896', boxShadow: '0 0 12px rgba(224,200,150,0.8)' }} />
|
||||
Exclusive UAE Access · Dubai
|
||||
</span>
|
||||
|
||||
<h1
|
||||
style={{
|
||||
margin: 0,
|
||||
fontSize: 'clamp(2.6rem, 6.4vw, 4.6rem)',
|
||||
lineHeight: 1.02,
|
||||
letterSpacing: '-0.035em',
|
||||
fontWeight: 300,
|
||||
}}
|
||||
>
|
||||
<span style={{ display: 'block', color: '#f5f1e8', fontWeight: 200 }}>Advanced robotics.</span>
|
||||
<span className="text-gradient-accent" style={{ display: 'block', fontWeight: 600 }}>
|
||||
Exclusive UAE access.
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<p style={{ margin: 0, color: '#cbc4b3', fontSize: 'clamp(1rem, 2vw, 1.18rem)', lineHeight: 1.7, maxWidth: 600, fontWeight: 300 }}>
|
||||
YS Lootah Robotics is the exclusive UAE sales destination for selected Unitree and Pudu Robotics solutions — helping businesses explore, configure, book demos, and deploy advanced robots across Dubai and the UAE.
|
||||
</p>
|
||||
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.75rem', marginTop: '0.5rem' }}>
|
||||
<PremiumButton href="/robots/">Explore exclusive robots</PremiumButton>
|
||||
<PremiumButton href="/book-demo/" variant="ghost">Book a demo</PremiumButton>
|
||||
<PremiumButton href="/configure/" variant="outline">Configure your solution</PremiumButton>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(3, minmax(0, 1fr))',
|
||||
gap: 'clamp(0.5rem, 2vw, 1.25rem)',
|
||||
paddingTop: '1.25rem',
|
||||
marginTop: '0.5rem',
|
||||
borderTop: '1px solid rgba(196,162,101,0.18)',
|
||||
}}
|
||||
>
|
||||
{HERO_STATS.map((s) => (
|
||||
<div key={s.label} style={{ display: 'flex', flexDirection: 'column', gap: '0.25rem' }}>
|
||||
<span className="text-gradient-accent" style={{ fontSize: 'clamp(1.3rem, 2.6vw, 1.8rem)', fontWeight: 700, letterSpacing: '-0.02em' }}>
|
||||
{s.value}
|
||||
</span>
|
||||
<span style={{ fontSize: '0.7rem', color: '#94908a', letterSpacing: '0.22em', textTransform: 'uppercase' }}>
|
||||
{s.label}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
ref={wrapRef}
|
||||
style={{
|
||||
position: 'relative',
|
||||
perspective: '1500px',
|
||||
minHeight: 'clamp(420px, 56vw, 620px)',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
inset: '-3rem',
|
||||
background:
|
||||
`radial-gradient(ellipse 60% 50% at 50% 50%, ${robot.accent}55 0%, transparent 65%)`,
|
||||
filter: 'blur(28px)',
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
className="float-y"
|
||||
style={{
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
minHeight: 'clamp(420px, 56vw, 620px)',
|
||||
borderRadius: '1.75rem',
|
||||
background:
|
||||
'linear-gradient(180deg, rgba(20,17,11,0.7) 0%, rgba(5,5,5,0.92) 100%)',
|
||||
border: '1px solid rgba(196,162,101,0.28)',
|
||||
backdropFilter: 'blur(18px)',
|
||||
boxShadow:
|
||||
'0 30px 100px rgba(0,0,0,0.75), inset 0 1px 0 rgba(255,240,200,0.08), 0 0 60px rgba(196,162,101,0.15)',
|
||||
transform: `rotateX(${tilt.rx}deg) rotateY(${tilt.ry}deg)`,
|
||||
transition: 'transform 0.25s ease-out',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
aria-hidden
|
||||
style={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
background:
|
||||
'linear-gradient(rgba(196,162,101,0.06) 1px, transparent 1px), linear-gradient(90deg, rgba(196,162,101,0.06) 1px, transparent 1px)',
|
||||
backgroundSize: '40px 40px',
|
||||
maskImage: 'radial-gradient(ellipse 70% 70% at 50% 50%, #000 30%, transparent 80%)',
|
||||
WebkitMaskImage: 'radial-gradient(ellipse 70% 70% at 50% 50%, #000 30%, transparent 80%)',
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
aria-hidden
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: '-30%',
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
width: '70%',
|
||||
height: '60%',
|
||||
background:
|
||||
'radial-gradient(ellipse 50% 50% at 50% 30%, rgba(224,200,150,0.30) 0%, transparent 70%)',
|
||||
filter: 'blur(20px)',
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
/>
|
||||
|
||||
{FEATURED_ROBOTS.map((r, idx) => (
|
||||
<div
|
||||
key={r.id}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: 'clamp(1.5rem, 5vw, 3rem)',
|
||||
opacity: idx === activeIdx ? 1 : 0,
|
||||
transform: idx === activeIdx ? 'translateY(0) scale(1)' : 'translateY(14px) scale(0.97)',
|
||||
transition: 'opacity 0.9s ease, transform 0.9s ease',
|
||||
}}
|
||||
aria-hidden={idx !== activeIdx}
|
||||
>
|
||||
<Image
|
||||
src={r.image}
|
||||
alt={`${r.brandLabel} ${r.name}`}
|
||||
fill
|
||||
sizes="(max-width: 768px) 90vw, 620px"
|
||||
style={{ objectFit: 'contain', padding: 'clamp(1rem, 4vw, 2rem)' }}
|
||||
priority={idx === 0}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="hero-labels" aria-hidden>
|
||||
{FLOATING_LABELS.map((l) => (
|
||||
<span
|
||||
key={l.text}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: l.top,
|
||||
bottom: l.bottom,
|
||||
left: l.left,
|
||||
right: l.right,
|
||||
padding: '0.4rem 0.75rem',
|
||||
borderRadius: 999,
|
||||
background: 'rgba(10,9,7,0.7)',
|
||||
border: '1px solid rgba(196,162,101,0.3)',
|
||||
color: '#e0c896',
|
||||
fontSize: '0.66rem',
|
||||
letterSpacing: '0.22em',
|
||||
textTransform: 'uppercase',
|
||||
fontWeight: 600,
|
||||
backdropFilter: 'blur(8px)',
|
||||
animation: `floatY 6s ease-in-out ${l.delay}s infinite`,
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{l.text}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: '1.25rem',
|
||||
left: '1.25rem',
|
||||
right: '1.25rem',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: '1rem',
|
||||
flexWrap: 'wrap',
|
||||
}}
|
||||
>
|
||||
<Link
|
||||
href={`/robots/${robot.slug}/`}
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: '0.75rem',
|
||||
padding: '0.65rem 1rem',
|
||||
borderRadius: 14,
|
||||
background: 'rgba(10,9,7,0.7)',
|
||||
border: '1px solid rgba(196,162,101,0.35)',
|
||||
backdropFilter: 'blur(10px)',
|
||||
color: '#f5f1e8',
|
||||
textDecoration: 'none',
|
||||
}}
|
||||
>
|
||||
<span style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<span style={{ fontSize: '0.62rem', color: '#94908a', letterSpacing: '0.24em', textTransform: 'uppercase' }}>
|
||||
Featured · {robot.brandLabel}
|
||||
</span>
|
||||
<span style={{ fontSize: '0.95rem', fontWeight: 600 }}>{robot.name}</span>
|
||||
</span>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={robot.accent} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||
<line x1="5" y1="12" x2="19" y2="12" />
|
||||
<polyline points="12 5 19 12 12 19" />
|
||||
</svg>
|
||||
</Link>
|
||||
<div style={{ display: 'flex', gap: '0.4rem' }}>
|
||||
{FEATURED_ROBOTS.map((r, idx) => (
|
||||
<button
|
||||
key={r.id}
|
||||
type="button"
|
||||
onClick={() => setActiveIdx(idx)}
|
||||
aria-label={`Show ${r.name}`}
|
||||
style={{
|
||||
width: idx === activeIdx ? 24 : 8,
|
||||
height: 8,
|
||||
borderRadius: 999,
|
||||
border: 'none',
|
||||
background: idx === activeIdx ? r.accent : 'rgba(196,162,101,0.3)',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.4s ease',
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style jsx>{`
|
||||
@media (min-width: 1024px) {
|
||||
.hero-grid {
|
||||
grid-template-columns: 1.05fr 1fr !important;
|
||||
}
|
||||
}
|
||||
.hero-labels { display: none; }
|
||||
@media (min-width: 768px) {
|
||||
.hero-labels { display: block; }
|
||||
}
|
||||
`}</style>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user