refactor: replace RobotCategoryGrid with CategoryShowcaseScroll and update styles across components
Some checks are pending
CI/CD / test-and-build (push) Waiting to run
CI/CD / deploy (push) Blocked by required conditions

- Updated HomePage to use CategoryShowcaseScroll instead of RobotCategoryGrid for better category display.
- Modified BrandShowcase component to adjust background gradients and text colors for improved aesthetics.
- Changed CompanyStory component to enhance visual consistency with updated accent colors.
- Refined ExclusiveAccessSection to align with new design standards, including gradient adjustments.
- Enhanced Hero3DRobotics with new gradient backgrounds and updated text colors for better readability.
- Updated RoboticsScrollShowcase to reflect new color scheme and improved text visibility.
- Adjusted RoboticsSplineShowcase to maintain design consistency with updated colors and gradients.
- Improved MotionSection to enhance scroll behavior and visibility transitions.
- Updated container-scroll-animation styles for better visual effects.
- Introduced new CategoryShowcaseScroll component for a more dynamic category display experience.
This commit is contained in:
Najjar\NajjarV02 2026-05-20 18:15:43 +04:00
parent 729ab71c2c
commit 8457493f49
11 changed files with 711 additions and 39 deletions

View File

@ -15,7 +15,7 @@
--color-blue-deep: #1a2e6e; --color-blue-deep: #1a2e6e;
--color-blue-bright: #3a55c4; --color-blue-bright: #3a55c4;
--color-silver: #DEE0F0; --color-silver: #DEE0F0;
--color-silver-soft: #BFC3E2; --color-silver-soft: #D7DBEA;
--color-steel: #8891C7; --color-steel: #8891C7;
--color-graphite: #221F20; --color-graphite: #221F20;
--color-white: #FBFBFD; --color-white: #FBFBFD;
@ -23,7 +23,7 @@
/* Accent aliases (kept for legacy class names) */ /* Accent aliases (kept for legacy class names) */
--color-gold: #DEE0F0; --color-gold: #DEE0F0;
--color-accent: #273F94; --color-accent: #273F94;
--color-accent-2: #BFC3E2; --color-accent-2: #D7DBEA;
--color-accent-3: #8891C7; --color-accent-3: #8891C7;
--color-accent-hover: #3a55c4; --color-accent-hover: #3a55c4;
@ -34,14 +34,14 @@
--color-text-dim: #6a73a5; --color-text-dim: #6a73a5;
/* Borders */ /* Borders */
--color-border: rgba(191, 195, 226, 0.18); --color-border: rgba(199, 207, 230, 0.18);
--color-border-strong: rgba(191, 195, 226, 0.36); --color-border-strong: rgba(199, 207, 230, 0.36);
--color-border-light: rgba(191, 195, 226, 0.08); --color-border-light: rgba(199, 207, 230, 0.08);
--color-border-neutral: rgba(255, 255, 255, 0.08); --color-border-neutral: rgba(255, 255, 255, 0.08);
/* Glass */ /* Glass */
--color-glass-bg: rgba(28, 27, 33, 0.6); --color-glass-bg: rgba(28, 27, 33, 0.6);
--color-glass-border: rgba(191, 195, 226, 0.22); --color-glass-border: rgba(199, 207, 230, 0.22);
--color-glass-highlight: rgba(222, 224, 240, 0.06); --color-glass-highlight: rgba(222, 224, 240, 0.06);
/* Spacing */ /* Spacing */
@ -129,7 +129,7 @@ html {
/* === Typography gradients (metallic silver-blue) === */ /* === Typography gradients (metallic silver-blue) === */
.text-gradient { .text-gradient {
background: linear-gradient(135deg, #FBFBFD 0%, #DEE0F0 45%, #8891C7 100%); background: linear-gradient(135deg, #FFFFFF 0%, #DEE0F0 50%, #8891C7 100%);
-webkit-background-clip: text; -webkit-background-clip: text;
background-clip: text; background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
@ -137,7 +137,7 @@ html {
} }
.text-gradient-accent { .text-gradient-accent {
background: linear-gradient(135deg, #FBFBFD 0%, #DEE0F0 35%, #BFC3E2 65%, #273F94 100%); background: linear-gradient(135deg, #FFFFFF 0%, #DEE0F0 50%, #8891C7 100%);
-webkit-background-clip: text; -webkit-background-clip: text;
background-clip: text; background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
@ -186,8 +186,8 @@ html {
position: absolute; position: absolute;
inset: 0; inset: 0;
background-image: background-image:
linear-gradient(rgba(191, 195, 226, 0.05) 1px, transparent 1px), linear-gradient(rgba(199, 207, 230, 0.05) 1px, transparent 1px),
linear-gradient(90deg, rgba(191, 195, 226, 0.05) 1px, transparent 1px); linear-gradient(90deg, rgba(199, 207, 230, 0.05) 1px, transparent 1px);
background-size: 48px 48px; background-size: 48px 48px;
mask-image: radial-gradient(ellipse 60% 50% at 50% 40%, #000 30%, transparent 80%); mask-image: radial-gradient(ellipse 60% 50% at 50% 40%, #000 30%, transparent 80%);
-webkit-mask-image: radial-gradient(ellipse 60% 50% at 50% 40%, #000 30%, transparent 80%); -webkit-mask-image: radial-gradient(ellipse 60% 50% at 50% 40%, #000 30%, transparent 80%);
@ -213,7 +213,7 @@ html {
position: absolute; position: absolute;
inset: 0; inset: 0;
pointer-events: none; pointer-events: none;
background: linear-gradient(110deg, transparent 30%, rgba(191, 195, 226, 0.07) 50%, transparent 70%); background: linear-gradient(110deg, transparent 30%, rgba(199, 207, 230, 0.07) 50%, transparent 70%);
background-size: 250% 100%; background-size: 250% 100%;
animation: lightSweep 9s ease-in-out infinite; animation: lightSweep 9s ease-in-out infinite;
mix-blend-mode: screen; mix-blend-mode: screen;
@ -297,7 +297,7 @@ html {
.btn-ghost { .btn-ghost {
background: rgba(222, 224, 240, 0.04); background: rgba(222, 224, 240, 0.04);
color: #FBFBFD; color: #FBFBFD;
border-color: rgba(191, 195, 226, 0.28); border-color: rgba(199, 207, 230, 0.28);
backdrop-filter: blur(12px); backdrop-filter: blur(12px);
} }
.btn-ghost:hover { .btn-ghost:hover {

View File

@ -3,7 +3,7 @@ import { Navbar } from '@/components/Navbar';
import { FooterAndContact } from '@/components/FooterAndContact'; import { FooterAndContact } from '@/components/FooterAndContact';
import { Hero3DRobotics } from '@/components/robotics/Hero3DRobotics'; import { Hero3DRobotics } from '@/components/robotics/Hero3DRobotics';
import { BrandShowcase } from '@/components/robotics/BrandShowcase'; import { BrandShowcase } from '@/components/robotics/BrandShowcase';
import { RobotCategoryGrid } from '@/components/robotics/RobotCategoryGrid'; import { CategoryShowcaseScroll } from '@/components/robotics/CategoryShowcaseScroll';
import { RobotProductCard } from '@/components/robotics/RobotProductCard'; import { RobotProductCard } from '@/components/robotics/RobotProductCard';
import { IndustryUseCases } from '@/components/robotics/IndustryUseCases'; import { IndustryUseCases } from '@/components/robotics/IndustryUseCases';
import { DemoCTA } from '@/components/robotics/DemoCTA'; import { DemoCTA } from '@/components/robotics/DemoCTA';
@ -88,9 +88,9 @@ export default function HomePage() {
<SectionHeading <SectionHeading
eyebrow="Robot categories" eyebrow="Robot categories"
title="From quadruped patrol to humanoid concierge." title="From quadruped patrol to humanoid concierge."
description="Find the right robot for your business humanoid for events and education, quadruped for inspection and security, service and delivery for hospitality." description="Find the right robot for your business — humanoid platforms for events and education, quadrupeds for inspection and security, and service robots for hospitality, delivery, and cleaning."
/> />
<RobotCategoryGrid /> <CategoryShowcaseScroll />
</div> </div>
</MotionSection> </MotionSection>

View File

@ -107,7 +107,7 @@ function BrandCard({
position: 'absolute', position: 'absolute',
inset: 0, inset: 0,
backgroundImage: backgroundImage:
'linear-gradient(rgba(191,195,226,0.04) 1px, transparent 1px), linear-gradient(90deg, rgba(191,195,226,0.04) 1px, transparent 1px)', 'linear-gradient(rgba(199, 207, 230,0.04) 1px, transparent 1px), linear-gradient(90deg, rgba(199, 207, 230,0.04) 1px, transparent 1px)',
backgroundSize: '32px 32px', backgroundSize: '32px 32px',
maskImage: 'radial-gradient(ellipse 60% 80% at 50% 40%, #000 30%, transparent 80%)', maskImage: 'radial-gradient(ellipse 60% 80% at 50% 40%, #000 30%, transparent 80%)',
WebkitMaskImage: 'radial-gradient(ellipse 60% 80% at 50% 40%, #000 30%, transparent 80%)', WebkitMaskImage: 'radial-gradient(ellipse 60% 80% at 50% 40%, #000 30%, transparent 80%)',
@ -198,7 +198,7 @@ function BrandCard({
<p <p
style={{ style={{
margin: 0, margin: 0,
color: '#BFC3E2', color: '#D7DBEA',
fontSize: '0.92rem', fontSize: '0.92rem',
lineHeight: 1.55, lineHeight: 1.55,
}} }}
@ -274,7 +274,7 @@ function BrandCard({
position: 'absolute', position: 'absolute',
inset: 0, inset: 0,
backgroundImage: backgroundImage:
'linear-gradient(rgba(191,195,226,0.06) 1px, transparent 1px), linear-gradient(90deg, rgba(191,195,226,0.06) 1px, transparent 1px)', 'linear-gradient(rgba(199, 207, 230,0.06) 1px, transparent 1px), linear-gradient(90deg, rgba(199, 207, 230,0.06) 1px, transparent 1px)',
backgroundSize: '24px 24px', backgroundSize: '24px 24px',
maskImage: 'radial-gradient(ellipse 60% 60% at 50% 50%, #000 30%, transparent 80%)', maskImage: 'radial-gradient(ellipse 60% 60% at 50% 50%, #000 30%, transparent 80%)',
WebkitMaskImage: 'radial-gradient(ellipse 60% 60% at 50% 50%, #000 30%, transparent 80%)', WebkitMaskImage: 'radial-gradient(ellipse 60% 60% at 50% 50%, #000 30%, transparent 80%)',

View File

@ -0,0 +1,670 @@
'use client';
import { useRef, useEffect, useState } from 'react';
import Image from 'next/image';
import Link from 'next/link';
import { motion, useScroll, useTransform } from 'framer-motion';
import { ArrowRight, Bot, Footprints, Coffee, Truck, Sparkles, Hotel, Search, Factory } from 'lucide-react';
import type { LucideIcon } from 'lucide-react';
type Category = {
id: string;
name: string;
brand: 'Unitree' | 'Pudu Robotics' | 'Unitree + Pudu';
brandAccent: string;
models: string;
description: string;
useCases: string[];
image: string;
imageAlt: string;
href: string;
Icon: LucideIcon;
};
const CATEGORIES: Category[] = [
{
id: 'humanoid',
name: 'Humanoid Robots',
brand: 'Unitree',
brandAccent: '#DEE0F0',
models: 'G1 · H2 · R1',
description:
'Bipedal humanoid platforms for events, education, research, and customer-facing experiences across the UAE.',
useCases: ['Events & activations', 'Education & STEM', 'Research labs', 'Concierge & reception'],
image: '/images/robots/unitree-g1.png',
imageAlt: 'Unitree G1 humanoid robot',
href: '/robots/?category=humanoid',
Icon: Bot,
},
{
id: 'quadruped',
name: 'Quadruped Robots',
brand: 'Unitree',
brandAccent: '#DEE0F0',
models: 'Go2 · B2 · A2',
description:
'Agile four-legged robots built for inspection, security patrol, and field mobility in any terrain.',
useCases: ['Facility inspection', 'Security patrol', 'Terrain mobility', 'Robotics research'],
image: '/images/robots/unitree-go2.png',
imageAlt: 'Unitree Go2 quadruped robot',
href: '/robots/?category=quadruped',
Icon: Footprints,
},
{
id: 'service',
name: 'Service Robots',
brand: 'Pudu Robotics',
brandAccent: '#8891C7',
models: 'KettyBot · BellaBot',
description:
'Greeting, guiding, and customer-interaction robots designed for restaurants, hotels, and retail venues.',
useCases: ['Restaurant greeting', 'Customer guidance', 'Retail activation', 'Hospitality service'],
image: '/images/robots/pudu-kettybot.svg',
imageAlt: 'Pudu KettyBot service robot',
href: '/robots/?category=service',
Icon: Coffee,
},
{
id: 'delivery',
name: 'Delivery Robots',
brand: 'Pudu Robotics',
brandAccent: '#8891C7',
models: 'BellaBot · D-Series',
description:
'Multi-tray autonomous delivery robots for hotels, restaurants, hospitals, and back-of-house operations.',
useCases: ['Hotel room service', 'Restaurant delivery', 'Hospital logistics', 'Back-of-house transport'],
image: '/images/robots/pudu-bellabot.svg',
imageAlt: 'Pudu BellaBot delivery robot',
href: '/robots/?category=delivery',
Icon: Truck,
},
{
id: 'cleaning',
name: 'Cleaning Robots',
brand: 'Pudu Robotics',
brandAccent: '#8891C7',
models: 'CC1 · Commercial Cleaning',
description:
'Autonomous commercial cleaning platforms that sweep, scrub, mop, and vacuum across large venues.',
useCases: ['Shopping malls', 'Airports & transit', 'Hotels & resorts', 'Corporate offices'],
image: '/images/robots/pudu-cc1.svg',
imageAlt: 'Pudu CC1 cleaning robot',
href: '/robots/?category=cleaning',
Icon: Sparkles,
},
{
id: 'hospitality',
name: 'Hospitality Robots',
brand: 'Pudu Robotics',
brandAccent: '#8891C7',
models: 'BellaBot · KettyBot',
description:
'Premium guest-facing robots for hotels, resorts, and events — designed to enhance every guest moment.',
useCases: ['Hotel concierge', 'Resort service', 'Event activations', 'Guest experience'],
image: '/images/robots/pudu-bellabot.svg',
imageAlt: 'Pudu BellaBot hospitality robot',
href: '/robots/?category=hospitality',
Icon: Hotel,
},
{
id: 'inspection',
name: 'Inspection Robots',
brand: 'Unitree',
brandAccent: '#DEE0F0',
models: 'Go2 · B2 · AS2',
description:
'Quadruped inspection platforms for energy, utilities, and industrial sites — equipped with rich sensor payloads.',
useCases: ['Energy & utilities', 'Industrial sites', 'Infrastructure', 'Security patrols'],
image: '/images/robots/unitree-b2.png',
imageAlt: 'Unitree B2 industrial quadruped robot',
href: '/robots/?category=inspection',
Icon: Search,
},
{
id: 'commercial',
name: 'Commercial Automation',
brand: 'Unitree + Pudu',
brandAccent: '#3a55c4',
models: 'Multi-brand portfolio',
description:
'Mixed robotics fleets for warehouses, smart buildings, and enterprise operations across the UAE.',
useCases: ['Warehouses', 'Smart buildings', 'Enterprise venues', 'Mixed-fleet deployments'],
image: '/images/robots/unitree-as2.png',
imageAlt: 'Unitree AS2 autonomous system for commercial automation',
href: '/robots/?category=commercial',
Icon: Factory,
},
];
export function CategoryShowcaseScroll() {
const [isMobile, setIsMobile] = useState(false);
useEffect(() => {
const check = () => setIsMobile(window.innerWidth < 900);
check();
window.addEventListener('resize', check);
return () => window.removeEventListener('resize', check);
}, []);
if (isMobile) return <CategoryStack />;
return <CategorySticky />;
}
function CategorySticky() {
const containerRef = useRef<HTMLDivElement>(null);
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ['start start', 'end end'],
});
const [activeIdx, setActiveIdx] = useState(0);
useEffect(() => {
return scrollYProgress.on('change', (v) => {
const idx = Math.min(
CATEGORIES.length - 1,
Math.max(0, Math.floor(v * CATEGORIES.length))
);
setActiveIdx(idx);
});
}, [scrollYProgress]);
/* progress bar height */
const progressHeight = useTransform(scrollYProgress, [0, 1], ['0%', '100%']);
return (
<div
ref={containerRef}
style={{
position: 'relative',
/* 65vh per category — total ~520vh feels responsive, not endless */
height: `${CATEGORIES.length * 65}vh`,
}}
>
<div
style={{
position: 'sticky',
top: '5.5rem',
/* fill the viewport — no empty area below the panel during sticky */
height: 'calc(100svh - 6.5rem)',
maxHeight: '780px',
display: 'grid',
gridTemplateColumns: 'minmax(0, 0.95fr) minmax(0, 1.05fr)',
gap: 'clamp(1.5rem, 3vw, 2.5rem)',
alignItems: 'stretch',
}}
>
{/* LEFT — sticky text column with progress + active category */}
<div style={{ position: 'relative', display: 'flex', flexDirection: 'column', justifyContent: 'space-between', padding: '0.25rem 0', minWidth: 0 }}>
{/* progress rail */}
<div
style={{
position: 'absolute',
left: 0,
top: 0,
bottom: 0,
width: 2,
background: 'rgba(136,145,199,0.15)',
borderRadius: 1,
overflow: 'hidden',
}}
aria-hidden
>
<motion.div
style={{
width: '100%',
height: progressHeight,
background: 'linear-gradient(180deg, #DEE0F0, #3a55c4)',
boxShadow: '0 0 12px rgba(58,85,196,0.6)',
}}
/>
</div>
{/* category list */}
<div style={{ paddingLeft: '1.25rem', display: 'flex', flexDirection: 'column', gap: '0.4rem', maxHeight: '32vh', overflow: 'hidden' }}>
<span className="eyebrow" style={{ marginBottom: '0.5rem' }}>Robot categories · 8</span>
{CATEGORIES.map((c, i) => (
<button
key={c.id}
type="button"
onClick={() => {
const el = containerRef.current;
if (!el) return;
const rect = el.getBoundingClientRect();
const targetY = window.scrollY + rect.top + (rect.height * (i + 0.5)) / CATEGORIES.length;
window.scrollTo({ top: targetY - window.innerHeight / 2, behavior: 'smooth' });
}}
style={{
display: 'flex',
alignItems: 'center',
gap: '0.55rem',
padding: '0.4rem 0',
background: 'transparent',
border: 'none',
cursor: 'pointer',
textAlign: 'left',
color: i === activeIdx ? '#FBFBFD' : '#6a73a5',
fontSize: i === activeIdx ? '1rem' : '0.85rem',
fontWeight: i === activeIdx ? 600 : 500,
letterSpacing: '-0.005em',
transition: 'color 0.3s, font-size 0.3s',
}}
>
<span
aria-hidden
style={{
width: i === activeIdx ? 18 : 8,
height: 1,
background: i === activeIdx ? '#DEE0F0' : '#3a3d52',
transition: 'width 0.3s, background 0.3s',
}}
/>
<span style={{ fontSize: '0.65rem', letterSpacing: '0.18em', color: '#6a73a5' }}>
{String(i + 1).padStart(2, '0')}
</span>
{c.name}
</button>
))}
</div>
{/* active category copy */}
<div style={{ paddingLeft: '1.25rem', marginTop: '1rem', display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<motion.h2
key={`title-${activeIdx}`}
initial={{ opacity: 0, y: 14 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.45, ease: [0.16, 1, 0.3, 1] }}
style={{
margin: 0,
fontSize: 'clamp(1.6rem, 3.4vw, 2.4rem)',
fontWeight: 300,
lineHeight: 1.1,
letterSpacing: '-0.03em',
}}
>
<span className="text-gradient" style={{ fontWeight: 500 }}>
{CATEGORIES[activeIdx].name}
</span>
</motion.h2>
<motion.p
key={`desc-${activeIdx}`}
initial={{ opacity: 0, y: 14 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.45, delay: 0.05, ease: [0.16, 1, 0.3, 1] }}
style={{
margin: 0,
color: '#DEE0F0',
fontSize: 'clamp(0.92rem, 1.7vw, 1rem)',
lineHeight: 1.65,
maxWidth: 500,
}}
>
{CATEGORIES[activeIdx].description}
</motion.p>
<motion.div
key={`cta-${activeIdx}`}
initial={{ opacity: 0, y: 14 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.45, delay: 0.12, ease: [0.16, 1, 0.3, 1] }}
style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem' }}
>
<Link
href={CATEGORIES[activeIdx].href}
style={{
display: 'inline-flex',
alignItems: 'center',
gap: '0.4rem',
padding: '0.7rem 1.2rem',
borderRadius: 999,
background: 'linear-gradient(135deg, #3a55c4 0%, #273F94 55%, #1a2e6e 100%)',
color: '#FBFBFD',
fontSize: '0.72rem',
fontWeight: 700,
letterSpacing: '0.16em',
textTransform: 'uppercase',
textDecoration: 'none',
boxShadow: '0 12px 32px rgba(39,63,148,0.45)',
}}
>
Explore robots
<ArrowRight size={13} />
</Link>
<Link
href="/book-demo/"
style={{
display: 'inline-flex',
alignItems: 'center',
gap: '0.4rem',
padding: '0.7rem 1.2rem',
borderRadius: 999,
border: '1px solid rgba(222,224,240,0.28)',
background: 'rgba(255,255,255,0.04)',
color: '#DEE0F0',
fontSize: '0.72rem',
fontWeight: 700,
letterSpacing: '0.16em',
textTransform: 'uppercase',
textDecoration: 'none',
}}
>
Book demo
<ArrowRight size={13} />
</Link>
</motion.div>
</div>
</div>
{/* RIGHT — visual stage */}
<div
style={{
position: 'relative',
borderRadius: 24,
overflow: 'hidden',
background:
`radial-gradient(ellipse 70% 50% at 50% 60%, rgba(58,85,196,0.28) 0%, transparent 65%), linear-gradient(180deg, rgba(28,27,33,0.85), rgba(8,8,10,0.97))`,
border: '1px solid rgba(222,224,240,0.14)',
boxShadow: '0 30px 90px rgba(0,0,0,0.7), inset 0 1px 0 rgba(222,224,240,0.06)',
}}
>
{/* metallic edge */}
<div
aria-hidden
style={{
position: 'absolute',
inset: 0,
borderRadius: 24,
padding: 1,
background:
'linear-gradient(135deg, rgba(222,224,240,0.32), transparent 45%, rgba(58,85,196,0.22))',
WebkitMask: 'linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0)',
WebkitMaskComposite: 'xor',
maskComposite: 'exclude',
pointerEvents: 'none',
}}
/>
{/* grid */}
<div
aria-hidden
style={{
position: 'absolute',
inset: 0,
backgroundImage:
'linear-gradient(rgba(199, 207, 230,0.06) 1px, transparent 1px), linear-gradient(90deg, rgba(199, 207, 230,0.06) 1px, transparent 1px)',
backgroundSize: '36px 36px',
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',
}}
/>
{/* spotlight */}
<div
aria-hidden
style={{
position: 'absolute',
top: '5%',
left: '50%',
transform: 'translateX(-50%)',
width: '70%',
height: '70%',
background:
'radial-gradient(ellipse 60% 70% at 50% 35%, rgba(58,85,196,0.45) 0%, transparent 65%)',
filter: 'blur(14px)',
pointerEvents: 'none',
}}
/>
{/* top floating brand chip */}
<div
style={{
position: 'absolute',
top: '1.25rem',
left: '1.25rem',
zIndex: 5,
display: 'flex',
alignItems: 'center',
gap: '0.5rem',
padding: '0.4rem 0.85rem',
borderRadius: 999,
background: 'rgba(14,13,18,0.7)',
border: `1px solid ${CATEGORIES[activeIdx].brandAccent}55`,
color: CATEGORIES[activeIdx].brandAccent,
fontSize: '0.65rem',
fontWeight: 800,
letterSpacing: '0.24em',
textTransform: 'uppercase',
backdropFilter: 'blur(8px)',
transition: 'border-color 0.4s, color 0.4s',
}}
>
<span style={{ width: 6, height: 6, borderRadius: 999, background: CATEGORIES[activeIdx].brandAccent }} />
{CATEGORIES[activeIdx].brand}
</div>
{/* top-right index */}
<div
style={{
position: 'absolute',
top: '1.25rem',
right: '1.25rem',
zIndex: 5,
fontSize: 'clamp(2rem, 4vw, 3rem)',
fontWeight: 800,
color: 'transparent',
WebkitTextStroke: '1px rgba(222,224,240,0.32)',
letterSpacing: '-0.04em',
lineHeight: 1,
}}
>
{String(activeIdx + 1).padStart(2, '0')}
<span style={{ color: '#6a73a5', WebkitTextStroke: 0, fontSize: '0.7rem', letterSpacing: '0.2em', marginLeft: 4 }}>
/ {String(CATEGORIES.length).padStart(2, '0')}
</span>
</div>
{/* product stage — cross-fade per category */}
<div style={{ position: 'absolute', inset: 0 }}>
{CATEGORIES.map((c, i) => (
<motion.div
key={c.id}
aria-hidden={i !== activeIdx}
initial={false}
animate={{
opacity: i === activeIdx ? 1 : 0,
scale: i === activeIdx ? 1 : 0.94,
filter: i === activeIdx ? 'blur(0px)' : 'blur(6px)',
}}
transition={{ duration: 0.7, ease: [0.16, 1, 0.3, 1] }}
style={{
position: 'absolute',
inset: 0,
display: 'flex',
alignItems: 'flex-end',
justifyContent: 'center',
padding: 'clamp(1.5rem, 4vw, 3rem)',
paddingBottom: '32%',
}}
>
<div
style={{
position: 'relative',
width: '100%',
height: '100%',
filter:
'drop-shadow(0 32px 50px rgba(0,0,0,0.78)) drop-shadow(0 0 32px rgba(58,85,196,0.45))',
}}
>
<Image
src={c.image}
alt={c.imageAlt}
fill
sizes="(max-width: 768px) 90vw, 620px"
style={{ objectFit: 'contain', objectPosition: 'center bottom' }}
priority={i === 0}
/>
</div>
</motion.div>
))}
</div>
{/* floor line */}
<div
aria-hidden
style={{
position: 'absolute',
left: '6%',
right: '6%',
bottom: '32%',
height: 1,
background: 'linear-gradient(90deg, transparent, rgba(222,224,240,0.55), transparent)',
pointerEvents: 'none',
}}
/>
{/* floor reflection */}
{CATEGORIES.map((c, i) => (
<motion.div
key={`r-${c.id}`}
aria-hidden
initial={false}
animate={{ opacity: i === activeIdx ? 0.3 : 0 }}
transition={{ duration: 0.7 }}
style={{
position: 'absolute',
left: 'clamp(1.5rem, 4vw, 3rem)',
right: 'clamp(1.5rem, 4vw, 3rem)',
bottom: '6%',
height: '22%',
transform: 'scaleY(-1)',
transformOrigin: 'center top',
maskImage: 'linear-gradient(to bottom, rgba(0,0,0,0.8), transparent 78%)',
WebkitMaskImage: 'linear-gradient(to bottom, rgba(0,0,0,0.8), transparent 78%)',
pointerEvents: 'none',
filter: 'blur(1.4px)',
}}
>
<Image
src={c.image}
alt=""
fill
sizes="(max-width: 768px) 90vw, 620px"
style={{ objectFit: 'contain', objectPosition: 'center top' }}
/>
</motion.div>
))}
{/* bottom info bar: models + use cases */}
<motion.div
key={`info-${activeIdx}`}
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.08, ease: [0.16, 1, 0.3, 1] }}
style={{
position: 'absolute',
bottom: '1rem',
left: '1rem',
right: '1rem',
zIndex: 5,
padding: '0.875rem 1.1rem',
borderRadius: 16,
background: 'rgba(14,13,18,0.78)',
border: '1px solid rgba(222,224,240,0.16)',
backdropFilter: 'blur(14px)',
display: 'flex',
flexDirection: 'column',
gap: '0.55rem',
}}
>
<div style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', justifyContent: 'space-between', gap: '0.5rem' }}>
<span style={{ fontSize: '0.62rem', color: '#8891C7', letterSpacing: '0.24em', textTransform: 'uppercase', fontWeight: 700 }}>
Featured models
</span>
<span style={{ fontSize: '0.85rem', color: '#FBFBFD', fontWeight: 600 }}>
{CATEGORIES[activeIdx].models}
</span>
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.35rem' }}>
{CATEGORIES[activeIdx].useCases.map((u) => (
<span
key={u}
style={{
fontSize: '0.66rem',
padding: '0.22rem 0.55rem',
borderRadius: 999,
background: 'rgba(255,255,255,0.04)',
border: '1px solid rgba(222,224,240,0.18)',
color: '#D7DBEA',
letterSpacing: '0.04em',
}}
>
{u}
</span>
))}
</div>
</motion.div>
</div>
</div>
</div>
);
}
function CategoryStack() {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
{CATEGORIES.map((c, i) => (
<Link
key={c.id}
href={c.href}
style={{
position: 'relative',
display: 'grid',
gridTemplateColumns: 'minmax(0, 1fr) 130px',
gap: '0.75rem',
padding: '1rem',
borderRadius: 18,
background:
`radial-gradient(ellipse 70% 80% at 100% 0%, ${c.brandAccent}1F, transparent 60%), linear-gradient(135deg, rgba(28,27,33,0.85), rgba(8,8,10,0.96))`,
border: `1px solid ${c.brandAccent}33`,
color: '#FBFBFD',
textDecoration: 'none',
overflow: 'hidden',
minHeight: 150,
}}
>
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem', minWidth: 0 }}>
<span style={{ fontSize: '0.6rem', letterSpacing: '0.22em', textTransform: 'uppercase', color: '#8891C7', fontWeight: 700 }}>
{String(i + 1).padStart(2, '0')} · {c.brand}
</span>
<h3 style={{ margin: 0, fontSize: '1.05rem', fontWeight: 700, letterSpacing: '-0.01em' }}>
{c.name}
</h3>
<p style={{ margin: 0, color: '#D7DBEA', fontSize: '0.82rem', lineHeight: 1.5 }}>
{c.description}
</p>
<span style={{ marginTop: 'auto', display: 'inline-flex', alignItems: 'center', gap: '0.35rem', color: c.brandAccent, fontSize: '0.68rem', letterSpacing: '0.18em', textTransform: 'uppercase', fontWeight: 700 }}>
Explore
<ArrowRight size={12} />
</span>
</div>
<div
style={{
position: 'relative',
borderRadius: 12,
overflow: 'hidden',
background:
`radial-gradient(ellipse 70% 50% at 50% 60%, ${c.brandAccent}22 0%, transparent 65%), linear-gradient(180deg, rgba(28,27,33,0.7), rgba(10,10,12,0.95))`,
border: '1px solid rgba(222,224,240,0.08)',
}}
>
<Image
src={c.image}
alt={c.imageAlt}
fill
sizes="130px"
style={{ objectFit: 'contain', padding: '0.5rem' }}
/>
</div>
</Link>
))}
</div>
);
}

View File

@ -31,7 +31,7 @@ const PILLARS: Pillar[] = [
title: 'Digital Transformation', title: 'Digital Transformation',
body: 'Automation systems and smart projects that modernize operations.', body: 'Automation systems and smart projects that modernize operations.',
Icon: Workflow, Icon: Workflow,
accent: '#BFC3E2', accent: '#D7DBEA',
}, },
{ {
num: '04', num: '04',
@ -63,7 +63,7 @@ export function CompanyStory() {
position: 'absolute', position: 'absolute',
inset: 0, inset: 0,
backgroundImage: backgroundImage:
'linear-gradient(rgba(191,195,226,0.06) 1px, transparent 1px), linear-gradient(90deg, rgba(191,195,226,0.06) 1px, transparent 1px)', 'linear-gradient(rgba(199, 207, 230,0.06) 1px, transparent 1px), linear-gradient(90deg, rgba(199, 207, 230,0.06) 1px, transparent 1px)',
backgroundSize: '36px 36px', backgroundSize: '36px 36px',
maskImage: 'radial-gradient(ellipse 70% 80% at 50% 40%, #000 25%, transparent 80%)', maskImage: 'radial-gradient(ellipse 70% 80% at 50% 40%, #000 25%, transparent 80%)',
WebkitMaskImage: 'radial-gradient(ellipse 70% 80% at 50% 40%, #000 25%, transparent 80%)', WebkitMaskImage: 'radial-gradient(ellipse 70% 80% at 50% 40%, #000 25%, transparent 80%)',
@ -348,7 +348,7 @@ function CapabilityTile({ pillar, offset }: { pillar: Pillar; offset: 'up' | 'do
margin: 0, margin: 0,
fontSize: '0.88rem', fontSize: '0.88rem',
lineHeight: 1.55, lineHeight: 1.55,
color: '#BFC3E2', color: '#D7DBEA',
position: 'relative', position: 'relative',
}} }}
> >

View File

@ -189,7 +189,7 @@ function BrandShowcaseTile({
position: 'absolute', position: 'absolute',
inset: 0, inset: 0,
backgroundImage: backgroundImage:
'linear-gradient(rgba(191,195,226,0.06) 1px, transparent 1px), linear-gradient(90deg, rgba(191,195,226,0.06) 1px, transparent 1px)', 'linear-gradient(rgba(199, 207, 230,0.06) 1px, transparent 1px), linear-gradient(90deg, rgba(199, 207, 230,0.06) 1px, transparent 1px)',
backgroundSize: '24px 24px', backgroundSize: '24px 24px',
maskImage: 'radial-gradient(ellipse 60% 60% at 50% 50%, #000 30%, transparent 80%)', maskImage: 'radial-gradient(ellipse 60% 60% at 50% 50%, #000 30%, transparent 80%)',
WebkitMaskImage: 'radial-gradient(ellipse 60% 60% at 50% 50%, #000 30%, transparent 80%)', WebkitMaskImage: 'radial-gradient(ellipse 60% 60% at 50% 50%, #000 30%, transparent 80%)',
@ -334,7 +334,7 @@ function TerritoryTile() {
<circle cx="155" cy="55" r="32" fill="none" stroke="#DEE0F0" strokeOpacity="0.45" strokeDasharray="2 4" /> <circle cx="155" cy="55" r="32" fill="none" stroke="#DEE0F0" strokeOpacity="0.45" strokeDasharray="2 4" />
<circle cx="155" cy="55" r="22" fill="none" stroke="#DEE0F0" strokeOpacity="0.65" /> <circle cx="155" cy="55" r="22" fill="none" stroke="#DEE0F0" strokeOpacity="0.65" />
<circle cx="155" cy="55" r="3.5" fill="#DEE0F0" /> <circle cx="155" cy="55" r="3.5" fill="#DEE0F0" />
<text x="160" y="44" fontFamily="Inter, sans-serif" fontSize="8" letterSpacing="3" fill="#BFC3E2" textAnchor="start">DXB</text> <text x="160" y="44" fontFamily="Inter, sans-serif" fontSize="8" letterSpacing="3" fill="#D7DBEA" textAnchor="start">DXB</text>
</svg> </svg>
<div style={{ position: 'relative', display: 'flex', flexDirection: 'column', gap: 4 }}> <div style={{ position: 'relative', display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ fontSize: '0.62rem', color: '#8891C7', letterSpacing: '0.22em', textTransform: 'uppercase', fontWeight: 700 }}> <span style={{ fontSize: '0.62rem', color: '#8891C7', letterSpacing: '0.22em', textTransform: 'uppercase', fontWeight: 700 }}>

View File

@ -141,7 +141,7 @@ export function Hero3DRobotics() {
display: 'block', display: 'block',
fontWeight: 600, fontWeight: 600,
background: background:
'linear-gradient(110deg, #FBFBFD 0%, #DEE0F0 28%, #BFC3E2 55%, #3a55c4 100%)', 'linear-gradient(110deg, #FFFFFF 0%, #DEE0F0 50%, #8891C7 100%)',
WebkitBackgroundClip: 'text', WebkitBackgroundClip: 'text',
backgroundClip: 'text', backgroundClip: 'text',
WebkitTextFillColor: 'transparent', WebkitTextFillColor: 'transparent',
@ -157,7 +157,7 @@ export function Hero3DRobotics() {
<p style={{ margin: 0, color: '#DEE0F0', fontSize: 'clamp(1rem, 2vw, 1.18rem)', lineHeight: 1.7, maxWidth: 600, fontWeight: 300 }}> <p style={{ margin: 0, color: '#DEE0F0', 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. 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> </p>
<p style={{ margin: 0, color: '#BFC3E2', fontSize: '0.78rem', letterSpacing: '0.32em', textTransform: 'uppercase', fontWeight: 600 }}> <p style={{ margin: 0, color: '#D7DBEA', fontSize: '0.78rem', letterSpacing: '0.32em', textTransform: 'uppercase', fontWeight: 600 }}>
In Tech We Innovate · In Trust We Lead In Tech We Innovate · In Trust We Lead
</p> </p>

View File

@ -18,7 +18,7 @@ const SHOWROOM_ROBOTS = [
slug: 'unitree-go2', slug: 'unitree-go2',
image: '/images/robots/unitree-go2.png', image: '/images/robots/unitree-go2.png',
category: 'Quadruped', category: 'Quadruped',
accent: '#BFC3E2', accent: '#D7DBEA',
}, },
{ {
name: 'Pudu BellaBot', name: 'Pudu BellaBot',
@ -58,10 +58,10 @@ export function RoboticsScrollShowcase() {
</span> </span>
<h2 className="text-4xl font-light leading-[1.05] tracking-[-0.035em] text-white sm:text-5xl md:text-6xl lg:text-7xl"> <h2 className="text-4xl font-light leading-[1.05] tracking-[-0.035em] text-white sm:text-5xl md:text-6xl lg:text-7xl">
Advanced robotics. Advanced robotics.
<span className="block bg-gradient-to-r from-white via-[#DEE0F0] to-[#3a55c4] bg-clip-text font-semibold text-transparent"> <span className="block bg-gradient-to-r from-white via-[#DEE0F0] to-[#8891C7] bg-clip-text font-semibold text-transparent">
Exclusive UAE access. Exclusive UAE access.
</span> </span>
<span className="mt-3 block text-base font-normal text-[#BFC3E2] sm:text-lg md:text-xl"> <span className="mt-3 block text-base font-normal text-[#D7DBEA] sm:text-lg md:text-xl">
Built for Dubai&apos;s next generation of automation. Built for Dubai&apos;s next generation of automation.
</span> </span>
</h2> </h2>
@ -92,14 +92,14 @@ function ConsoleInterior() {
className={`flex items-center gap-2.5 rounded-lg border px-3 py-2 text-left text-xs font-semibold uppercase tracking-[0.12em] transition ${ className={`flex items-center gap-2.5 rounded-lg border px-3 py-2 text-left text-xs font-semibold uppercase tracking-[0.12em] transition ${
t.active t.active
? 'border-[#DEE0F0]/40 bg-[#273F94]/30 text-[#DEE0F0]' ? 'border-[#DEE0F0]/40 bg-[#273F94]/30 text-[#DEE0F0]'
: 'border-transparent text-[#BFC3E2]/80 hover:bg-white/[0.04]' : 'border-transparent text-[#D7DBEA]/80 hover:bg-white/[0.04]'
}`} }`}
> >
<t.icon className="h-3.5 w-3.5" /> <t.icon className="h-3.5 w-3.5" />
{t.label} {t.label}
</button> </button>
))} ))}
<div className="mt-auto rounded-xl border border-[#DEE0F0]/10 bg-[#221F20]/60 p-3 text-[10px] uppercase tracking-[0.18em] text-[#BFC3E2]/70"> <div className="mt-auto rounded-xl border border-[#DEE0F0]/10 bg-[#221F20]/60 p-3 text-[10px] uppercase tracking-[0.18em] text-[#D7DBEA]/70">
<span className="block text-[#DEE0F0]">YS Lootah Robotics</span> <span className="block text-[#DEE0F0]">YS Lootah Robotics</span>
In Tech We Innovate In Tech We Innovate
</div> </div>
@ -169,7 +169,7 @@ function ConsoleInterior() {
</Link> </Link>
<Link <Link
href="/contact/" href="/contact/"
className="inline-flex items-center gap-1.5 rounded-full border border-[#DEE0F0]/30 px-3.5 py-2 text-[10px] font-bold uppercase tracking-[0.16em] text-[#BFC3E2] md:text-[11px]" className="inline-flex items-center gap-1.5 rounded-full border border-[#DEE0F0]/30 px-3.5 py-2 text-[10px] font-bold uppercase tracking-[0.16em] text-[#D7DBEA] md:text-[11px]"
> >
Request UAE quotation Request UAE quotation
</Link> </Link>

View File

@ -99,7 +99,7 @@ export function RoboticsSplineShowcase() {
style={{ style={{
display: 'inline-block', display: 'inline-block',
background: background:
'linear-gradient(110deg, #FBFBFD 0%, #DEE0F0 35%, #BFC3E2 60%, #3a55c4 100%)', 'linear-gradient(110deg, #FFFFFF 0%, #DEE0F0 50%, #8891C7 100%)',
WebkitBackgroundClip: 'text', WebkitBackgroundClip: 'text',
backgroundClip: 'text', backgroundClip: 'text',
WebkitTextFillColor: 'transparent', WebkitTextFillColor: 'transparent',
@ -190,7 +190,7 @@ export function RoboticsSplineShowcase() {
borderRadius: 999, borderRadius: 999,
border: '1px solid rgba(136,145,199,0.5)', border: '1px solid rgba(136,145,199,0.5)',
background: 'transparent', background: 'transparent',
color: '#BFC3E2', color: '#D7DBEA',
fontSize: '0.78rem', fontSize: '0.78rem',
fontWeight: 700, fontWeight: 700,
letterSpacing: '0.14em', letterSpacing: '0.14em',
@ -231,7 +231,7 @@ export function RoboticsSplineShowcase() {
> >
<item.Icon size={18} style={{ color: '#DEE0F0', marginBottom: '0.5rem' }} /> <item.Icon size={18} style={{ color: '#DEE0F0', marginBottom: '0.5rem' }} />
<div style={{ fontSize: '0.85rem', fontWeight: 600, color: '#FBFBFD' }}>{item.label}</div> <div style={{ fontSize: '0.85rem', fontWeight: 600, color: '#FBFBFD' }}>{item.label}</div>
<div style={{ marginTop: 4, fontSize: '0.66rem', letterSpacing: '0.2em', textTransform: 'uppercase', color: 'rgba(191,195,226,0.72)' }}> <div style={{ marginTop: 4, fontSize: '0.66rem', letterSpacing: '0.2em', textTransform: 'uppercase', color: 'rgba(199, 207, 230,0.72)' }}>
{item.value} {item.value}
</div> </div>
</div> </div>
@ -246,7 +246,7 @@ export function RoboticsSplineShowcase() {
fontWeight: 600, fontWeight: 600,
letterSpacing: '0.32em', letterSpacing: '0.32em',
textTransform: 'uppercase', textTransform: 'uppercase',
color: '#BFC3E2', color: '#D7DBEA',
}} }}
> >
In Tech We Innovate · In Trust We Lead In Tech We Innovate · In Trust We Lead
@ -308,7 +308,7 @@ export function RoboticsSplineShowcase() {
backdropFilter: 'blur(12px)', backdropFilter: 'blur(12px)',
}} }}
> >
<div style={{ fontSize: '0.62rem', letterSpacing: '0.22em', textTransform: 'uppercase', color: 'rgba(191,195,226,0.7)' }}> <div style={{ fontSize: '0.62rem', letterSpacing: '0.22em', textTransform: 'uppercase', color: 'rgba(199, 207, 230,0.7)' }}>
YS Lootah Robotics YS Lootah Robotics
</div> </div>
<div style={{ marginTop: 4, fontSize: '0.88rem', fontWeight: 600, color: '#FBFBFD' }}> <div style={{ marginTop: 4, fontSize: '0.88rem', fontWeight: 600, color: '#FBFBFD' }}>

View File

@ -32,7 +32,7 @@ export function MotionSection({ children, className = '', id, delay = 0, style }
} }
} }
}, },
{ threshold: 0.12 } { threshold: 0, rootMargin: '0px 0px -10% 0px' }
); );
obs.observe(el); obs.observe(el);
return () => obs.disconnect(); return () => obs.disconnect();
@ -45,7 +45,9 @@ export function MotionSection({ children, className = '', id, delay = 0, style }
className={className} className={className}
style={{ style={{
opacity: visible ? 1 : 0, opacity: visible ? 1 : 0,
transform: visible ? 'translateY(0)' : 'translateY(28px)', // when hidden, lift via translate. When visible, drop transform entirely so
// child position:sticky behaves correctly (transformed ancestor would break sticky).
...(visible ? {} : { transform: 'translateY(28px)' }),
transition: `opacity 0.9s cubic-bezier(0.16,1,0.3,1) ${delay}s, transform 0.9s cubic-bezier(0.16,1,0.3,1) ${delay}s`, transition: `opacity 0.9s cubic-bezier(0.16,1,0.3,1) ${delay}s, transform 0.9s cubic-bezier(0.16,1,0.3,1) ${delay}s`,
scrollMarginTop: '6rem', scrollMarginTop: '6rem',
...style, ...style,

View File

@ -113,7 +113,7 @@ function DisplayFrame({
/> />
<div <div
aria-hidden aria-hidden
className="pointer-events-none absolute inset-0 bg-[linear-gradient(rgba(191,195,226,0.06)_1px,transparent_1px),linear-gradient(90deg,rgba(191,195,226,0.06)_1px,transparent_1px)] bg-[size:42px_42px] [mask-image:radial-gradient(ellipse_70%_70%_at_50%_50%,#000_30%,transparent_82%)]" className="pointer-events-none absolute inset-0 bg-[linear-gradient(rgba(199, 207, 230,0.06)_1px,transparent_1px),linear-gradient(90deg,rgba(199, 207, 230,0.06)_1px,transparent_1px)] bg-[size:42px_42px] [mask-image:radial-gradient(ellipse_70%_70%_at_50%_50%,#000_30%,transparent_82%)]"
/> />
{/* status bar */} {/* status bar */}
<div className="absolute left-0 right-0 top-0 z-20 flex items-center justify-between border-b border-[#DEE0F0]/10 bg-[#0a0a0c]/80 px-4 py-2.5 backdrop-blur-xl"> <div className="absolute left-0 right-0 top-0 z-20 flex items-center justify-between border-b border-[#DEE0F0]/10 bg-[#0a0a0c]/80 px-4 py-2.5 backdrop-blur-xl">