feat: add robotics components and data structures
CI/CD / test-and-build (push) Waiting to run
CI/CD / deploy (push) Blocked by required conditions

- 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:
Najjar\NajjarV02
2026-05-20 17:22:47 +04:00
parent 2eac53dac3
commit 92cf4aba3b
87 changed files with 6887 additions and 1430 deletions
+55
View File
@@ -0,0 +1,55 @@
import type { Metadata } from 'next';
import { Navbar } from '@/components/Navbar';
import { FooterAndContact } from '@/components/FooterAndContact';
import { MotionSection } from '@/components/ui/MotionSection';
import { DemoCTA } from '@/components/robotics/DemoCTA';
import { WhyUs } from '@/components/robotics/WhyUs';
import { FloatingTechPanel } from '@/components/robotics/FloatingTechPanel';
export const metadata: Metadata = {
title: 'About YS Lootah Robotics — Exclusive UAE Access to Unitree & Pudu',
description:
'YS Lootah Robotics holds exclusive UAE sales rights for selected Unitree and Pudu Robotics solutions — with a Dubai-based team managing sales, demo, configuration, and deployment.',
};
export default function AboutPage() {
return (
<>
<Navbar />
<main style={{ paddingTop: 'clamp(6rem, 10vw, 8rem)', paddingBottom: 'clamp(4rem, 8vw, 6rem)' }}>
<div className="container-wide" style={{ display: 'flex', flexDirection: 'column', gap: 'clamp(3rem, 6vw, 5rem)' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.25rem', maxWidth: 820 }}>
<span className="eyebrow">About · Exclusive UAE Access</span>
<h1 style={{ margin: 0, fontSize: 'clamp(2rem, 5vw, 3.4rem)', fontWeight: 300, lineHeight: 1.05, letterSpacing: '-0.03em' }}>
<span className="text-gradient" style={{ fontWeight: 500 }}>The UAE&apos;s dedicated destination for Unitree and Pudu Robotics.</span>
</h1>
<p style={{ margin: 0, color: '#cbc4b3', fontSize: 'clamp(0.95rem, 2vw, 1.05rem)', lineHeight: 1.7 }}>
YS Lootah Robotics is the robotics arm of the YS Lootah group — a UAE-based technology partner that holds exclusive sales rights in the UAE for selected Unitree and Pudu Robotics solutions. Our Dubai team manages sales, live demos, configuration, deployment, and ongoing service across the UAE.
</p>
<p style={{ margin: 0, color: '#94908a', fontSize: '0.92rem', lineHeight: 1.7 }}>
Brand names and product trademarks are property of their respective owners. Available exclusively in the UAE through YS Lootah Robotics.
</p>
</div>
<MotionSection>
<FloatingTechPanel />
</MotionSection>
<MotionSection>
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
<h2 style={{ margin: 0, fontSize: 'clamp(1.5rem, 3vw, 2.2rem)', fontWeight: 400, letterSpacing: '-0.02em' }}>
<span className="text-gradient" style={{ fontWeight: 500 }}>Why work with us</span>
</h2>
<WhyUs />
</div>
</MotionSection>
<DemoCTA />
</div>
</main>
<FooterAndContact />
</>
);
}
+172 -172
View File
@@ -1,172 +1,172 @@
'use client';
import { useState, FormEvent } from 'react';
import { useRouter } from 'next/navigation';
export default function AdminLoginPage() {
const router = useRouter();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
setError('');
setLoading(true);
try {
const res = await fetch('/api/admin/login/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
});
const data = await res.json();
if (res.ok) {
router.push('/admin/');
router.refresh();
} else {
setError(data.error ?? 'Login failed');
}
} catch {
setError('Network error. Please try again.');
} finally {
setLoading(false);
}
};
return (
<div style={pageStyle}>
<div style={cardStyle}>
<div style={{ textAlign: 'center', marginBottom: '2rem' }}>
<div style={{
width: '48px',
height: '48px',
borderRadius: '12px',
background: 'rgba(59, 130, 246, 0.08)',
border: '1px solid rgba(59, 130, 246, 0.2)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
margin: '0 auto 1rem',
fontSize: '1.25rem',
}}>
🔐
</div>
<h1 style={{ fontSize: '1.25rem', fontWeight: 700, color: '#1a1a2e', margin: 0, marginBottom: '0.25rem' }}>
Admin Login
</h1>
<p style={{ fontSize: '0.8rem', color: '#94a3b8', margin: 0 }}>
Lootah Robotics — G1 Configurator
</p>
</div>
<form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<div>
<label style={labelStyle} htmlFor="username">Username</label>
<input
id="username"
type="text"
autoComplete="username"
required
value={username}
onChange={(e) => setUsername(e.target.value)}
style={inputStyle}
onFocus={(e) => (e.currentTarget.style.borderColor = 'rgba(59, 130, 246, 0.5)')}
onBlur={(e) => (e.currentTarget.style.borderColor = 'rgba(0, 0, 0, 0.1)')}
placeholder="admin"
/>
</div>
<div>
<label style={labelStyle} htmlFor="password">Password</label>
<input
id="password"
type="password"
autoComplete="current-password"
required
value={password}
onChange={(e) => setPassword(e.target.value)}
style={inputStyle}
onFocus={(e) => (e.currentTarget.style.borderColor = 'rgba(59, 130, 246, 0.5)')}
onBlur={(e) => (e.currentTarget.style.borderColor = 'rgba(0, 0, 0, 0.1)')}
placeholder="••••••••"
/>
</div>
{error && (
<div style={{
padding: '0.6rem 0.875rem',
borderRadius: '0.375rem',
background: 'rgba(239, 68, 68, 0.06)',
border: '1px solid rgba(239, 68, 68, 0.2)',
color: '#dc2626',
fontSize: '0.8rem',
}}>
{error}
</div>
)}
<button
type="submit"
disabled={loading}
style={{
padding: '0.7rem',
borderRadius: '0.5rem',
border: '1px solid rgba(59, 130, 246, 0.3)',
background: loading ? 'rgba(148, 163, 184, 0.1)' : 'rgba(59, 130, 246, 0.08)',
color: loading ? '#94a3b8' : '#2563eb',
fontSize: '0.875rem',
fontWeight: 600,
cursor: loading ? 'not-allowed' : 'pointer',
transition: 'all 0.2s ease',
marginTop: '0.25rem',
}}
>
{loading ? 'Signing in…' : 'Sign In'}
</button>
</form>
</div>
</div>
);
}
const pageStyle: React.CSSProperties = {
minHeight: '100vh',
background: '#ffffff',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '2rem',
fontFamily: 'system-ui, -apple-system, sans-serif',
};
const cardStyle: React.CSSProperties = {
width: '100%',
maxWidth: '380px',
background: 'rgba(255, 255, 255, 0.95)',
backdropFilter: 'blur(20px)',
border: '1px solid rgba(0, 0, 0, 0.06)',
borderRadius: '1rem',
padding: '2rem',
};
const labelStyle: React.CSSProperties = {
display: 'block',
fontSize: '0.75rem',
fontWeight: 600,
color: '#374151',
marginBottom: '0.375rem',
letterSpacing: '0.02em',
};
const inputStyle: React.CSSProperties = {
width: '100%',
padding: '0.6rem 0.875rem',
borderRadius: '0.5rem',
border: '1px solid rgba(0, 0, 0, 0.1)',
background: '#ffffff',
color: '#1a1a2e',
fontSize: '0.875rem',
outline: 'none',
transition: 'border-color 0.2s ease',
boxSizing: 'border-box',
};
'use client';
import { useState, FormEvent } from 'react';
import { useRouter } from 'next/navigation';
export default function AdminLoginPage() {
const router = useRouter();
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
setError('');
setLoading(true);
try {
const res = await fetch('/api/admin/login/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
});
const data = await res.json();
if (res.ok) {
router.push('/admin/');
router.refresh();
} else {
setError(data.error ?? 'Login failed');
}
} catch {
setError('Network error. Please try again.');
} finally {
setLoading(false);
}
};
return (
<div style={pageStyle}>
<div style={cardStyle}>
<div style={{ textAlign: 'center', marginBottom: '2rem' }}>
<div style={{
width: '48px',
height: '48px',
borderRadius: '12px',
background: 'rgba(59, 130, 246, 0.08)',
border: '1px solid rgba(59, 130, 246, 0.2)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
margin: '0 auto 1rem',
fontSize: '1.25rem',
}}>
🔐
</div>
<h1 style={{ fontSize: '1.25rem', fontWeight: 700, color: '#0a0907', margin: 0, marginBottom: '0.25rem' }}>
Admin Login
</h1>
<p style={{ fontSize: '0.8rem', color: '#94908a', margin: 0 }}>
Lootah Robotics — G1 Configurator
</p>
</div>
<form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<div>
<label style={labelStyle} htmlFor="username">Username</label>
<input
id="username"
type="text"
autoComplete="username"
required
value={username}
onChange={(e) => setUsername(e.target.value)}
style={inputStyle}
onFocus={(e) => (e.currentTarget.style.borderColor = 'rgba(59, 130, 246, 0.5)')}
onBlur={(e) => (e.currentTarget.style.borderColor = 'rgba(0, 0, 0, 0.1)')}
placeholder="admin"
/>
</div>
<div>
<label style={labelStyle} htmlFor="password">Password</label>
<input
id="password"
type="password"
autoComplete="current-password"
required
value={password}
onChange={(e) => setPassword(e.target.value)}
style={inputStyle}
onFocus={(e) => (e.currentTarget.style.borderColor = 'rgba(59, 130, 246, 0.5)')}
onBlur={(e) => (e.currentTarget.style.borderColor = 'rgba(0, 0, 0, 0.1)')}
placeholder="••••••••"
/>
</div>
{error && (
<div style={{
padding: '0.6rem 0.875rem',
borderRadius: '0.375rem',
background: 'rgba(239, 68, 68, 0.06)',
border: '1px solid rgba(239, 68, 68, 0.2)',
color: '#dc2626',
fontSize: '0.8rem',
}}>
{error}
</div>
)}
<button
type="submit"
disabled={loading}
style={{
padding: '0.7rem',
borderRadius: '0.5rem',
border: '1px solid rgba(59, 130, 246, 0.3)',
background: loading ? 'rgba(196, 162, 101, 0.1)' : 'rgba(59, 130, 246, 0.08)',
color: loading ? '#94908a' : '#2563eb',
fontSize: '0.875rem',
fontWeight: 600,
cursor: loading ? 'not-allowed' : 'pointer',
transition: 'all 0.2s ease',
marginTop: '0.25rem',
}}
>
{loading ? 'Signing in…' : 'Sign In'}
</button>
</form>
</div>
</div>
);
}
const pageStyle: React.CSSProperties = {
minHeight: '100vh',
background: '#ffffff',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '2rem',
fontFamily: 'system-ui, -apple-system, sans-serif',
};
const cardStyle: React.CSSProperties = {
width: '100%',
maxWidth: '380px',
background: 'rgba(255, 255, 255, 0.95)',
backdropFilter: 'blur(20px)',
border: '1px solid rgba(0, 0, 0, 0.06)',
borderRadius: '1rem',
padding: '2rem',
};
const labelStyle: React.CSSProperties = {
display: 'block',
fontSize: '0.75rem',
fontWeight: 600,
color: '#374151',
marginBottom: '0.375rem',
letterSpacing: '0.02em',
};
const inputStyle: React.CSSProperties = {
width: '100%',
padding: '0.6rem 0.875rem',
borderRadius: '0.5rem',
border: '1px solid rgba(0, 0, 0, 0.1)',
background: '#ffffff',
color: '#0a0907',
fontSize: '0.875rem',
outline: 'none',
transition: 'border-color 0.2s ease',
boxSizing: 'border-box',
};
+47 -47
View File
@@ -488,7 +488,7 @@ export default function AdminPage() {
new Date(ts * 1000).toLocaleDateString('en-AE', { day: 'numeric', month: 'short', year: 'numeric' });
if (!isPricingHydrated) {
return <div style={pageStyle}><p style={{ color: '#64748b' }}>Loading…</p></div>;
return <div style={pageStyle}><p style={{ color: '#6b6862' }}>Loading…</p></div>;
}
return (
@@ -497,10 +497,10 @@ export default function AdminPage() {
{/* HEADER */}
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '1.5rem' }}>
<div>
<h1 style={{ fontSize: '1.25rem', fontWeight: 700, color: '#1a1a2e', margin: 0, marginBottom: '0.2rem' }}>
<h1 style={{ fontSize: '1.25rem', fontWeight: 700, color: '#0a0907', margin: 0, marginBottom: '0.2rem' }}>
Admin Dashboard
</h1>
<p style={{ fontSize: '0.75rem', color: '#94a3b8', margin: 0 }}>Lootah Robotics G1 Configurator</p>
<p style={{ fontSize: '0.75rem', color: '#94908a', margin: 0 }}>Lootah Robotics G1 Configurator</p>
</div>
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
<button onClick={() => setShowPwModal(true)} style={ghostBtnStyle}>Change Password</button>
@@ -527,7 +527,7 @@ export default function AdminPage() {
borderRadius: '0.375rem',
border: 'none',
background: activeTab === t ? 'rgba(59,130,246,0.08)' : 'transparent',
color: activeTab === t ? '#2563eb' : '#64748b',
color: activeTab === t ? '#2563eb' : '#6b6862',
fontSize: '0.8rem',
fontWeight: activeTab === t ? 600 : 400,
cursor: 'pointer',
@@ -558,9 +558,9 @@ export default function AdminPage() {
aria-label={`Label for ${item.label}`}
/>
</div>
<div style={{ fontSize: '0.7rem', color: '#94a3b8', fontFamily: 'monospace', paddingLeft: '0.25rem' }}>{item.id}</div>
<div style={{ fontSize: '0.7rem', color: '#94908a', fontFamily: 'monospace', paddingLeft: '0.25rem' }}>{item.id}</div>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.375rem' }}>
<span style={{ fontSize: '0.7rem', color: '#94a3b8' }}>AED</span>
<span style={{ fontSize: '0.7rem', color: '#94908a' }}>AED</span>
<input
type="text"
value={formatPrice(editedPrices[item.id] ?? item.price)}
@@ -584,7 +584,7 @@ export default function AdminPage() {
) : null}
<label
title="Upload / replace .glb"
style={{ display: 'inline-flex', alignItems: 'center', gap: '0.25rem', cursor: rowGlbUploading[item.id] ? 'wait' : 'pointer', fontSize: '0.7rem', color: rowGlbUploading[item.id] ? '#2563eb' : '#94a3b8', background: rowGlbUploading[item.id] ? 'rgba(59,130,246,0.06)' : 'transparent', border: `1px dashed ${rowGlbUploading[item.id] ? 'rgba(59,130,246,0.3)' : 'rgba(0,0,0,0.12)'}`, borderRadius: '0.375rem', padding: '0.2rem 0.45rem', whiteSpace: 'nowrap' }}
style={{ display: 'inline-flex', alignItems: 'center', gap: '0.25rem', cursor: rowGlbUploading[item.id] ? 'wait' : 'pointer', fontSize: '0.7rem', color: rowGlbUploading[item.id] ? '#2563eb' : '#94908a', background: rowGlbUploading[item.id] ? 'rgba(59,130,246,0.06)' : 'transparent', border: `1px dashed ${rowGlbUploading[item.id] ? 'rgba(59,130,246,0.3)' : 'rgba(0,0,0,0.12)'}`, borderRadius: '0.375rem', padding: '0.2rem 0.45rem', whiteSpace: 'nowrap' }}
>
<input type="file" accept=".glb" style={{ display: 'none' }} onChange={(e) => {
const f = e.target.files?.[0] ?? null;
@@ -642,7 +642,7 @@ export default function AdminPage() {
background: addItemGlb ? 'rgba(59,130,246,0.04)' : '#fff',
cursor: 'pointer',
fontSize: '0.8rem',
color: addItemGlb ? '#2563eb' : '#94a3b8',
color: addItemGlb ? '#2563eb' : '#94908a',
transition: 'all 0.2s',
}}>
<input
@@ -658,7 +658,7 @@ export default function AdminPage() {
{addItemGlb && (
<button
onClick={(e) => { e.preventDefault(); setAddItemGlb(null); }}
style={{ marginLeft: 'auto', background: 'none', border: 'none', cursor: 'pointer', color: '#94a3b8', fontSize: '0.75rem', padding: '0 2px' }}
style={{ marginLeft: 'auto', background: 'none', border: 'none', cursor: 'pointer', color: '#94908a', fontSize: '0.75rem', padding: '0 2px' }}
>✕</button>
)}
</label>
@@ -686,7 +686,7 @@ export default function AdminPage() {
{activeTab === 'personas' && (
<div>
{!isPersonaHydrated ? (
<p style={{ color: '#64748b', fontSize: '0.85rem' }}>Loading personas…</p>
<p style={{ color: '#6b6862', fontSize: '0.85rem' }}>Loading personas…</p>
) : (
<TableCard>
<TableHeader cols="1fr 80px 80px 56px" labels={['Persona', 'Torso', 'Legs', '']} />
@@ -694,15 +694,15 @@ export default function AdminPage() {
<div key={p.id} style={{ display: 'grid', gridTemplateColumns: '1fr 80px 80px 56px', padding: '0.875rem 1.25rem', alignItems: 'center', borderBottom: i < personas.length - 1 ? '1px solid rgba(0,0,0,0.04)' : 'none' }}>
<div>
<div style={{ fontSize: '0.85rem', color: '#374151', fontWeight: 500 }}>{p.label}</div>
<div style={{ fontSize: '0.7rem', color: '#94a3b8' }}>{p.description}</div>
<div style={{ fontSize: '0.7rem', color: '#94908a' }}>{p.description}</div>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.375rem' }}>
<div style={{ width: 20, height: 20, borderRadius: 4, backgroundColor: p.colors.torso, border: '1px solid rgba(0,0,0,0.1)', flexShrink: 0 }} />
<span style={{ fontSize: '0.7rem', color: '#94a3b8', fontFamily: 'monospace' }}>{p.colors.torso}</span>
<span style={{ fontSize: '0.7rem', color: '#94908a', fontFamily: 'monospace' }}>{p.colors.torso}</span>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: '0.375rem' }}>
<div style={{ width: 20, height: 20, borderRadius: 4, backgroundColor: p.colors.legs, border: '1px solid rgba(0,0,0,0.1)', flexShrink: 0 }} />
<span style={{ fontSize: '0.7rem', color: '#94a3b8', fontFamily: 'monospace' }}>{p.colors.legs}</span>
<span style={{ fontSize: '0.7rem', color: '#94908a', fontFamily: 'monospace' }}>{p.colors.legs}</span>
</div>
<div style={{ display: 'flex', justifyContent: 'center' }}>
{p.id !== 'none' && (
@@ -762,7 +762,7 @@ export default function AdminPage() {
</div>
{ordersError && <p style={errorTextStyle}>{ordersError}</p>}
{!ordersLoading && orders.length === 0 && !ordersError && (
<p style={{ color: '#94a3b8', fontSize: '0.85rem', textAlign: 'center', padding: '2rem' }}>No orders found.</p>
<p style={{ color: '#94908a', fontSize: '0.85rem', textAlign: 'center', padding: '2rem' }}>No orders found.</p>
)}
{orders.length > 0 && (
<TableCard>
@@ -791,7 +791,7 @@ export default function AdminPage() {
</div>
{contactsError && <p style={errorTextStyle}>{contactsError}</p>}
{!contactsLoading && contacts.length === 0 && !contactsError && (
<p style={{ color: '#94a3b8', fontSize: '0.85rem', textAlign: 'center', padding: '2rem' }}>No contact inquiries yet.</p>
<p style={{ color: '#94908a', fontSize: '0.85rem', textAlign: 'center', padding: '2rem' }}>No contact inquiries yet.</p>
)}
{contacts.length > 0 && (
<TableCard>
@@ -799,10 +799,10 @@ export default function AdminPage() {
{contacts.map((c, i) => (
<div key={c.id} style={{ display: 'grid', gridTemplateColumns: '1.5fr 1fr 1fr 2.5fr 1fr', padding: '1rem 1.25rem', alignItems: 'flex-start', borderBottom: i < contacts.length - 1 ? '1px solid rgba(0,0,0,0.04)' : 'none', gap: '1rem' }}>
<div style={{ fontSize: '0.85rem', fontWeight: 500, color: '#1e293b' }}>{c.name}</div>
<div style={{ fontSize: '0.8rem', color: '#64748b' }}><a href={`mailto:${c.email}`} style={{ color: '#3b82f6', textDecoration: 'none' }}>{c.email}</a></div>
<div style={{ fontSize: '0.8rem', color: '#64748b' }}>{c.phone ? <a href={`tel:${c.phone}`} style={{ color: '#64748b', textDecoration: 'none' }}>{c.phone}</a> : '-'}</div>
<div style={{ fontSize: '0.8rem', color: '#6b6862' }}><a href={`mailto:${c.email}`} style={{ color: '#3b82f6', textDecoration: 'none' }}>{c.email}</a></div>
<div style={{ fontSize: '0.8rem', color: '#6b6862' }}>{c.phone ? <a href={`tel:${c.phone}`} style={{ color: '#6b6862', textDecoration: 'none' }}>{c.phone}</a> : '-'}</div>
<div style={{ fontSize: '0.8rem', color: '#475569', whiteSpace: 'pre-wrap', lineHeight: 1.5 }}>{c.message}</div>
<div style={{ fontSize: '0.75rem', color: '#94a3b8' }}>{new Date(c.createdAt).toLocaleDateString('en-AE')}</div>
<div style={{ fontSize: '0.75rem', color: '#94908a' }}>{new Date(c.createdAt).toLocaleDateString('en-AE')}</div>
</div>
))}
</TableCard>
@@ -813,15 +813,15 @@ export default function AdminPage() {
{/* SETTINGS TAB */}
{activeTab === 'settings' && (
<div>
<h2 style={{ fontSize: '1rem', fontWeight: 700, color: '#1a1a2e', margin: '0 0 1rem' }}>App Settings</h2>
<h2 style={{ fontSize: '1rem', fontWeight: 700, color: '#0a0907', margin: '0 0 1rem' }}>App Settings</h2>
{settingError && <p style={{ color: '#dc2626', fontSize: '0.8rem', marginBottom: '0.75rem' }}>{settingError}</p>}
{settingsLoading ? (
<p style={{ color: '#64748b', fontSize: '0.875rem' }}>Loading…</p>
<p style={{ color: '#6b6862', fontSize: '0.875rem' }}>Loading…</p>
) : (
<TableCard>
<TableHeader cols="1fr 1.5fr 110px 50px" labels={['Key', 'Value', '', '']} />
{settings.length === 0 && (
<p style={{ padding: '1rem', color: '#94a3b8', fontSize: '0.8rem' }}>No settings yet.</p>
<p style={{ padding: '1rem', color: '#94908a', fontSize: '0.8rem' }}>No settings yet.</p>
)}
{settings.map((s, i) => (
<SettingRow
@@ -837,8 +837,8 @@ export default function AdminPage() {
)}
{/* Add new setting */}
<div style={{ marginTop: '1.25rem', padding: '1rem', background: '#f8fafc', borderRadius: '0.75rem', border: '1px solid rgba(0,0,0,0.06)' }}>
<h3 style={{ fontSize: '0.875rem', fontWeight: 600, color: '#1a1a2e', margin: '0 0 0.75rem' }}>Add Setting</h3>
<div style={{ marginTop: '1.25rem', padding: '1rem', background: '#f5f1e8', borderRadius: '0.75rem', border: '1px solid rgba(0,0,0,0.06)' }}>
<h3 style={{ fontSize: '0.875rem', fontWeight: 600, color: '#0a0907', margin: '0 0 0.75rem' }}>Add Setting</h3>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1.5fr auto', gap: '0.5rem', alignItems: 'flex-end' }}>
<div>
<label style={labelStyle}>Key</label>
@@ -869,7 +869,7 @@ export default function AdminPage() {
{showPwModal && (
<div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.3)', backdropFilter: 'blur(4px)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 50 }}>
<div style={{ background: '#fff', borderRadius: '1rem', padding: '1.5rem', width: '100%', maxWidth: '380px', boxShadow: '0 20px 60px rgba(0,0,0,0.15)' }}>
<h2 style={{ fontSize: '1rem', fontWeight: 700, color: '#1a1a2e', margin: '0 0 1.25rem' }}>Change Password</h2>
<h2 style={{ fontSize: '1rem', fontWeight: 700, color: '#0a0907', margin: '0 0 1.25rem' }}>Change Password</h2>
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
{(['current', 'next', 'confirm'] as const).map((field) => (
<div key={field}>
@@ -918,7 +918,7 @@ function SettingRow({
useEffect(() => { setEditVal(setting.value); }, [setting.value]);
return (
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1.5fr 110px 50px', gap: '0.75rem', padding: '0.625rem 1rem', alignItems: 'center', background: index % 2 === 0 ? '#fff' : '#f8fafc', borderTop: index > 0 ? '1px solid rgba(0,0,0,0.04)' : 'none' }}>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1.5fr 110px 50px', gap: '0.75rem', padding: '0.625rem 1rem', alignItems: 'center', background: index % 2 === 0 ? '#fff' : '#f5f1e8', borderTop: index > 0 ? '1px solid rgba(0,0,0,0.04)' : 'none' }}>
<span style={{ fontSize: '0.8rem', fontFamily: 'monospace', color: '#334155' }}>{setting.key}</span>
<input
value={editVal}
@@ -979,7 +979,7 @@ function OrderRow({
const fieldStyle: React.CSSProperties = {
fontSize: '0.63rem',
fontWeight: 600,
color: '#94a3b8',
color: '#94908a',
textTransform: 'uppercase',
letterSpacing: '0.04em',
};
@@ -996,8 +996,8 @@ function OrderRow({
<div style={{ display: 'grid', gridTemplateColumns: '1fr 130px 90px 100px 28px', padding: '0.75rem 1.25rem', alignItems: 'center' }}>
<div>
<div style={{ fontSize: '0.85rem', color: '#374151', fontWeight: 500 }}>{name}</div>
{email && <div style={{ fontSize: '0.7rem', color: '#94a3b8' }}>{email}</div>}
<div style={{ fontSize: '0.65rem', color: '#cbd5e1', fontFamily: 'monospace', marginTop: '0.1rem' }}>{order.id}</div>
{email && <div style={{ fontSize: '0.7rem', color: '#94908a' }}>{email}</div>}
<div style={{ fontSize: '0.65rem', color: '#cbc4b3', fontFamily: 'monospace', marginTop: '0.1rem' }}>{order.id}</div>
</div>
<div style={{ fontSize: '0.85rem', color: '#374151', fontWeight: 500 }}>{formatAmount(order.amount, order.currency)}</div>
<div>
@@ -1008,17 +1008,17 @@ function OrderRow({
fontSize: '0.65rem',
fontWeight: 600,
textTransform: 'uppercase',
background: order.status === 'succeeded' ? 'rgba(34,197,94,0.1)' : order.status === 'canceled' ? 'rgba(239,68,68,0.08)' : 'rgba(148,163,184,0.15)',
color: order.status === 'succeeded' ? '#16a34a' : order.status === 'canceled' ? '#dc2626' : '#64748b',
background: order.status === 'succeeded' ? 'rgba(34,197,94,0.1)' : order.status === 'canceled' ? 'rgba(239,68,68,0.08)' : 'rgba(196, 162, 101,0.15)',
color: order.status === 'succeeded' ? '#16a34a' : order.status === 'canceled' ? '#dc2626' : '#6b6862',
}}>
{order.status}
</span>
</div>
<div style={{ fontSize: '0.75rem', color: '#94a3b8' }}>{formatDate(order.created)}</div>
<div style={{ fontSize: '0.75rem', color: '#94908a' }}>{formatDate(order.created)}</div>
<button
onClick={handleExpand}
title={expanded ? 'Collapse' : 'Show details'}
style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#94a3b8', fontSize: '0.7rem', padding: '2px 4px', borderRadius: 4 }}
style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#94908a', fontSize: '0.7rem', padding: '2px 4px', borderRadius: 4 }}
>
{expanded ? '▲' : '▼'}
</button>
@@ -1073,16 +1073,16 @@ function OrderRow({
</div>
))}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', paddingTop: '0.4rem', marginTop: '0.1rem', borderTop: '1px solid rgba(0,0,0,0.07)' }}>
<span style={{ fontSize: '0.82rem', fontWeight: 700, color: '#1a1a2e' }}>Total</span>
<span style={{ fontSize: '0.88rem', fontWeight: 700, color: '#1a1a2e', fontFamily: 'monospace' }}>
<span style={{ fontSize: '0.82rem', fontWeight: 700, color: '#0a0907' }}>Total</span>
<span style={{ fontSize: '0.88rem', fontWeight: 700, color: '#0a0907', fontFamily: 'monospace' }}>
{formatAmount(order.amount, order.currency)}
</span>
</div>
</div>
) : (
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{ fontSize: '0.78rem', color: '#64748b' }}>Total (legacy order)</span>
<span style={{ fontSize: '0.88rem', fontWeight: 700, color: '#1a1a2e', fontFamily: 'monospace' }}>
<span style={{ fontSize: '0.78rem', color: '#6b6862' }}>Total (legacy order)</span>
<span style={{ fontSize: '0.88rem', fontWeight: 700, color: '#0a0907', fontFamily: 'monospace' }}>
{formatAmount(order.amount, order.currency)}
</span>
</div>
@@ -1090,16 +1090,16 @@ function OrderRow({
</SectionBox>
{/* Payment ID */}
<div style={{ fontSize: '0.65rem', color: '#cbd5e1', fontFamily: 'monospace' }}>
<div style={{ fontSize: '0.65rem', color: '#cbc4b3', fontFamily: 'monospace' }}>
Payment ID: {order.id}
</div>
</div>
{/* Right: snapshot */}
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.375rem' }}>
<div style={{ fontSize: '0.63rem', fontWeight: 600, color: '#94a3b8', textTransform: 'uppercase', letterSpacing: '0.04em' }}>Robot Snapshot</div>
<div style={{ fontSize: '0.63rem', fontWeight: 600, color: '#94908a', textTransform: 'uppercase', letterSpacing: '0.04em' }}>Robot Snapshot</div>
{snapshot === 'loading' && (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(248,248,246,0.8)', borderRadius: '0.5rem', minHeight: 120, fontSize: '0.75rem', color: '#94a3b8' }}>Loading…</div>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(248,248,246,0.8)', borderRadius: '0.5rem', minHeight: 120, fontSize: '0.75rem', color: '#94908a' }}>Loading…</div>
)}
{snapshot && snapshot !== 'loading' && snapshot !== 'none' && (
/* eslint-disable-next-line @next/next/no-img-element */
@@ -1110,7 +1110,7 @@ function OrderRow({
/>
)}
{snapshot === 'none' && (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(248,248,246,0.8)', borderRadius: '0.5rem', minHeight: 80, fontSize: '0.72rem', color: '#cbd5e1' }}>No snapshot</div>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(248,248,246,0.8)', borderRadius: '0.5rem', minHeight: 80, fontSize: '0.72rem', color: '#cbc4b3' }}>No snapshot</div>
)}
</div>
</div>
@@ -1122,7 +1122,7 @@ function OrderRow({
function SectionBox({ title, children }: { title: string; children: React.ReactNode }) {
return (
<div style={{ background: 'rgba(248,248,246,0.8)', borderRadius: '0.5rem', padding: '0.75rem 1rem' }}>
<div style={{ fontSize: '0.6rem', fontWeight: 700, color: '#94a3b8', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: '0.5rem' }}>{title}</div>
<div style={{ fontSize: '0.6rem', fontWeight: 700, color: '#94908a', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: '0.5rem' }}>{title}</div>
{children}
</div>
);
@@ -1138,7 +1138,7 @@ function InfoField({ label, value }: { label: string; value?: string | null }) {
if (!value) return null;
return (
<div>
<div style={{ fontSize: '0.63rem', fontWeight: 600, color: '#94a3b8', textTransform: 'uppercase', letterSpacing: '0.04em' }}>{label}</div>
<div style={{ fontSize: '0.63rem', fontWeight: 600, color: '#94908a', textTransform: 'uppercase', letterSpacing: '0.04em' }}>{label}</div>
<div style={{ fontSize: '0.8rem', color: '#374151', marginTop: '0.1rem', wordBreak: 'break-word' }}>{value}</div>
</div>
);
@@ -1147,8 +1147,8 @@ function InfoField({ label, value }: { label: string; value?: string | null }) {
function StatCard({ label, value }: { label: string; value: string }) {
return (
<div style={{ background: 'rgba(255,255,255,0.95)', border: '1px solid rgba(0,0,0,0.06)', borderRadius: '0.75rem', padding: '1rem 1.25rem' }}>
<div style={{ fontSize: '0.7rem', fontWeight: 600, color: '#94a3b8', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: '0.25rem' }}>{label}</div>
<div style={{ fontSize: '1.25rem', fontWeight: 700, color: '#1a1a2e' }}>{value}</div>
<div style={{ fontSize: '0.7rem', fontWeight: 600, color: '#94908a', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: '0.25rem' }}>{label}</div>
<div style={{ fontSize: '1.25rem', fontWeight: 700, color: '#0a0907' }}>{value}</div>
</div>
);
}
@@ -1165,7 +1165,7 @@ function TableHeader({ cols, labels }: { cols: string; labels: string[] }) {
return (
<div style={{ display: 'grid', gridTemplateColumns: cols, padding: '0.6rem 1.25rem', borderBottom: '1px solid rgba(0,0,0,0.04)', background: 'rgba(248,248,246,0.5)' }}>
{labels.map((l) => (
<span key={l} style={{ fontSize: '0.65rem', fontWeight: 600, color: '#64748b', textTransform: 'uppercase', letterSpacing: '0.05em' }}>{l}</span>
<span key={l} style={{ fontSize: '0.65rem', fontWeight: 600, color: '#6b6862', textTransform: 'uppercase', letterSpacing: '0.05em' }}>{l}</span>
))}
</div>
);
@@ -1204,7 +1204,7 @@ const formInputStyle: React.CSSProperties = {
borderRadius: '0.375rem',
border: '1px solid rgba(0,0,0,0.1)',
background: '#ffffff',
color: '#1a1a2e',
color: '#0a0907',
fontSize: '0.8rem',
outline: 'none',
boxSizing: 'border-box',
@@ -1216,7 +1216,7 @@ const tableInputStyle: React.CSSProperties = {
borderRadius: '0.375rem',
border: '1px solid rgba(0,0,0,0.1)',
background: '#ffffff',
color: '#1a1a2e',
color: '#0a0907',
fontSize: '0.8rem',
fontFamily: 'monospace',
textAlign: 'right' as const,
+56
View File
@@ -0,0 +1,56 @@
import type { Metadata } from 'next';
import { Navbar } from '@/components/Navbar';
import { FooterAndContact } from '@/components/FooterAndContact';
import { InquiryForm } from '@/components/robotics/InquiryForm';
export const metadata: Metadata = {
title: 'Book a Robotics Demo in Dubai — YS Lootah Robotics',
description:
'Book a live robot demo at our Dubai showroom or at your venue. See selected Unitree and Pudu Robotics solutions in action — available exclusively in the UAE through YS Lootah Robotics.',
};
export default function BookDemoPage() {
return (
<>
<Navbar />
<main style={{ paddingTop: 'clamp(6rem, 10vw, 8rem)', paddingBottom: 'clamp(4rem, 8vw, 6rem)' }}>
<div className="container-wide" style={{ display: 'grid', gap: 'clamp(2rem, 5vw, 3rem)', gridTemplateColumns: 'repeat(auto-fit, minmax(min(320px, 100%), 1fr))', alignItems: 'flex-start' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.25rem' }}>
<span className="eyebrow">Book a live demo · Dubai showroom</span>
<h1 style={{ margin: 0, fontSize: 'clamp(2rem, 5vw, 3.2rem)', fontWeight: 300, lineHeight: 1.05, letterSpacing: '-0.03em' }}>
<span className="text-gradient" style={{ fontWeight: 500 }}>See the future in person.</span>
</h1>
<p style={{ margin: 0, color: '#cbc4b3', fontSize: 'clamp(0.95rem, 2vw, 1.05rem)', lineHeight: 1.7 }}>
Book a live robot demo at our Dubai showroom or schedule an on-site demo at your venue. Our team will tailor the demo to your use case before you arrive.
</p>
<ul style={{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
{[
'Choose a humanoid, quadruped, or service robot to see live',
'Designed around your industry — restaurant, mall, hotel, security, etc.',
'Walk through configuration and deployment options',
'Get UAE-specific pricing and availability',
].map((b) => (
<li key={b} style={{ display: 'flex', alignItems: 'flex-start', gap: '0.625rem', color: '#cbc4b3', lineHeight: 1.55 }}>
<span style={{ width: 8, height: 8, marginTop: 8, borderRadius: 999, background: '#e0c896', flex: 'none' }} />
{b}
</li>
))}
</ul>
</div>
<div className="card" style={{ padding: 'clamp(1.5rem, 4vw, 2.5rem)' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', marginBottom: '1.5rem' }}>
<span className="eyebrow">Demo request</span>
<h2 style={{ margin: 0, fontSize: '1.4rem', fontWeight: 500 }}>Tell us when and where</h2>
</div>
<InquiryForm intent="demo" />
</div>
</div>
</main>
<FooterAndContact />
</>
);
}
+95
View File
@@ -0,0 +1,95 @@
import type { Metadata } from 'next';
import { Navbar } from '@/components/Navbar';
import { FooterAndContact } from '@/components/FooterAndContact';
import { BRANDS, ROBOTS, type RobotBrand } from '@/data/robots';
import { RobotProductCard } from '@/components/robotics/RobotProductCard';
import { MotionSection } from '@/components/ui/MotionSection';
import { DemoCTA } from '@/components/robotics/DemoCTA';
export const metadata: Metadata = {
title: 'Robotics Brands — Unitree & Pudu | Exclusive UAE Access via YS Lootah Robotics',
description:
'Selected Unitree and Pudu Robotics solutions — available exclusively in the UAE through YS Lootah Robotics. Humanoid, quadruped, service, delivery, and cleaning robots.',
};
const ORDER: RobotBrand[] = ['unitree', 'pudu'];
export default function BrandsPage() {
return (
<>
<Navbar />
<main style={{ paddingTop: 'clamp(6rem, 10vw, 8rem)', paddingBottom: 'clamp(4rem, 8vw, 6rem)' }}>
<div className="container-wide" style={{ display: 'flex', flexDirection: 'column', gap: 'clamp(3rem, 5vw, 5rem)' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem', maxWidth: 760 }}>
<span className="eyebrow">Exclusive UAE Access · Dubai</span>
<h1 style={{ margin: 0, fontSize: 'clamp(2rem, 5vw, 3.4rem)', fontWeight: 300, lineHeight: 1.05, letterSpacing: '-0.03em' }}>
<span className="text-gradient" style={{ fontWeight: 500 }}>
Selected Unitree and Pudu solutions — exclusively in the UAE.
</span>
</h1>
<p style={{ margin: 0, color: '#cbc4b3', fontSize: 'clamp(0.95rem, 2vw, 1.05rem)', lineHeight: 1.7 }}>
YS Lootah Robotics holds exclusive UAE sales rights for selected Unitree and Pudu Robotics solutions — with on-the-ground sales, demo, and deployment support across Dubai and the UAE.
</p>
<p style={{ margin: 0, color: '#6b6862', fontSize: '0.82rem', lineHeight: 1.6 }}>
Brand names and product trademarks are property of their respective owners. Available exclusively in the UAE through YS Lootah Robotics.
</p>
</div>
{ORDER.map((id) => {
const brand = BRANDS[id];
const robots = ROBOTS.filter((r) => r.brand === id);
return (
<MotionSection key={id} id={id}>
<div
className="card"
style={{
padding: 'clamp(1.5rem, 4vw, 2.5rem)',
background:
`radial-gradient(ellipse 80% 60% at 0% 0%, ${brand.accent}1A, transparent 60%), linear-gradient(180deg, rgba(15, 12, 8,0.65), rgba(5, 5, 5,0.95))`,
}}
>
<div style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'baseline', gap: '1rem', marginBottom: '1.5rem' }}>
<h2 style={{ margin: 0, fontSize: 'clamp(1.6rem, 3.4vw, 2.4rem)', fontWeight: 600, letterSpacing: '-0.02em' }}>
{brand.name.split(' ')[0]} <span style={{ color: brand.accent }}>{brand.name.split(' ').slice(1).join(' ')}</span>
</h2>
<span style={{ fontSize: '0.72rem', letterSpacing: '0.22em', textTransform: 'uppercase', color: brand.accent }}>
{robots.length} model{robots.length === 1 ? '' : 's'}
</span>
</div>
<p style={{ margin: 0, color: '#cbc4b3', lineHeight: 1.7, maxWidth: 800, marginBottom: '1.5rem' }}>
{brand.description}
</p>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.625rem', marginBottom: '2rem' }}>
<a className="btn btn-primary" href={`/robots/?brand=${id}`}>See all {brand.name.split(' ')[0]} robots</a>
<a className="btn btn-ghost" href={brand.url} target="_blank" rel="noopener noreferrer">
Visit brand site
</a>
</div>
<div
style={{
display: 'grid',
gap: '1.25rem',
gridTemplateColumns: 'repeat(auto-fit, minmax(min(260px, 100%), 1fr))',
}}
>
{robots.map((r) => (
<RobotProductCard key={r.id} robot={r} />
))}
</div>
</div>
</MotionSection>
);
})}
<DemoCTA
title="Not sure which brand fits?"
description="Tell us about your venue, timeline, and use case. We will recommend a brand and model — and book a live demo at our Dubai showroom."
/>
</div>
</main>
<FooterAndContact />
</>
);
}
+50 -20
View File
@@ -1,20 +1,50 @@
'use client';
import { ConfiguratorSection } from '@/components/ConfiguratorSection';
import { Navbar } from '@/components/Navbar';
import { FooterAndContact } from '@/components/FooterAndContact';
export default function ConfigurePage() {
return (
<>
<Navbar />
{/* Configurator section takes up full height minus navbar height roughly, or we just let it take its normal height */}
<div style={{ minHeight: '100vh', paddingTop: '80px' }}>
<ConfiguratorSection />
</div>
<FooterAndContact />
</>
);
}
'use client';
import { ConfiguratorSection } from '@/components/ConfiguratorSection';
import { Navbar } from '@/components/Navbar';
import { FooterAndContact } from '@/components/FooterAndContact';
export default function ConfigurePage() {
return (
<>
<Navbar />
<header
style={{
position: 'relative',
paddingTop: 'clamp(6rem, 10vw, 8rem)',
paddingBottom: 'clamp(1.5rem, 3vw, 2.5rem)',
paddingLeft: 'clamp(1rem, 4vw, 2rem)',
paddingRight: 'clamp(1rem, 4vw, 2rem)',
background:
'radial-gradient(ellipse 60% 80% at 50% 0%, rgba(196, 162, 101,0.12), transparent 60%), linear-gradient(180deg, #050505 0%, #030303 100%)',
borderBottom: '1px solid rgba(196, 162, 101,0.12)',
}}
>
<div style={{ maxWidth: 1320, margin: '0 auto', display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
<span className="eyebrow">Configurator · Unitree G1 humanoid · Dubai</span>
<h1
style={{
margin: 0,
fontSize: 'clamp(1.6rem, 3.6vw, 2.4rem)',
fontWeight: 300,
letterSpacing: '-0.02em',
lineHeight: 1.1,
}}
>
<span className="text-gradient" style={{ fontWeight: 500 }}>Configure your robot.</span>
</h1>
<p style={{ margin: 0, color: '#94908a', fontSize: 'clamp(0.85rem, 1.6vw, 0.95rem)', lineHeight: 1.6, maxWidth: 720 }}>
Choose persona, attire, colors, and accessories — visualize your Unitree G1 humanoid before you request a quotation from YS Lootah Robotics.
</p>
</div>
</header>
<div style={{ minHeight: '100vh', background: '#ffffff' }}>
<ConfiguratorSection />
</div>
<FooterAndContact />
</>
);
}
+72
View File
@@ -0,0 +1,72 @@
import type { Metadata } from 'next';
import { Navbar } from '@/components/Navbar';
import { FooterAndContact } from '@/components/FooterAndContact';
import { InquiryForm } from '@/components/robotics/InquiryForm';
export const metadata: Metadata = {
title: 'Contact YS Lootah Robotics Dubai — Robotics Sales & Demo Inquiries',
description:
'Dubai robotics sales, support, and demo inquiries. Contact YS Lootah Robotics — the exclusive UAE destination for selected Unitree and Pudu Robotics solutions — by phone, email, or WhatsApp.',
};
export default function ContactPage() {
return (
<>
<Navbar />
<main style={{ paddingTop: 'clamp(6rem, 10vw, 8rem)', paddingBottom: 'clamp(4rem, 8vw, 6rem)' }}>
<div className="container-wide" style={{ display: 'grid', gap: 'clamp(2rem, 5vw, 3rem)', gridTemplateColumns: 'repeat(auto-fit, minmax(min(320px, 100%), 1fr))', alignItems: 'flex-start' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.25rem' }}>
<span className="eyebrow">Contact · Dubai · UAE</span>
<h1 style={{ margin: 0, fontSize: 'clamp(2rem, 5vw, 3.2rem)', fontWeight: 300, lineHeight: 1.05, letterSpacing: '-0.03em' }}>
<span className="text-gradient" style={{ fontWeight: 500 }}>Talk to our Dubai robotics team.</span>
</h1>
<p style={{ margin: 0, color: '#cbc4b3', fontSize: 'clamp(0.95rem, 2vw, 1.05rem)', lineHeight: 1.7 }}>
Tell us about your venue, timeline, and use case. We will recommend a robot, share availability, and book a live demo at our Dubai showroom.
</p>
<div className="card" style={{ padding: 'clamp(1.5rem, 4vw, 2rem)', display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<ContactRow label="Phone" value="+971 55 948 2728" href="tel:+971559482728" />
<ContactRow label="Office" value="+971 4 349 9319" href="tel:+97143499319" />
<ContactRow label="Email" value="info@yslootahtech.com" href="mailto:info@yslootahtech.com" />
<ContactRow label="WhatsApp" value="+971 55 948 2728" href="https://wa.me/971559482728" external />
<ContactRow label="Address" value="Office 408, City Bay Business Center, Dubai, UAE" href="https://maps.google.com/?q=Office+408+City+Bay+Business+Center+Dubai" external />
</div>
</div>
<div className="card" style={{ padding: 'clamp(1.5rem, 4vw, 2.5rem)' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', marginBottom: '1.5rem' }}>
<span className="eyebrow">Inquiry form</span>
<h2 style={{ margin: 0, fontSize: '1.4rem', fontWeight: 500 }}>Send us a message</h2>
</div>
<InquiryForm intent="inquiry" />
</div>
</div>
</main>
<FooterAndContact />
</>
);
}
function ContactRow({ label, value, href, external = false }: { label: string; value: string; href: string; external?: boolean }) {
return (
<a
href={href}
{...(external ? { target: '_blank', rel: 'noopener noreferrer' } : {})}
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
gap: '1rem',
padding: '0.75rem 0',
borderBottom: '1px solid rgba(196, 162, 101,0.1)',
textDecoration: 'none',
color: '#f5f1e8',
}}
>
<span style={{ fontSize: '0.7rem', letterSpacing: '0.22em', textTransform: 'uppercase', color: '#94908a' }}>{label}</span>
<span style={{ fontSize: '0.95rem', color: '#f5f1e8', textAlign: 'right' }}>{value}</span>
</a>
);
}
+262 -339
View File
@@ -1,24 +1,46 @@
@import "tailwindcss";
@theme {
/* Light Mode Color Palette */
--color-primary: #ffffff;
--color-secondary: #f8f8f6;
--color-accent: #3b82f6;
--color-accent-hover: #2563eb;
/* === Luxury robotics — Black + Gold === */
--color-bg: #050505;
--color-bg-2: #0a0a0c;
--color-bg-3: #111114;
--color-bg-4: #18181c;
--color-surface: rgba(20, 18, 14, 0.7);
--color-primary: #050505;
--color-secondary: #0a0a0c;
/* Gold spectrum (matches YS Lootah brand) */
--color-gold: #c4a265;
--color-text-primary: #1a1a2e;
--color-text-secondary: #64748b;
--color-text-muted: #94a3b8;
--color-border: #e2e8f0;
--color-border-light: #cbd5e1;
--color-gold-light: #e0c896;
--color-gold-rich: #d4af6a;
--color-gold-bronze: #8b6f47;
--color-gold-deep: #6b5436;
/* Glassmorphism Colors - Light */
--color-glass-bg: rgba(255, 255, 255, 0.85);
--color-glass-border: rgba(0, 0, 0, 0.08);
--color-glass-highlight: rgba(255, 255, 255, 0.5);
/* Accent aliases */
--color-accent: #c4a265;
--color-accent-2: #d4af6a;
--color-accent-3: #8b6f47;
--color-accent-hover: #d4af6a;
/* Spacing & Sizing */
/* Text */
--color-text-primary: #f5f1e8;
--color-text-secondary: #cbc4b3;
--color-text-muted: #94908a;
--color-text-dim: #6b6862;
/* Borders */
--color-border: rgba(196, 162, 101, 0.18);
--color-border-strong: rgba(196, 162, 101, 0.36);
--color-border-light: rgba(196, 162, 101, 0.08);
--color-border-neutral: rgba(245, 241, 232, 0.08);
/* Glass */
--color-glass-bg: rgba(20, 18, 14, 0.55);
--color-glass-border: rgba(196, 162, 101, 0.22);
--color-glass-highlight: rgba(245, 241, 232, 0.05);
/* Spacing */
--spacing-xs: 0.25rem;
--spacing-sm: 0.5rem;
--spacing-md: 1rem;
@@ -26,15 +48,17 @@
--spacing-xl: 2rem;
--spacing-2xl: 3rem;
/* Border Radius */
/* Radius */
--radius-sm: 0.375rem;
--radius-md: 0.5rem;
--radius-lg: 0.75rem;
--radius-xl: 1rem;
--radius-2xl: 1.5rem;
/* Shadows */
--shadow-glow: 0 2px 20px rgba(0, 0, 0, 0.06);
--shadow-glow-lg: 0 4px 40px rgba(0, 0, 0, 0.08);
--shadow-glow: 0 0 30px rgba(196, 162, 101, 0.22);
--shadow-glow-lg: 0 0 60px rgba(196, 162, 101, 0.28);
--shadow-card: 0 14px 50px rgba(0, 0, 0, 0.55);
/* Transitions */
--transition-fast: 150ms ease;
@@ -42,101 +66,165 @@
--transition-slow: 300ms ease;
}
/* Base Styles */
html,
body {
margin: 0;
padding: 0;
min-height: 100vh;
background-color: var(--color-primary);
background-color: var(--color-bg);
color: var(--color-text-primary);
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
overflow-x: hidden;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
background:
radial-gradient(1200px 700px at 80% -10%, rgba(196, 162, 101, 0.10), transparent 60%),
radial-gradient(900px 500px at -10% 30%, rgba(196, 162, 101, 0.06), transparent 60%),
radial-gradient(800px 500px at 50% 110%, rgba(139, 111, 71, 0.10), transparent 60%),
linear-gradient(180deg, #030303 0%, #050505 50%, #030303 100%);
background-attachment: fixed;
}
/* Scroll Snap */
html {
scroll-behavior: smooth;
scroll-snap-type: y mandatory;
}
/* Focus Styles for Accessibility */
:focus-visible {
outline: 2px solid var(--color-accent);
outline: 2px solid var(--color-gold);
outline-offset: 2px;
}
/* Selection Styles */
::selection {
background-color: var(--color-accent);
background-color: rgba(196, 162, 101, 0.4);
color: #ffffff;
}
/* Glassmorphism Utilities - Light */
/* === Glassmorphism === */
.glass {
background: var(--color-glass-bg);
backdrop-filter: blur(18px);
-webkit-backdrop-filter: blur(18px);
border: 1px solid var(--color-glass-border);
box-shadow: var(--shadow-card);
}
.glass-panel {
background: var(--color-glass-bg);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid var(--color-glass-border);
box-shadow: var(--shadow-glow);
box-shadow: var(--shadow-card);
}
.glass-panel-highlight {
background: linear-gradient(
135deg,
var(--color-glass-highlight) 0%,
transparent 50%
);
background: linear-gradient(135deg, var(--color-glass-highlight) 0%, transparent 60%);
}
/* Hero animated gradient - Light */
/* === Typography gradients === */
.text-gradient {
background: linear-gradient(135deg, #ffffff 0%, #f5e9c8 40%, #d4af6a 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
color: transparent;
}
.text-gradient-accent {
background: linear-gradient(135deg, #e0c896 0%, #c4a265 50%, #8b6f47 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
color: transparent;
}
.text-gold {
color: var(--color-gold);
}
/* Metallic gold border */
.metallic-border {
position: relative;
}
.metallic-border::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
background: linear-gradient(135deg, rgba(224, 200, 150, 0.6), rgba(196, 162, 101, 0.25) 45%, rgba(139, 111, 71, 0.0) 100%);
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}
/* === Hero animated gradient === */
.hero-gradient {
position: absolute;
inset: 0;
background: radial-gradient(ellipse 80% 60% at 50% 40%, rgba(59, 130, 246, 0.04) 0%, transparent 60%),
radial-gradient(ellipse 60% 50% at 30% 70%, rgba(196, 162, 101, 0.03) 0%, transparent 50%),
linear-gradient(180deg, #f0f0ec 0%, #e8e8e4 50%, #f0f0ec 100%);
animation: heroShift 12s ease-in-out infinite alternate;
background:
radial-gradient(ellipse 70% 60% at 60% 40%, rgba(196, 162, 101, 0.20) 0%, transparent 60%),
radial-gradient(ellipse 60% 50% at 20% 70%, rgba(139, 111, 71, 0.18) 0%, transparent 55%),
radial-gradient(ellipse 80% 70% at 80% 100%, rgba(224, 200, 150, 0.14) 0%, transparent 60%),
linear-gradient(180deg, #050505 0%, #070605 50%, #030303 100%);
animation: heroShift 16s ease-in-out infinite alternate;
}
@keyframes heroShift {
0% {
background-position: 0% 0%, 0% 0%, 0% 0%;
opacity: 1;
}
50% {
opacity: 0.9;
}
100% {
background-position: 100% 100%, 100% 0%, 0% 0%;
opacity: 1;
}
0% { background-position: 0% 0%, 0% 0%, 0% 0%, 0% 0%; }
100% { background-position: 100% 100%, 80% 0%, 0% 100%, 0% 0%; }
}
/* Fade in up animation */
.fade-in-up {
animation: fadeInUp 0.8s cubic-bezier(0.16, 1, 0.3, 1) both;
/* Grid overlay */
.grid-overlay {
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(196, 162, 101, 0.04) 1px, transparent 1px),
linear-gradient(90deg, rgba(196, 162, 101, 0.04) 1px, transparent 1px);
background-size: 48px 48px;
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%);
pointer-events: none;
}
/* === Animations === */
.fade-in-up { animation: fadeInUp 0.8s cubic-bezier(0.16, 1, 0.3, 1) both; }
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(24px);
}
to {
opacity: 1;
transform: translateY(0);
}
from { opacity: 0; transform: translateY(24px); }
to { opacity: 1; transform: translateY(0); }
}
/* Scroll indicator - dark for light mode */
.shimmer {
background: linear-gradient(110deg, transparent 35%, rgba(224, 200, 150, 0.12) 50%, transparent 65%);
background-size: 200% 100%;
animation: shimmer 3.5s linear infinite;
}
@keyframes shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
.float-y { animation: floatY 6s ease-in-out infinite; }
@keyframes floatY {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-12px); }
}
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes pulseGlow {
0%, 100% { box-shadow: 0 0 0 0 rgba(196, 162, 101, 0.55), 0 0 40px rgba(196, 162, 101, 0.25); }
50% { box-shadow: 0 0 0 12px rgba(196, 162, 101, 0), 0 0 60px rgba(196, 162, 101, 0.45); }
}
/* Scroll indicator */
.scroll-indicator {
width: 1px;
height: 40px;
position: relative;
overflow: hidden;
}
.scroll-indicator::after {
content: '';
position: absolute;
@@ -144,58 +232,111 @@ html {
left: 0;
width: 1px;
height: 100%;
background: linear-gradient(180deg, #c4a265, transparent);
background: linear-gradient(180deg, var(--color-gold), transparent);
animation: scrollPulse 2s ease-in-out infinite;
}
@keyframes scrollPulse {
0%, 100% {
transform: translateY(-100%);
opacity: 0;
}
50% {
transform: translateY(0);
opacity: 1;
}
0%, 100% { transform: translateY(-100%); opacity: 0; }
50% { transform: translateY(0); opacity: 1; }
}
/* Spin animation for loaders */
@keyframes spin {
to {
transform: rotate(360deg);
}
/* === Scrollbar === */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: var(--color-bg-2); }
::-webkit-scrollbar-thumb { background: rgba(196, 162, 101, 0.25); border-radius: var(--radius-sm); }
::-webkit-scrollbar-thumb:hover { background: rgba(196, 162, 101, 0.55); }
/* === Buttons === */
.btn {
display: inline-flex;
align-items: center;
gap: 0.625rem;
padding: 0.875rem 1.75rem;
border-radius: 999px;
font-size: 0.85rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
text-decoration: none;
cursor: pointer;
transition: transform 0.25s cubic-bezier(0.16,1,0.3,1), box-shadow 0.25s ease, background 0.25s ease, color 0.25s ease;
border: 1px solid transparent;
white-space: nowrap;
}
.btn:hover { transform: translateY(-1px); }
.btn-primary {
background: linear-gradient(135deg, #e0c896 0%, #c4a265 55%, #8b6f47 100%);
color: #0b0905;
box-shadow: 0 8px 28px rgba(196, 162, 101, 0.35), inset 0 1px 0 rgba(255, 244, 220, 0.4);
}
.btn-primary:hover {
box-shadow: 0 12px 38px rgba(224, 200, 150, 0.55), inset 0 1px 0 rgba(255, 244, 220, 0.5);
}
/* Scrollbar Styling - Light */
::-webkit-scrollbar {
width: 8px;
height: 8px;
.btn-ghost {
background: rgba(245, 241, 232, 0.04);
color: #f5f1e8;
border-color: rgba(196, 162, 101, 0.28);
backdrop-filter: blur(12px);
}
.btn-ghost:hover {
background: rgba(196, 162, 101, 0.10);
border-color: rgba(196, 162, 101, 0.55);
color: #e0c896;
}
::-webkit-scrollbar-track {
background: var(--color-secondary);
.btn-outline {
background: transparent;
color: #e0c896;
border-color: rgba(196, 162, 101, 0.55);
}
.btn-outline:hover {
background: rgba(196, 162, 101, 0.10);
}
::-webkit-scrollbar-thumb {
background: var(--color-border-light);
border-radius: var(--radius-sm);
/* === Layout helpers === */
.container-wide { max-width: 1320px; margin: 0 auto; padding-left: clamp(1rem, 4vw, 2rem); padding-right: clamp(1rem, 4vw, 2rem); }
.eyebrow {
display: inline-flex;
align-items: center;
gap: 0.625rem;
font-size: 0.72rem;
letter-spacing: 0.34em;
text-transform: uppercase;
color: var(--color-text-muted);
font-weight: 600;
}
.eyebrow::before {
content: '';
width: 28px;
height: 1px;
background: linear-gradient(90deg, transparent, var(--color-gold));
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-text-muted);
/* === Cards === */
.card {
position: relative;
background: rgba(10, 9, 7, 0.72);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid var(--color-border);
border-radius: var(--radius-2xl);
transition: transform 0.4s cubic-bezier(0.16,1,0.3,1), border-color 0.3s, box-shadow 0.3s;
overflow: hidden;
}
.card:hover {
transform: translateY(-6px);
border-color: rgba(196, 162, 101, 0.55);
box-shadow: 0 20px 60px rgba(0,0,0,0.7), 0 0 40px rgba(196, 162, 101, 0.15);
}
/* Snap children */
.snap-section {
scroll-snap-align: start;
height: 100vh;
}
/* === Configurator (preserve previous theme rules) === */
.snap-section { scroll-snap-align: start; }
/* Responsive Layout Styles */
@media (max-width: 1024px) {
.glass-panel-responsive {
width: 360px !important;
}
.glass-panel-responsive { width: 360px !important; }
}
@media (max-width: 768px) {
@@ -203,7 +344,6 @@ html {
flex-direction: column-reverse !important;
height: 100dvh !important;
}
.glass-panel-responsive {
order: unset !important;
position: relative !important;
@@ -223,8 +363,6 @@ html {
z-index: 50;
padding-bottom: env(safe-area-inset-bottom, 0px) !important;
}
/* Expanded (fullscreen) state overrides */
.glass-panel-responsive.panel-expanded {
position: fixed !important;
top: 0 !important;
@@ -240,7 +378,6 @@ html {
-webkit-overflow-scrolling: touch !important;
padding-bottom: 0 !important;
}
.canvas-area {
height: 45dvh !important;
max-height: 45dvh !important;
@@ -248,59 +385,32 @@ html {
width: 100% !important;
flex: none !important;
}
#configurator {
height: 100dvh !important;
}
.mobile-handle {
display: flex !important;
}
#configurator { height: 100dvh !important; }
.mobile-handle { display: flex !important; }
}
/* Small phones */
@media (max-width: 480px) {
.glass-panel-responsive {
height: 58dvh !important;
max-height: 58dvh !important;
border-radius: 0.75rem 0.75rem 0 0 !important;
}
.canvas-area {
height: 42dvh !important;
max-height: 42dvh !important;
min-height: 160px !important;
}
.glass-panel-responsive header {
padding: 0.75rem 1rem !important;
}
.glass-panel-responsive > div[role="region"] {
padding: 1rem !important;
}
.glass-panel-responsive header { padding: 0.75rem 1rem !important; }
.glass-panel-responsive > div[role="region"] { padding: 1rem !important; }
}
/* Very small phones (iPhone SE, etc.) */
@media (max-width: 375px) {
.glass-panel-responsive {
height: 60dvh !important;
max-height: 60dvh !important;
}
.canvas-area {
height: 40dvh !important;
max-height: 40dvh !important;
min-height: 140px !important;
}
.glass-panel-responsive { height: 60dvh !important; max-height: 60dvh !important; }
.canvas-area { height: 40dvh !important; max-height: 40dvh !important; min-height: 140px !important; }
}
/* Landscape mobile */
@media (max-height: 500px) and (orientation: landscape) {
.layout-container {
flex-direction: row !important;
}
.layout-container { flex-direction: row !important; }
.glass-panel-responsive {
order: unset !important;
position: relative !important;
@@ -313,35 +423,15 @@ html {
border-left: 1px solid var(--color-border) !important;
overflow-y: auto !important;
}
.canvas-area {
height: 100% !important;
max-height: 100% !important;
width: auto !important;
flex: 1 !important;
}
.mobile-handle {
display: none !important;
}
.canvas-area { height: 100% !important; max-height: 100% !important; width: auto !important; flex: 1 !important; }
.mobile-handle { display: none !important; }
}
.mobile-handle {
display: none;
}
.mobile-handle { display: none; }
/* Hide expand button on desktop */
.panel-expand-btn {
display: none !important;
}
/* Show expand button only on mobile */
.panel-expand-btn { display: none !important; }
@media (max-width: 768px) {
.panel-expand-btn {
display: flex !important;
}
/* Expanded (fullscreen) panel state – position handled by React inline styles */
.panel-expand-btn { display: flex !important; }
.layout-expanded .canvas-area {
height: 0 !important;
max-height: 0 !important;
@@ -351,193 +441,26 @@ html {
}
@media (max-width: 480px) {
.glass-panel-responsive.panel-expanded {
height: 100dvh !important;
max-height: 100dvh !important;
}
.glass-panel-responsive.panel-expanded { height: 100dvh !important; max-height: 100dvh !important; }
}
@media (max-width: 375px) {
.glass-panel-responsive.panel-expanded {
height: 100dvh !important;
max-height: 100dvh !important;
}
.glass-panel-responsive.panel-expanded { height: 100dvh !important; max-height: 100dvh !important; }
}
/* ===== Scroll Overlay Responsive ===== */
/* Overlay glass panels (side sections) */
.overlay-panel {
max-width: 450px;
padding: 2.5rem;
border-radius: 1.5rem;
}
/* Overlay section positioning */
.overlay-section-left {
left: clamp(2rem, 6vw, 6rem);
}
.overlay-section-right {
right: clamp(2rem, 6vw, 6rem);
}
.overlay-heading {
font-size: clamp(2rem, 3.5vw, 3rem);
}
.overlay-hero-heading {
font-size: clamp(2.5rem, 5vw, 4.5rem);
}
.overlay-stat {
font-size: 1.8rem;
}
/* Tablet */
@media (max-width: 1024px) {
.overlay-panel {
max-width: 380px;
padding: 2rem;
}
}
/* Mobile */
@media (max-width: 768px) {
.overlay-section-left,
.overlay-section-right {
left: 50% !important;
right: auto !important;
transform: translateX(-50%);
width: 90vw;
max-width: 400px;
}
.overlay-section-left {
top: auto !important;
bottom: 4vh !important;
align-items: center !important;
}
.overlay-section-right {
top: auto !important;
bottom: 4vh !important;
align-items: center !important;
}
.overlay-panel {
max-width: 100%;
padding: 1.25rem;
border-radius: 1rem;
text-align: center !important;
}
.overlay-panel > div {
align-items: center !important;
}
.overlay-hero-heading {
font-size: clamp(1.8rem, 8vw, 2.8rem);
}
.overlay-heading {
font-size: clamp(1.5rem, 6vw, 2rem);
}
.overlay-stat {
font-size: 1.4rem;
}
.overlay-brand {
top: 4vh !important;
bottom: auto !important;
left: 50% !important;
transform: translateX(-50%);
width: 90vw;
}
.overlay-brand span {
font-size: 0.6rem !important;
letter-spacing: 0.25em !important;
}
.overlay-brand p {
font-size: 0.75rem !important;
}
.overlay-scroll-hint {
bottom: 1.5rem !important;
}
.overlay-cta-section {
top: 12vh !important;
bottom: auto !important;
}
.overlay-cta-btn {
padding: 1.2rem 3rem !important;
font-size: 1.1rem !important;
}
}
/* Hide overlay CTA on desktop since it takes over the screen */
@media (min-width: 769px) {
.overlay-cta-section {
display: none !important;
}
}
/* Specific class to ensure configurator shows only on desktop */
.desktop-configurator {
display: block !important;
position: relative;
z-index: 10;
background-color: #ffffff;
}
@media (max-width: 768px) {
.desktop-configurator {
display: none !important;
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
scroll-behavior: auto !important;
}
.hero-gradient { animation: none !important; }
}
/* Small phones */
@media (max-width: 480px) {
.overlay-section-left,
.overlay-section-right {
width: 92vw;
bottom: 4vh !important;
}
.overlay-panel {
padding: 1.25rem;
border-radius: 1rem;
}
.overlay-hero-heading {
font-size: clamp(1.5rem, 7vw, 2.2rem);
}
.overlay-heading {
font-size: clamp(1.3rem, 5.5vw, 1.8rem);
}
.overlay-stat {
font-size: 1.2rem;
}
}
/* Very small phones */
@media (max-width: 375px) {
.overlay-panel {
padding: 1rem;
}
.overlay-hero-heading {
font-size: 1.5rem;
}
.overlay-heading {
font-size: 1.3rem;
}
}
html, body { overflow-x: hidden; max-width: 100vw; }
+47
View File
@@ -0,0 +1,47 @@
import type { Metadata } from 'next';
import { Navbar } from '@/components/Navbar';
import { FooterAndContact } from '@/components/FooterAndContact';
import { IndustryUseCases } from '@/components/robotics/IndustryUseCases';
import { DemoCTA } from '@/components/robotics/DemoCTA';
import { MotionSection } from '@/components/ui/MotionSection';
export const metadata: Metadata = {
title: 'Robotics for UAE Industries — YS Lootah Robotics Dubai',
description:
'Robotics solutions for hospitality, restaurants, hotels, healthcare, education, security, warehouses, smart buildings, and government across the UAE.',
};
export default function IndustriesPage() {
return (
<>
<Navbar />
<main style={{ paddingTop: 'clamp(6rem, 10vw, 8rem)', paddingBottom: 'clamp(4rem, 8vw, 6rem)' }}>
<div className="container-wide" style={{ display: 'flex', flexDirection: 'column', gap: 'clamp(3rem, 5vw, 5rem)' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem', maxWidth: 760 }}>
<span className="eyebrow">Industries served · Dubai · UAE</span>
<h1 style={{ margin: 0, fontSize: 'clamp(2rem, 5vw, 3.4rem)', fontWeight: 300, lineHeight: 1.05, letterSpacing: '-0.03em' }}>
<span className="text-gradient" style={{ fontWeight: 500 }}>
Robotics solutions for UAE businesses.
</span>
</h1>
<p style={{ margin: 0, color: '#cbc4b3', fontSize: 'clamp(0.95rem, 2vw, 1.05rem)', lineHeight: 1.7 }}>
We deploy humanoid, quadruped, and service robots across industries that are reshaping how the UAE operates — every venue is matched to the right robot.
</p>
</div>
<MotionSection>
<IndustryUseCases />
</MotionSection>
<DemoCTA
title="Don't see your industry?"
description="If your venue is unusual, complex, or one-of-a-kind — that's exactly when we like to talk. Let's design the robotics fit."
/>
</div>
</main>
<FooterAndContact />
</>
);
}
+39 -5
View File
@@ -4,14 +4,48 @@ import { I18nProvider } from "@/components/I18nProvider";
import "./globals.css";
export const metadata: Metadata = {
title: "Lootah Robotics | G1 Configurator",
description: "3D Configurator for the G1 Robot by Lootah Robotics",
metadataBase: new URL('https://yslootahrobotics.com'),
title: {
default: 'YS Lootah Robotics — Exclusive UAE Access to Unitree & Pudu Robotics',
template: '%s | YS Lootah Robotics',
},
description:
'YS Lootah Robotics is the exclusive UAE sales destination for selected Unitree and Pudu Robotics solutions. Explore, configure, book demos, and deploy advanced robots across Dubai and the UAE.',
keywords: [
'YS Lootah Robotics',
'robotics Dubai',
'robotics UAE',
'exclusive robotics UAE',
'Unitree robots UAE',
'Pudu Robotics UAE',
'humanoid robots Dubai',
'quadruped robots UAE',
'service robots UAE',
'robot sales Dubai',
'book robot demo UAE',
'robotics showroom Dubai',
'robotics configuration UAE',
],
openGraph: {
type: 'website',
title: 'YS Lootah Robotics — Exclusive UAE Access to Unitree & Pudu Robotics',
description:
'Advanced robotics. Exclusive UAE access. Selected Unitree and Pudu solutions available through YS Lootah Robotics in Dubai.',
locale: 'en_AE',
siteName: 'YS Lootah Robotics',
},
twitter: {
card: 'summary_large_image',
title: 'YS Lootah Robotics — Exclusive UAE Access to Unitree & Pudu Robotics',
description:
'The UAE’s dedicated destination for Unitree and Pudu Robotics sales, demo inquiries, and business deployment.',
},
};
export const viewport: Viewport = {
width: "device-width",
width: 'device-width',
initialScale: 1,
themeColor: "#ffffff",
themeColor: '#050505',
};
export default function RootLayout({
@@ -20,7 +54,7 @@ export default function RootLayout({
children: React.ReactNode;
}) {
return (
<html lang="en" dir="ltr">
<html lang="en" dir="ltr" data-scroll-behavior="smooth">
<head>
<link rel="icon" href="/favicon.ico" sizes="any" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
+155 -29
View File
@@ -1,41 +1,167 @@
"use client";
import type { Metadata } from 'next';
import { Navbar } from '@/components/Navbar';
import { FooterAndContact } from '@/components/FooterAndContact';
import { Hero3DRobotics } from '@/components/robotics/Hero3DRobotics';
import { BrandShowcase } from '@/components/robotics/BrandShowcase';
import { RobotCategoryGrid } from '@/components/robotics/RobotCategoryGrid';
import { RobotProductCard } from '@/components/robotics/RobotProductCard';
import { IndustryUseCases } from '@/components/robotics/IndustryUseCases';
import { DemoCTA } from '@/components/robotics/DemoCTA';
import { ConfigureCTA } from '@/components/robotics/ConfigureCTA';
import { ExclusiveAccessSection } from '@/components/robotics/ExclusiveAccessSection';
import { MarqueeStrip } from '@/components/robotics/MarqueeStrip';
import { BentoGrid } from '@/components/robotics/BentoGrid';
import { HowItWorks } from '@/components/robotics/HowItWorks';
import { MotionSection } from '@/components/ui/MotionSection';
import { FEATURED_ROBOTS } from '@/data/robots';
import { useRef } from "react";
import { ClientOnly } from "@/components/ClientOnly";
import { ScrollScene } from "@/components/ScrollScene";
import { ScrollOverlays } from "@/components/ScrollOverlays";
import { FooterAndContact } from "@/components/FooterAndContact";
import { Navbar } from "@/components/Navbar";
export const metadata: Metadata = {
title: 'YS Lootah Robotics — Exclusive UAE Access to Unitree & Pudu Robotics',
description:
'YS Lootah Robotics is the exclusive UAE sales destination for selected Unitree and Pudu Robotics solutions. Explore, configure, book demos, and deploy advanced robots across Dubai and the UAE.',
keywords: [
'YS Lootah Robotics',
'robotics Dubai',
'robotics UAE',
'exclusive robotics UAE',
'Unitree robots UAE',
'Pudu Robotics UAE',
'humanoid robots Dubai',
'quadruped robots UAE',
'service robots UAE',
'robotics showroom Dubai',
'robotics configuration UAE',
'robot sales and demo UAE',
],
};
export default function HomePage() {
const scrollContainerRef = useRef<HTMLDivElement>(null);
return (
<>
<Navbar />
{/* Fixed 3D scene behind everything */}
<ClientOnly>
<ScrollScene scrollContainerRef={scrollContainerRef} />
</ClientOnly>
{/* Text overlays that fade based on scroll */}
<ScrollOverlays />
<main>
<Hero3DRobotics />
{/* Scroll spacer with snap sections -- 7 sections for the landing */}
<div ref={scrollContainerRef} style={{ position: "relative", zIndex: 1, pointerEvents: "none" }}>
<div className="snap-section" />
<div className="snap-section" />
<div className="snap-section" />
<div className="snap-section" />
<div className="snap-section" />
<div className="snap-section" />
<div className="snap-section" />
</div>
<MarqueeStrip />
<div className="snap-section" style={{ scrollSnapAlign: 'start' }}>
<FooterAndContact />
</div>
<MotionSection style={{ padding: 'clamp(3rem, 6vw, 5rem) 0' }} id="exclusive">
<div className="container-wide">
<ExclusiveAccessSection />
</div>
</MotionSection>
<MotionSection style={{ padding: 'clamp(3rem, 6vw, 5rem) 0' }} id="brands">
<div className="container-wide" style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
<SectionHeading
eyebrow="Featured robotics brands"
title="Unitree & Pudu — available exclusively in the UAE."
description="We hold exclusive UAE sales rights for selected Unitree and Pudu Robotics solutions — curated for proven performance in the field and full local support across Dubai."
/>
<BrandShowcase />
</div>
</MotionSection>
<MotionSection style={{ padding: 'clamp(3rem, 6vw, 5rem) 0' }} id="why">
<div className="container-wide" style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
<SectionHeading
eyebrow="Why YS Lootah Robotics"
title="A premium robotics partner — not a marketplace."
description="From inquiry to live deployment, we manage your robotics program end to end with a UAE-based team and exclusive access to Unitree and Pudu portfolios."
/>
<BentoGrid />
</div>
</MotionSection>
<MotionSection style={{ padding: 'clamp(3rem, 6vw, 5rem) 0' }} id="categories">
<div className="container-wide" style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
<SectionHeading
eyebrow="Robot categories"
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."
/>
<RobotCategoryGrid />
</div>
</MotionSection>
<MotionSection style={{ padding: 'clamp(3rem, 6vw, 5rem) 0' }} id="featured">
<div className="container-wide" style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
<SectionHeading
eyebrow="Featured robots"
title="Available exclusively in the UAE through YS Lootah Robotics."
description="A live snapshot of robots ready to demo at our Dubai showroom or deploy at your venue. Request a UAE quotation or book a live demo."
/>
<div
style={{
display: 'grid',
gap: '1.25rem',
gridTemplateColumns: 'repeat(auto-fit, minmax(min(280px, 100%), 1fr))',
}}
>
{FEATURED_ROBOTS.map((r, idx) => (
<RobotProductCard key={r.id} robot={r} priority={idx === 0} />
))}
</div>
</div>
</MotionSection>
<MotionSection style={{ padding: 'clamp(2rem, 5vw, 3.5rem) 0' }}>
<div className="container-wide">
<ConfigureCTA />
</div>
</MotionSection>
<MotionSection style={{ padding: 'clamp(3rem, 6vw, 5rem) 0' }} id="how-it-works">
<div className="container-wide" style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
<SectionHeading
eyebrow="How we work"
title="From inquiry to live deployment."
description="Three steps from your first message to a robot running in your venue — fully supported by our Dubai team."
/>
<HowItWorks />
</div>
</MotionSection>
<MotionSection style={{ padding: 'clamp(3rem, 6vw, 5rem) 0' }} id="industries">
<div className="container-wide" style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
<SectionHeading
eyebrow="Industries served"
title="Robotics solutions for UAE businesses."
description="From restaurants to security operators to government innovation programs — we deliver robotics fits for the venue."
/>
<IndustryUseCases limit={8} />
</div>
</MotionSection>
<MotionSection style={{ padding: 'clamp(3rem, 6vw, 5rem) 0 clamp(4rem, 8vw, 7rem)' }}>
<div className="container-wide">
<DemoCTA
title="Talk to our robotics team."
description="Book a live demo at our Dubai showroom or request a UAE quotation. Our local team responds within one business day."
primaryLabel="Book a live demo"
secondaryLabel="Talk to our team"
/>
</div>
</MotionSection>
</main>
<FooterAndContact />
</>
);
}
function SectionHeading({ eyebrow, title, description }: { eyebrow: string; title: string; description?: string }) {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem', maxWidth: 760 }}>
<span className="eyebrow">{eyebrow}</span>
<h2 style={{ margin: 0, fontSize: 'clamp(1.8rem, 4.5vw, 3rem)', lineHeight: 1.1, fontWeight: 300, letterSpacing: '-0.03em' }}>
<span className="text-gradient" style={{ fontWeight: 500 }}>{title}</span>
</h2>
{description && (
<p style={{ margin: 0, color: '#cbc4b3', fontSize: 'clamp(0.95rem, 2vw, 1.05rem)', lineHeight: 1.7 }}>
{description}
</p>
)}
</div>
);
}
+9 -9
View File
@@ -9,32 +9,32 @@ export default function PrivacyPolicyPage() {
<div style={{ background: '#050508', minHeight: '100vh', color: '#ffffff', fontFamily: 'Inter, sans-serif' }}>
<main style={{ maxWidth: '800px', margin: '0 auto', padding: '12rem 1.5rem 6rem', lineHeight: 1.8 }}>
<h1 style={{ fontSize: '3rem', fontWeight: 200, marginBottom: '1rem', letterSpacing: '-0.03em' }}>Privacy <span style={{ color: 'var(--color-gold)', fontWeight: 500 }}>Policy</span></h1>
<p style={{ color: '#94a3b8', fontSize: '1rem', marginBottom: '4rem' }}>Effective Date: {new Date().toLocaleDateString('en-AE')}</p>
<p style={{ color: '#94908a', fontSize: '1rem', marginBottom: '4rem' }}>Effective Date: {new Date().toLocaleDateString('en-AE')}</p>
<section style={{ marginBottom: '3rem' }}>
<h2 style={{ fontSize: '1.5rem', fontWeight: 500, color: '#e2e8f0', marginBottom: '1rem' }}>1. Information We Collect</h2>
<p style={{ color: '#cbd5e1', marginBottom: '1rem' }}>
<h2 style={{ fontSize: '1.5rem', fontWeight: 500, color: '#e8e0cf', marginBottom: '1rem' }}>1. Information We Collect</h2>
<p style={{ color: '#cbc4b3', marginBottom: '1rem' }}>
At YS Lootah Robotics, we collect information you provide directly to us when you request information, use the G1 Customizer, or contact us. This includes your name, email address, phone number, and any other information you choose to provide in your message.
</p>
</section>
<section style={{ marginBottom: '3rem' }}>
<h2 style={{ fontSize: '1.5rem', fontWeight: 500, color: '#e2e8f0', marginBottom: '1rem' }}>2. How We Use Your Information</h2>
<p style={{ color: '#cbd5e1', marginBottom: '1rem' }}>
<h2 style={{ fontSize: '1.5rem', fontWeight: 500, color: '#e8e0cf', marginBottom: '1rem' }}>2. How We Use Your Information</h2>
<p style={{ color: '#cbc4b3', marginBottom: '1rem' }}>
We use the information we collect to respond to your inquiries, deliver our robotics enterprise solutions, maintain our dashboard, and communicate with you about your custom humanoid configurations.
</p>
</section>
<section style={{ marginBottom: '3rem' }}>
<h2 style={{ fontSize: '1.5rem', fontWeight: 500, color: '#e2e8f0', marginBottom: '1rem' }}>3. Data Security</h2>
<p style={{ color: '#cbd5e1', marginBottom: '1rem' }}>
<h2 style={{ fontSize: '1.5rem', fontWeight: 500, color: '#e8e0cf', marginBottom: '1rem' }}>3. Data Security</h2>
<p style={{ color: '#cbc4b3', marginBottom: '1rem' }}>
We implement robust security measures designed to protect your personal information. Your contact data is stored securely in our private databases strictly for administrative and operational purposes.
</p>
</section>
<section style={{ marginBottom: '3rem' }}>
<h2 style={{ fontSize: '1.5rem', fontWeight: 500, color: '#e2e8f0', marginBottom: '1rem' }}>4. Contact Us</h2>
<p style={{ color: '#cbd5e1', marginBottom: '1rem' }}>
<h2 style={{ fontSize: '1.5rem', fontWeight: 500, color: '#e8e0cf', marginBottom: '1rem' }}>4. Contact Us</h2>
<p style={{ color: '#cbc4b3', marginBottom: '1rem' }}>
If you have questions or concerns about this Privacy Policy, please reach out to us at:
<br/><br/>
<strong>YS Lootah Robotics</strong><br/>
+125
View File
@@ -0,0 +1,125 @@
'use client';
import { Suspense, useMemo, useState } from 'react';
import { useSearchParams, useRouter, usePathname } from 'next/navigation';
import { ROBOTS, CATEGORY_LABELS, BRANDS, type RobotCategory, type RobotBrand } from '@/data/robots';
import { RobotProductCard } from '@/components/robotics/RobotProductCard';
import { ProductFilterTabs, type Tab } from '@/components/robotics/ProductFilterTabs';
type Filter = { brand: RobotBrand | 'all'; category: RobotCategory | 'all' };
function CatalogInner() {
const params = useSearchParams();
const router = useRouter();
const pathname = usePathname();
const initialBrand = (params.get('brand') as RobotBrand) || 'all';
const initialCategory = (params.get('category') as RobotCategory) || 'all';
const [filter, setFilter] = useState<Filter>({ brand: initialBrand, category: initialCategory });
const filtered = useMemo(() => {
return ROBOTS.filter((r) => {
if (filter.brand !== 'all' && r.brand !== filter.brand) return false;
if (filter.category !== 'all' && !r.categories.includes(filter.category)) return false;
return true;
});
}, [filter]);
const updateFilter = (next: Partial<Filter>) => {
const newFilter = { ...filter, ...next };
setFilter(newFilter);
const sp = new URLSearchParams();
if (newFilter.brand !== 'all') sp.set('brand', newFilter.brand);
if (newFilter.category !== 'all') sp.set('category', newFilter.category);
const qs = sp.toString();
router.replace(qs ? `${pathname}?${qs}` : pathname, { scroll: false });
};
const brandTabs: Tab[] = [
{ id: 'all', label: 'All brands' },
...(Object.keys(BRANDS) as RobotBrand[]).map((b) => ({
id: b,
label: BRANDS[b].name.split(' ')[0],
accent: BRANDS[b].accent,
})),
];
const categoryTabs: Tab[] = [
{ id: 'all', label: 'All' },
...(Object.keys(CATEGORY_LABELS) as RobotCategory[]).map((c) => ({
id: c,
label: CATEGORY_LABELS[c],
})),
];
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
<div className="glass" style={{ padding: '1rem 1.25rem', borderRadius: '1.25rem', display: 'flex', flexWrap: 'wrap', gap: '1.5rem' }}>
<ProductFilterTabs
title="Brand"
tabs={brandTabs}
activeId={filter.brand}
onChange={(id) => updateFilter({ brand: id as RobotBrand | 'all' })}
/>
<ProductFilterTabs
title="Category"
tabs={categoryTabs}
activeId={filter.category}
onChange={(id) => updateFilter({ category: id as RobotCategory | 'all' })}
/>
</div>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: '0.5rem' }}>
<span style={{ color: '#94908a', fontSize: '0.85rem' }}>
{filtered.length} robot{filtered.length === 1 ? '' : 's'} found
</span>
{(filter.brand !== 'all' || filter.category !== 'all') && (
<button
type="button"
onClick={() => updateFilter({ brand: 'all', category: 'all' })}
style={{
background: 'transparent',
border: 'none',
color: '#e0c896',
fontSize: '0.78rem',
letterSpacing: '0.14em',
textTransform: 'uppercase',
fontWeight: 600,
cursor: 'pointer',
}}
>
Clear filters
</button>
)}
</div>
{filtered.length === 0 ? (
<div className="card" style={{ padding: '2rem', textAlign: 'center', color: '#cbc4b3' }}>
No robots match these filters yet. Try a different brand or category, or{' '}
<a href="/contact/" style={{ color: '#e0c896', textDecoration: 'none' }}>contact us</a> for tailored options.
</div>
) : (
<div
style={{
display: 'grid',
gap: '1.25rem',
gridTemplateColumns: 'repeat(auto-fit, minmax(min(280px, 100%), 1fr))',
}}
>
{filtered.map((r) => (
<RobotProductCard key={r.id} robot={r} />
))}
</div>
)}
</div>
);
}
export function CatalogClient() {
return (
<Suspense fallback={<div className="card" style={{ padding: '2rem', color: '#cbc4b3' }}>Loading catalog…</div>}>
<CatalogInner />
</Suspense>
);
}
+226
View File
@@ -0,0 +1,226 @@
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import Link from 'next/link';
import { Navbar } from '@/components/Navbar';
import { FooterAndContact } from '@/components/FooterAndContact';
import { InquiryForm } from '@/components/robotics/InquiryForm';
import { RobotProductCard } from '@/components/robotics/RobotProductCard';
import { DemoCTA } from '@/components/robotics/DemoCTA';
import { ConfigureCTA } from '@/components/robotics/ConfigureCTA';
import { ProductSpecTable } from '@/components/robotics/ProductSpecTable';
import { ProductGallery } from '@/components/robotics/ProductGallery';
import { MotionSection } from '@/components/ui/MotionSection';
import { ROBOTS, getRobotBySlug, BRANDS, CATEGORY_LABELS } from '@/data/robots';
type Params = { slug: string };
export function generateStaticParams() {
return ROBOTS.map((r) => ({ slug: r.slug }));
}
export async function generateMetadata({ params }: { params: Promise<Params> }): Promise<Metadata> {
const { slug } = await params;
const robot = getRobotBySlug(slug);
if (!robot) return { title: 'Robot not found — YS Lootah Robotics' };
return {
title: `${robot.brandLabel} ${robot.name} — Available in Dubai | YS Lootah Robotics`,
description: `${robot.shortDescription} Available through YS Lootah Robotics in Dubai — request a price or book a live demo.`,
keywords: [robot.brandLabel, robot.name, 'Dubai', 'UAE', CATEGORY_LABELS[robot.category], 'robotics'],
};
}
export default async function RobotDetailPage({ params }: { params: Promise<Params> }) {
const { slug } = await params;
const robot = getRobotBySlug(slug);
if (!robot) notFound();
const brand = BRANDS[robot.brand];
const related = ROBOTS.filter((r) => r.id !== robot.id && (r.brand === robot.brand || r.category === robot.category)).slice(0, 3);
return (
<>
<Navbar />
<main style={{ paddingTop: 'clamp(6rem, 10vw, 8rem)', paddingBottom: 'clamp(4rem, 8vw, 6rem)' }}>
<div className="container-wide" style={{ display: 'flex', flexDirection: 'column', gap: 'clamp(3rem, 6vw, 5rem)' }}>
<nav style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem', fontSize: '0.78rem', letterSpacing: '0.14em', textTransform: 'uppercase', color: '#94908a' }}>
<Link href="/" style={{ color: '#94908a', textDecoration: 'none' }}>Home</Link>
<span>/</span>
<Link href="/robots/" style={{ color: '#94908a', textDecoration: 'none' }}>Robots</Link>
<span>/</span>
<span style={{ color: '#cbc4b3' }}>{robot.name}</span>
</nav>
<section
style={{
display: 'grid',
gap: 'clamp(2rem, 5vw, 3rem)',
gridTemplateColumns: 'repeat(auto-fit, minmax(min(320px, 100%), 1fr))',
alignItems: 'center',
}}
>
<div style={{ position: 'relative' }}>
<ProductGallery
images={[{ src: robot.image, alt: `${robot.brandLabel} ${robot.name}` }]}
accent={robot.accent}
/>
{robot.imageType === 'placeholder' && (
<span
style={{
position: 'absolute',
bottom: '0.75rem',
left: '0.75rem',
padding: '0.3rem 0.55rem',
borderRadius: 999,
background: 'rgba(5, 5, 5,0.6)',
border: '1px solid rgba(196, 162, 101,0.2)',
color: '#94908a',
fontSize: '0.62rem',
letterSpacing: '0.2em',
textTransform: 'uppercase',
}}
>
Brand visual placeholder
</span>
)}
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
<span className="eyebrow" style={{ color: robot.accent }}>
<span style={{ width: 6, height: 6, borderRadius: 999, background: robot.accent }} /> {brand.name} · {CATEGORY_LABELS[robot.category]}
</span>
<h1 style={{ margin: 0, fontSize: 'clamp(2rem, 5vw, 3.4rem)', fontWeight: 300, lineHeight: 1.05, letterSpacing: '-0.03em' }}>
<span className="text-gradient" style={{ fontWeight: 500 }}>
{robot.name}
</span>
</h1>
<p style={{ margin: 0, color: '#e0c896', fontSize: '1.05rem', lineHeight: 1.5 }}>{robot.tagline}</p>
<p style={{ margin: 0, color: '#cbc4b3', fontSize: '1rem', lineHeight: 1.7 }}>{robot.longDescription}</p>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.625rem' }}>
<a className="btn btn-primary" href="#inquire">Request quotation</a>
<a className="btn btn-ghost" href="/book-demo/">Book a live demo</a>
{robot.configureSlug && (
<a className="btn btn-outline" href={robot.configureSlug}>Configure for your business</a>
)}
{robot.officialUrl && (
<a className="btn btn-ghost" href={robot.officialUrl} target="_blank" rel="noopener noreferrer">
Brand site
</a>
)}
</div>
</div>
</section>
<MotionSection>
<div
style={{
display: 'grid',
gap: '1.5rem',
gridTemplateColumns: 'repeat(auto-fit, minmax(min(280px, 100%), 1fr))',
}}
>
<Block title="Key features">
<ul style={{ margin: 0, paddingLeft: '1.1rem', display: 'flex', flexDirection: 'column', gap: '0.6rem' }}>
{robot.features.map((f) => (
<li key={f} style={{ color: '#cbc4b3', lineHeight: 1.6 }}>{f}</li>
))}
</ul>
</Block>
<Block title="Use cases">
<ul style={{ margin: 0, paddingLeft: '1.1rem', display: 'flex', flexDirection: 'column', gap: '0.6rem' }}>
{robot.useCases.map((u) => (
<li key={u} style={{ color: '#cbc4b3', lineHeight: 1.6 }}>{u}</li>
))}
</ul>
</Block>
<Block title="Technical specs">
<ProductSpecTable specs={robot.specs} />
</Block>
</div>
</MotionSection>
<MotionSection id="inquire">
<div
style={{
display: 'grid',
gap: '1.5rem',
gridTemplateColumns: 'repeat(auto-fit, minmax(min(300px, 100%), 1fr))',
}}
>
<div className="card" style={{ padding: 'clamp(1.5rem, 4vw, 2.25rem)' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem', marginBottom: '1.5rem' }}>
<span className="eyebrow">Inquiry</span>
<h2 style={{ margin: 0, fontSize: 'clamp(1.5rem, 3vw, 2rem)', fontWeight: 400, letterSpacing: '-0.02em' }}>
<span className="text-gradient" style={{ fontWeight: 500 }}>
Request a quotation for {robot.name}.
</span>
</h2>
<p style={{ margin: 0, color: '#cbc4b3', lineHeight: 1.7 }}>
Tell us about your venue, timeline, and use case. We will respond with availability, configuration options, and pricing for the UAE.
</p>
</div>
<InquiryForm defaultRobot={`${robot.brandLabel} ${robot.name}`} defaultCategory={robot.category} intent="quotation" />
</div>
<div className="card" style={{ padding: 'clamp(1.5rem, 4vw, 2.25rem)', display: 'flex', flexDirection: 'column', gap: '1rem' }}>
<span className="eyebrow">Talk to an advisor</span>
<h3 style={{ margin: 0, fontSize: '1.25rem', fontWeight: 600 }}>Prefer a quick conversation?</h3>
<p style={{ margin: 0, color: '#cbc4b3', lineHeight: 1.7 }}>
Call our Dubai team or message us on WhatsApp — we will share availability and demo slots for {robot.name}.
</p>
<a className="btn btn-ghost" href="tel:+971559482728">Call +971 55 948 2728</a>
<a className="btn btn-outline" href="https://wa.me/971559482728" target="_blank" rel="noopener noreferrer">WhatsApp us</a>
<a className="btn btn-ghost" href="mailto:info@yslootahtech.com">Email info@yslootahtech.com</a>
</div>
</div>
</MotionSection>
<MotionSection>
<ConfigureCTA
title={`Configure ${robot.name} for your business.`}
description="Choose persona, attire, color, and accessories — visualize your deployment before requesting a quotation."
/>
</MotionSection>
{related.length > 0 && (
<MotionSection>
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
<div>
<span className="eyebrow">Related robots</span>
<h2 style={{ margin: '0.5rem 0 0', fontSize: 'clamp(1.5rem, 3vw, 2rem)', fontWeight: 400 }}>
<span className="text-gradient" style={{ fontWeight: 500 }}>You might also consider…</span>
</h2>
</div>
<div
style={{
display: 'grid',
gap: '1.25rem',
gridTemplateColumns: 'repeat(auto-fit, minmax(min(280px, 100%), 1fr))',
}}
>
{related.map((r) => (
<RobotProductCard key={r.id} robot={r} />
))}
</div>
</div>
</MotionSection>
)}
<DemoCTA title="Ready to bring this robot to your venue?" />
</div>
</main>
<FooterAndContact />
</>
);
}
function Block({ title, children }: { title: string; children: React.ReactNode }) {
return (
<div className="card" style={{ padding: '1.5rem' }}>
<div className="eyebrow" style={{ marginBottom: '1rem' }}>{title}</div>
{children}
</div>
);
}
+37
View File
@@ -0,0 +1,37 @@
import type { Metadata } from 'next';
import { Navbar } from '@/components/Navbar';
import { FooterAndContact } from '@/components/FooterAndContact';
import { CatalogClient } from './CatalogClient';
import { ROBOTS } from '@/data/robots';
export const metadata: Metadata = {
title: 'Robots Catalog — YS Lootah Robotics Dubai',
description:
'Explore selected humanoid, quadruped, service, delivery, hospitality, and cleaning robots from Unitree and Pudu — available exclusively in the UAE through YS Lootah Robotics.',
};
export default function RobotsPage() {
return (
<>
<Navbar />
<main style={{ paddingTop: 'clamp(6rem, 10vw, 8rem)', paddingBottom: 'clamp(4rem, 8vw, 6rem)' }}>
<div className="container-wide" style={{ display: 'flex', flexDirection: 'column', gap: '2.5rem' }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem', maxWidth: 760 }}>
<span className="eyebrow">Robot Catalog · Dubai · UAE</span>
<h1 style={{ margin: 0, fontSize: 'clamp(2rem, 5vw, 3.4rem)', fontWeight: 300, lineHeight: 1.05, letterSpacing: '-0.03em' }}>
<span className="text-gradient" style={{ fontWeight: 500 }}>
Robots ready to deploy across the UAE.
</span>
</h1>
<p style={{ margin: 0, color: '#cbc4b3', fontSize: 'clamp(0.95rem, 2vw, 1.05rem)', lineHeight: 1.7 }}>
Browse {ROBOTS.length}+ models across our portfolio. Filter by brand or category, then request a price or book a live demo at our Dubai showroom.
</p>
</div>
<CatalogClient />
</div>
</main>
<FooterAndContact />
</>
);
}
+24 -27
View File
@@ -1,32 +1,29 @@
import { MetadataRoute } from 'next';
import { ROBOTS } from '@/data/robots';
const baseUrl = 'https://yslootahrobotics.com';
export default function sitemap(): MetadataRoute.Sitemap {
const baseUrl = 'https://lootahrobotics.com'; // Adjust to your actual production domain
return [
{
url: `${baseUrl}`,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 1,
},
{
url: `${baseUrl}/configure`,
lastModified: new Date(),
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: `${baseUrl}/privacy-policy`,
lastModified: new Date(),
changeFrequency: 'yearly',
priority: 0.5,
},
{
url: `${baseUrl}/terms-of-service`,
lastModified: new Date(),
changeFrequency: 'yearly',
priority: 0.5,
},
const now = new Date();
const staticRoutes: MetadataRoute.Sitemap = [
{ url: `${baseUrl}/`, lastModified: now, changeFrequency: 'weekly', priority: 1 },
{ url: `${baseUrl}/robots/`, lastModified: now, changeFrequency: 'weekly', priority: 0.9 },
{ url: `${baseUrl}/brands/`, lastModified: now, changeFrequency: 'weekly', priority: 0.8 },
{ url: `${baseUrl}/industries/`, lastModified: now, changeFrequency: 'weekly', priority: 0.8 },
{ url: `${baseUrl}/about/`, lastModified: now, changeFrequency: 'monthly', priority: 0.6 },
{ url: `${baseUrl}/contact/`, lastModified: now, changeFrequency: 'monthly', priority: 0.7 },
{ url: `${baseUrl}/book-demo/`, lastModified: now, changeFrequency: 'monthly', priority: 0.8 },
{ url: `${baseUrl}/configure/`, lastModified: now, changeFrequency: 'monthly', priority: 0.8 },
{ url: `${baseUrl}/privacy-policy/`, lastModified: now, changeFrequency: 'yearly', priority: 0.3 },
{ url: `${baseUrl}/terms-of-service/`, lastModified: now, changeFrequency: 'yearly', priority: 0.3 },
];
const robotRoutes: MetadataRoute.Sitemap = ROBOTS.map((r) => ({
url: `${baseUrl}/robots/${r.slug}/`,
lastModified: now,
changeFrequency: 'monthly',
priority: 0.7,
}));
return [...staticRoutes, ...robotRoutes];
}
+9 -9
View File
@@ -9,32 +9,32 @@ export default function TermsOfServicePage() {
<div style={{ background: '#050508', minHeight: '100vh', color: '#ffffff', fontFamily: 'Inter, sans-serif' }}>
<main style={{ maxWidth: '800px', margin: '0 auto', padding: '12rem 1.5rem 6rem', lineHeight: 1.8 }}>
<h1 style={{ fontSize: '3rem', fontWeight: 200, marginBottom: '1rem', letterSpacing: '-0.03em' }}>Terms of <span style={{ color: 'var(--color-gold)', fontWeight: 500 }}>Service</span></h1>
<p style={{ color: '#94a3b8', fontSize: '1rem', marginBottom: '4rem' }}>Effective Date: {new Date().toLocaleDateString('en-AE')}</p>
<p style={{ color: '#94908a', fontSize: '1rem', marginBottom: '4rem' }}>Effective Date: {new Date().toLocaleDateString('en-AE')}</p>
<section style={{ marginBottom: '3rem' }}>
<h2 style={{ fontSize: '1.5rem', fontWeight: 500, color: '#e2e8f0', marginBottom: '1rem' }}>1. Acceptance of Terms</h2>
<p style={{ color: '#cbd5e1', marginBottom: '1rem' }}>
<h2 style={{ fontSize: '1.5rem', fontWeight: 500, color: '#e8e0cf', marginBottom: '1rem' }}>1. Acceptance of Terms</h2>
<p style={{ color: '#cbc4b3', marginBottom: '1rem' }}>
By accessing and utilizing the YS Lootah Robotics web platform and the G1 Configurator, you accept and agree to be bound by the terms and provisions of this agreement.
</p>
</section>
<section style={{ marginBottom: '3rem' }}>
<h2 style={{ fontSize: '1.5rem', fontWeight: 500, color: '#e2e8f0', marginBottom: '1rem' }}>2. Use of the Site & Configurator</h2>
<p style={{ color: '#cbd5e1', marginBottom: '1rem' }}>
<h2 style={{ fontSize: '1.5rem', fontWeight: 500, color: '#e8e0cf', marginBottom: '1rem' }}>2. Use of the Site & Configurator</h2>
<p style={{ color: '#cbc4b3', marginBottom: '1rem' }}>
The 3D G1 Configurator is provided for informational and demonstrative purposes to showcase the capabilities of YS Lootah technologies. You agree to use this site strictly for lawful purposes resulting in enterprise robotics inquiries and configurations.
</p>
</section>
<section style={{ marginBottom: '3rem' }}>
<h2 style={{ fontSize: '1.5rem', fontWeight: 500, color: '#e2e8f0', marginBottom: '1rem' }}>3. Intellectual Property Rights</h2>
<p style={{ color: '#cbd5e1', marginBottom: '1rem' }}>
<h2 style={{ fontSize: '1.5rem', fontWeight: 500, color: '#e8e0cf', marginBottom: '1rem' }}>3. Intellectual Property Rights</h2>
<p style={{ color: '#cbc4b3', marginBottom: '1rem' }}>
All original content on this website, including but not limited to text, graphics, 3D models (GLB files), logos, and software, is the exclusive property of YS Lootah Robotics and is protected by United Arab Emirates and international copyright laws.
</p>
</section>
<section style={{ marginBottom: '3rem' }}>
<h2 style={{ fontSize: '1.5rem', fontWeight: 500, color: '#e2e8f0', marginBottom: '1rem' }}>4. Disclaimer of Warranties</h2>
<p style={{ color: '#cbd5e1', marginBottom: '1rem' }}>
<h2 style={{ fontSize: '1.5rem', fontWeight: 500, color: '#e8e0cf', marginBottom: '1rem' }}>4. Disclaimer of Warranties</h2>
<p style={{ color: '#cbc4b3', marginBottom: '1rem' }}>
The materials on our platform are provided "as is". We make no warranties, expressed or implied, and hereby disclaim to the fullest extent permitted by law all warranties regarding the immediate enterprise availability of the rendered concepts displayed in the Configurator.
</p>
</section>