feat: add accessory detail and index pages with dynamic routing and metadata
This commit is contained in:
parent
5271914b16
commit
2d32a1e722
Binary file not shown.
|
Before Width: | Height: | Size: 60 MiB |
BIN
public/images/accessories/unitree-dex5-1.png
Normal file
BIN
public/images/accessories/unitree-dex5-1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 278 KiB |
525
src/app/accessories/[slug]/page.tsx
Normal file
525
src/app/accessories/[slug]/page.tsx
Normal file
@ -0,0 +1,525 @@
|
||||
import type { Metadata } from 'next';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { notFound } from 'next/navigation';
|
||||
import ChevronRight from 'lucide-react/dist/esm/icons/chevron-right';
|
||||
import Check from 'lucide-react/dist/esm/icons/check';
|
||||
import ArrowRight from 'lucide-react/dist/esm/icons/arrow-right';
|
||||
import { Navbar } from '@/components/Navbar';
|
||||
import { FooterAndContact } from '@/components/FooterAndContact';
|
||||
import { MotionSection } from '@/components/ui/MotionSection';
|
||||
import { CTAButton } from '@/components/ui/CTAButton';
|
||||
import { ACCESSORIES, GROUP_LABELS, getAccessoryBySlug, type Accessory } from '@/data/accessories';
|
||||
|
||||
type Params = { slug: string };
|
||||
|
||||
export function generateStaticParams() {
|
||||
return ACCESSORIES.map((a) => ({ slug: a.slug }));
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: { params: Promise<Params> }): Promise<Metadata> {
|
||||
const { slug } = await params;
|
||||
const a = getAccessoryBySlug(slug);
|
||||
if (!a) return { title: 'Accessory not found · YS Lootah Robotics' };
|
||||
return {
|
||||
title: `${a.brandLabel} ${a.name} · Available in UAE | YS Lootah Robotics`,
|
||||
description: `${a.tagline} ${a.description} Available through YS Lootah Robotics in Dubai.`,
|
||||
alternates: { canonical: `/accessories/${a.slug}/` },
|
||||
openGraph: {
|
||||
title: `${a.brandLabel} ${a.name}`,
|
||||
description: a.tagline,
|
||||
url: `/accessories/${a.slug}/`,
|
||||
type: 'website',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default async function AccessoryDetailPage({ params }: { params: Promise<Params> }) {
|
||||
const { slug } = await params;
|
||||
const accessory = getAccessoryBySlug(slug);
|
||||
if (!accessory) notFound();
|
||||
|
||||
const accent = accessory.accent;
|
||||
const related = ACCESSORIES
|
||||
.filter((a) => a.id !== accessory.id && (a.group === accessory.group || a.brand === accessory.brand))
|
||||
.slice(0, 3);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
|
||||
<main className="acc-main">
|
||||
<div className="container-wide acc-stack">
|
||||
{/* BREADCRUMBS */}
|
||||
<nav aria-label="Breadcrumb" className="acc-breadcrumbs">
|
||||
<ol>
|
||||
<li><Link href="/">Home</Link></li>
|
||||
<li aria-hidden><ChevronRight size={12} /></li>
|
||||
<li><Link href="/robots/">Robots</Link></li>
|
||||
<li aria-hidden><ChevronRight size={12} /></li>
|
||||
<li><Link href="/accessories/">Accessories</Link></li>
|
||||
<li aria-hidden><ChevronRight size={12} /></li>
|
||||
<li aria-current="page">{accessory.name}</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
{/* HERO */}
|
||||
<MotionSection>
|
||||
<section className="acc-hero" style={{ ['--acc' as string]: accent }}>
|
||||
<div className="acc-hero-glow" aria-hidden />
|
||||
<div className="acc-hero-grid" aria-hidden />
|
||||
|
||||
<div className="acc-hero-inner">
|
||||
<div className="acc-hero-media">
|
||||
<Image
|
||||
src={accessory.image}
|
||||
alt={`${accessory.name} product image`}
|
||||
fill
|
||||
sizes="(max-width: 920px) 90vw, 520px"
|
||||
style={{ objectFit: 'contain', padding: 'clamp(1.5rem, 3.5vw, 2.5rem)' }}
|
||||
priority
|
||||
/>
|
||||
<span className="acc-hero-stage" aria-hidden />
|
||||
<span className="acc-hero-grid-inner" aria-hidden />
|
||||
</div>
|
||||
|
||||
<div className="acc-hero-copy">
|
||||
<span className="eyebrow acc-hero-eyebrow">
|
||||
<span className="acc-dot" />
|
||||
{accessory.brandLabel} · {GROUP_LABELS[accessory.group]}
|
||||
</span>
|
||||
<h1 className="acc-hero-title">
|
||||
<span className="text-gradient">{accessory.name}</span>
|
||||
</h1>
|
||||
<p className="acc-hero-tagline">{accessory.tagline}</p>
|
||||
<p className="acc-hero-desc">{accessory.description}</p>
|
||||
|
||||
<div className="acc-hero-actions">
|
||||
<CTAButton href="#inquire" variant="primary" size="lg" arrow="right">
|
||||
Request UAE Quotation
|
||||
</CTAButton>
|
||||
<CTAButton href="/book-demo/" variant="secondary" size="lg" arrow="right">
|
||||
Book a Live Demo
|
||||
</CTAButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</MotionSection>
|
||||
|
||||
{/* FEATURES + COMPATIBILITY */}
|
||||
<MotionSection>
|
||||
<div className="acc-grid">
|
||||
<article className="acc-block">
|
||||
<header>
|
||||
<span className="eyebrow">Key features</span>
|
||||
<h2>What makes it different.</h2>
|
||||
</header>
|
||||
<ul className="acc-feature-list">
|
||||
{accessory.features.map((f) => (
|
||||
<li key={f}>
|
||||
<span className="acc-feature-check"><Check size={12} strokeWidth={2.4} /></span>
|
||||
{f}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<article className="acc-block">
|
||||
<header>
|
||||
<span className="eyebrow">Compatibility</span>
|
||||
<h2>Where it deploys.</h2>
|
||||
</header>
|
||||
{accessory.compatibility && accessory.compatibility.length > 0 ? (
|
||||
<ul className="acc-compat-list">
|
||||
{accessory.compatibility.map((c) => (
|
||||
<li key={c}>
|
||||
<span className="acc-compat-icon">·</span>
|
||||
{c}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p className="acc-block-empty">Compatible across the YS Lootah Robotics catalog. Talk to our Dubai team for fit-check.</p>
|
||||
)}
|
||||
|
||||
<div className="acc-meta-grid">
|
||||
<div className="acc-meta">
|
||||
<span>Brand</span>
|
||||
<strong>{accessory.brandLabel}</strong>
|
||||
</div>
|
||||
<div className="acc-meta">
|
||||
<span>Category</span>
|
||||
<strong>{GROUP_LABELS[accessory.group]}</strong>
|
||||
</div>
|
||||
<div className="acc-meta">
|
||||
<span>Availability</span>
|
||||
<strong>UAE · Dubai</strong>
|
||||
</div>
|
||||
<div className="acc-meta">
|
||||
<span>Demo</span>
|
||||
<strong>Showroom or on-site</strong>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</MotionSection>
|
||||
|
||||
{/* INQUIRY */}
|
||||
<MotionSection>
|
||||
<section id="inquire" className="acc-cta">
|
||||
<div className="acc-cta-glow" aria-hidden />
|
||||
<div className="acc-cta-inner">
|
||||
<div className="acc-cta-text">
|
||||
<span className="eyebrow">Inquiry</span>
|
||||
<h2>Bring the {accessory.name} to your operation.</h2>
|
||||
<p>
|
||||
Tell us how you plan to use it, what robot platform you’re pairing it with, and your timeline. Our Dubai team will respond with UAE pricing, availability, and demo slots.
|
||||
</p>
|
||||
</div>
|
||||
<div className="acc-cta-actions">
|
||||
<CTAButton href="/contact/" variant="primary" size="lg" arrow="up-right">
|
||||
Request UAE Quotation
|
||||
</CTAButton>
|
||||
<CTAButton href="/book-demo/" variant="secondary" size="lg" arrow="right">
|
||||
Book a Live Demo
|
||||
</CTAButton>
|
||||
<CTAButton
|
||||
href="https://wa.me/971559482728"
|
||||
external
|
||||
variant="ghost"
|
||||
size="lg"
|
||||
arrow="up-right"
|
||||
ariaLabel="Chat with YS Lootah Robotics on WhatsApp"
|
||||
>
|
||||
WhatsApp Us
|
||||
</CTAButton>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</MotionSection>
|
||||
|
||||
{/* RELATED */}
|
||||
{related.length > 0 && (
|
||||
<MotionSection>
|
||||
<section className="acc-related">
|
||||
<header>
|
||||
<span className="eyebrow">Related accessories</span>
|
||||
<h2>You might also need…</h2>
|
||||
</header>
|
||||
<div className="acc-related-grid">
|
||||
{related.map((r) => (
|
||||
<RelatedCard key={r.id} accessory={r} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</MotionSection>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<FooterAndContact />
|
||||
|
||||
<style>{`
|
||||
.acc-main {
|
||||
padding-top: clamp(5rem, 9vw, 7rem);
|
||||
padding-bottom: clamp(2rem, 5vw, 4rem);
|
||||
}
|
||||
.acc-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: clamp(2.5rem, 5vw, 4rem);
|
||||
}
|
||||
|
||||
/* BREADCRUMBS */
|
||||
.acc-breadcrumbs ol {
|
||||
list-style: none; margin: 0; padding: 0;
|
||||
display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem;
|
||||
font-size: 0.7rem; letter-spacing: 0.16em; text-transform: uppercase;
|
||||
color: #8891C7;
|
||||
}
|
||||
.acc-breadcrumbs a { color: #8891C7; text-decoration: none; transition: color 0.25s; }
|
||||
.acc-breadcrumbs a:hover { color: #FFFFFF; }
|
||||
.acc-breadcrumbs li[aria-current="page"] { color: #FFFFFF; }
|
||||
.acc-breadcrumbs li[aria-hidden] { display: inline-flex; align-items: center; color: #4a4f63; }
|
||||
|
||||
/* HERO */
|
||||
.acc-hero {
|
||||
position: relative; overflow: hidden;
|
||||
border-radius: 26px;
|
||||
border: 1px solid rgba(74, 102, 216, 0.22);
|
||||
background:
|
||||
radial-gradient(ellipse 70% 100% at 0% 0%, rgba(58, 85, 196, 0.20), transparent 55%),
|
||||
radial-gradient(ellipse 60% 80% at 100% 100%, color-mix(in srgb, var(--acc) 16%, transparent), transparent 60%),
|
||||
linear-gradient(135deg, rgba(14, 13, 22, 0.95), rgba(6, 6, 10, 0.97));
|
||||
box-shadow: 0 30px 90px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
.acc-hero-glow {
|
||||
position: absolute; width: 520px; height: 520px; border-radius: 999px;
|
||||
top: -200px; right: -160px;
|
||||
background: radial-gradient(circle, color-mix(in srgb, var(--acc) 40%, transparent), transparent 70%);
|
||||
filter: blur(110px); opacity: 0.55; pointer-events: none;
|
||||
}
|
||||
.acc-hero-grid {
|
||||
position: absolute; inset: 0;
|
||||
background:
|
||||
linear-gradient(rgba(74, 102, 216, 0.05) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(74, 102, 216, 0.05) 1px, transparent 1px);
|
||||
background-size: 56px 56px;
|
||||
mask-image: radial-gradient(ellipse 70% 90% at 70% 60%, #000 25%, transparent 80%);
|
||||
-webkit-mask-image: radial-gradient(ellipse 70% 90% at 70% 60%, #000 25%, transparent 80%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.acc-hero-inner {
|
||||
position: relative; z-index: 1;
|
||||
display: grid; grid-template-columns: minmax(0, 1fr);
|
||||
gap: clamp(1.5rem, 3vw, 2.5rem);
|
||||
padding: clamp(1.5rem, 3.5vw, 2.5rem);
|
||||
align-items: center;
|
||||
}
|
||||
.acc-hero-media {
|
||||
position: relative;
|
||||
aspect-ratio: 5 / 4;
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(222, 224, 240, 0.10);
|
||||
background:
|
||||
radial-gradient(ellipse 38% 18% at 50% 80%, rgba(200, 210, 240, 0.16), transparent 70%),
|
||||
radial-gradient(ellipse 55% 55% at 50% 50%, color-mix(in srgb, var(--acc) 22%, transparent), transparent 65%),
|
||||
linear-gradient(180deg, rgba(10, 10, 16, 0.95), rgba(4, 4, 8, 0.98));
|
||||
box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
.acc-hero-stage {
|
||||
position: absolute; bottom: 0; left: 0; right: 0;
|
||||
height: 35%;
|
||||
background: radial-gradient(ellipse 60% 100% at 50% 100%, rgba(200, 210, 240, 0.10), transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.acc-hero-grid-inner {
|
||||
position: absolute; inset: 0;
|
||||
background-image:
|
||||
linear-gradient(rgba(222, 224, 240, 0.05) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(222, 224, 240, 0.05) 1px, transparent 1px);
|
||||
background-size: 32px 32px;
|
||||
mask-image: radial-gradient(ellipse 70% 80% at 50% 50%, #000 30%, transparent 80%);
|
||||
-webkit-mask-image: radial-gradient(ellipse 70% 80% at 50% 50%, #000 30%, transparent 80%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.acc-hero-copy { display: flex; flex-direction: column; gap: 1rem; min-width: 0; }
|
||||
.acc-hero-eyebrow {
|
||||
display: inline-flex; align-items: center; gap: 0.55rem;
|
||||
width: fit-content;
|
||||
}
|
||||
.acc-dot {
|
||||
width: 7px; height: 7px; border-radius: 999px;
|
||||
background: var(--acc);
|
||||
box-shadow: 0 0 14px color-mix(in srgb, var(--acc) 80%, transparent);
|
||||
}
|
||||
.acc-hero-title {
|
||||
margin: 0;
|
||||
font-size: clamp(2rem, 5vw, 3rem);
|
||||
line-height: 1.05; font-weight: 300; letter-spacing: -0.03em;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.acc-hero-title :global(.text-gradient) { font-weight: 500; }
|
||||
.acc-hero-tagline {
|
||||
margin: 0; color: #DEE0F0;
|
||||
font-size: clamp(0.98rem, 1.8vw, 1.12rem);
|
||||
line-height: 1.55;
|
||||
font-weight: 500;
|
||||
}
|
||||
.acc-hero-desc {
|
||||
margin: 0; color: #C9CCDE;
|
||||
font-size: clamp(0.9rem, 1.6vw, 1rem);
|
||||
line-height: 1.65;
|
||||
max-width: 560px;
|
||||
}
|
||||
.acc-hero-actions { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 0.5rem; }
|
||||
|
||||
@media (min-width: 920px) {
|
||||
.acc-hero-inner { grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); }
|
||||
}
|
||||
|
||||
/* BLOCKS */
|
||||
.acc-grid {
|
||||
display: grid; grid-template-columns: minmax(0, 1fr); gap: 1rem;
|
||||
}
|
||||
@media (min-width: 820px) { .acc-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.2rem; } }
|
||||
.acc-block {
|
||||
padding: clamp(1.2rem, 2vw, 1.6rem);
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(222, 224, 240, 0.10);
|
||||
background:
|
||||
radial-gradient(ellipse 80% 60% at 100% 0%, color-mix(in srgb, var(--acc) 12%, transparent), transparent 60%),
|
||||
linear-gradient(180deg, rgba(22, 21, 30, 0.92), rgba(10, 10, 14, 0.96));
|
||||
display: flex; flex-direction: column; gap: 1rem;
|
||||
}
|
||||
.acc-block header { display: flex; flex-direction: column; gap: 0.45rem; }
|
||||
.acc-block h2 {
|
||||
margin: 0; font-size: clamp(1.2rem, 2.4vw, 1.5rem);
|
||||
font-weight: 500; letter-spacing: -0.015em; color: #FFFFFF; line-height: 1.2;
|
||||
}
|
||||
.acc-feature-list, .acc-compat-list {
|
||||
list-style: none; margin: 0; padding: 0;
|
||||
display: flex; flex-direction: column; gap: 0.6rem;
|
||||
}
|
||||
.acc-feature-list li {
|
||||
display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.65rem;
|
||||
color: #DEE0F0; font-size: 0.92rem; line-height: 1.55;
|
||||
}
|
||||
.acc-feature-check {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
width: 22px; height: 22px; border-radius: 999px;
|
||||
background: rgba(127, 214, 208, 0.18);
|
||||
border: 1px solid rgba(127, 214, 208, 0.4);
|
||||
color: #7FD6D0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.acc-compat-list li {
|
||||
display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.55rem;
|
||||
color: #DEE0F0; font-size: 0.92rem;
|
||||
padding: 0.55rem 0.7rem;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(222, 224, 240, 0.08);
|
||||
background: linear-gradient(135deg, rgba(20, 19, 26, 0.6), rgba(10, 10, 14, 0.8));
|
||||
}
|
||||
.acc-compat-icon { color: var(--acc); font-weight: 800; font-size: 1.1rem; line-height: 1; }
|
||||
.acc-block-empty { margin: 0; color: #C9CCDE; font-size: 0.92rem; line-height: 1.55; }
|
||||
|
||||
.acc-meta-grid {
|
||||
margin-top: auto;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid rgba(74, 102, 216, 0.18);
|
||||
display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 0.7rem;
|
||||
}
|
||||
.acc-meta {
|
||||
display: flex; flex-direction: column; gap: 0.18rem;
|
||||
}
|
||||
.acc-meta span {
|
||||
font-size: 0.58rem; letter-spacing: 0.24em; text-transform: uppercase;
|
||||
color: #8891C7; font-weight: 800;
|
||||
}
|
||||
.acc-meta strong { color: #FFFFFF; font-weight: 600; font-size: 0.88rem; letter-spacing: -0.005em; }
|
||||
|
||||
/* CTA */
|
||||
.acc-cta {
|
||||
position: relative; overflow: hidden;
|
||||
border-radius: 26px;
|
||||
border: 1px solid rgba(74, 102, 216, 0.26);
|
||||
background:
|
||||
radial-gradient(ellipse 60% 100% at 100% 0%, rgba(58, 85, 196, 0.22), transparent 60%),
|
||||
linear-gradient(135deg, rgba(18, 16, 28, 0.95), rgba(8, 8, 12, 0.97));
|
||||
box-shadow: 0 30px 90px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.04);
|
||||
scroll-margin-top: clamp(80px, 12vh, 120px);
|
||||
}
|
||||
.acc-cta-glow {
|
||||
position: absolute; width: 480px; height: 480px; border-radius: 999px;
|
||||
top: -160px; left: -160px;
|
||||
background: radial-gradient(circle, rgba(74, 102, 216, 0.45), transparent 70%);
|
||||
filter: blur(100px); opacity: 0.55; pointer-events: none;
|
||||
}
|
||||
.acc-cta-inner {
|
||||
position: relative; z-index: 1;
|
||||
display: grid; grid-template-columns: minmax(0, 1fr);
|
||||
gap: clamp(1.3rem, 3vw, 2rem);
|
||||
padding: clamp(1.7rem, 3.6vw, 2.7rem);
|
||||
align-items: center;
|
||||
}
|
||||
@media (min-width: 900px) {
|
||||
.acc-cta-inner { grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); }
|
||||
}
|
||||
.acc-cta-text { display: flex; flex-direction: column; gap: 0.6rem; max-width: 560px; }
|
||||
.acc-cta-text h2 {
|
||||
margin: 0;
|
||||
font-size: clamp(1.55rem, 3vw, 2rem);
|
||||
font-weight: 400; letter-spacing: -0.02em; color: #FFFFFF; line-height: 1.1;
|
||||
}
|
||||
.acc-cta-text p { margin: 0; color: #DEE0F0; font-size: 0.95rem; line-height: 1.65; }
|
||||
.acc-cta-actions {
|
||||
display: flex; flex-wrap: wrap; gap: 0.55rem; justify-content: flex-start;
|
||||
}
|
||||
@media (min-width: 900px) {
|
||||
.acc-cta-actions { justify-content: flex-end; }
|
||||
}
|
||||
@media (max-width: 520px) {
|
||||
.acc-cta-actions .cta-btn { width: 100%; justify-content: space-between; }
|
||||
.acc-hero-actions .cta-btn { width: 100%; justify-content: space-between; }
|
||||
}
|
||||
|
||||
/* RELATED */
|
||||
.acc-related { display: flex; flex-direction: column; gap: 1.25rem; }
|
||||
.acc-related header { display: flex; flex-direction: column; gap: 0.45rem; }
|
||||
.acc-related h2 {
|
||||
margin: 0; font-size: clamp(1.3rem, 2.6vw, 1.7rem);
|
||||
font-weight: 400; letter-spacing: -0.02em; color: #FFFFFF;
|
||||
}
|
||||
.acc-related-grid {
|
||||
display: grid; grid-template-columns: minmax(0, 1fr); gap: 1rem;
|
||||
}
|
||||
@media (min-width: 720px) { .acc-related-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.1rem; } }
|
||||
`}</style>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function RelatedCard({ accessory }: { accessory: Accessory }) {
|
||||
const accent = accessory.accent;
|
||||
return (
|
||||
<Link
|
||||
href={`/accessories/${accessory.slug}/`}
|
||||
className="acc-rel-card"
|
||||
style={{
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderRadius: 18,
|
||||
overflow: 'hidden',
|
||||
textDecoration: 'none',
|
||||
color: '#FFFFFF',
|
||||
border: '1px solid rgba(222, 224, 240, 0.10)',
|
||||
background:
|
||||
`radial-gradient(ellipse 80% 60% at 100% 0%, ${accent}1A, transparent 60%), linear-gradient(180deg, rgba(22, 21, 30, 0.92), rgba(10, 10, 14, 0.96))`,
|
||||
boxShadow: '0 1px 0 rgba(222, 224, 240, 0.04) inset, 0 14px 28px rgba(0, 0, 0, 0.36)',
|
||||
transition: 'transform 0.4s cubic-bezier(0.16,1,0.3,1), border-color 0.4s, box-shadow 0.4s',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
aspectRatio: '16 / 11',
|
||||
background: '#0a0a0e',
|
||||
overflow: 'hidden',
|
||||
borderBottom: '1px solid rgba(222, 224, 240, 0.06)',
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src={accessory.image}
|
||||
alt={`${accessory.name} product image`}
|
||||
fill
|
||||
sizes="(max-width: 720px) 100vw, 33vw"
|
||||
style={{ objectFit: 'contain', padding: '0.6rem' }}
|
||||
/>
|
||||
<span
|
||||
aria-hidden
|
||||
style={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
background: `radial-gradient(ellipse 60% 70% at 50% 40%, ${accent}26, transparent 65%)`,
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.45rem', padding: '1rem 1.1rem 1.1rem', flex: 1 }}>
|
||||
<span style={{ fontSize: '0.58rem', fontWeight: 800, letterSpacing: '0.22em', textTransform: 'uppercase', color: accent }}>
|
||||
{accessory.brandLabel}
|
||||
</span>
|
||||
<h3 style={{ margin: 0, fontSize: '1rem', fontWeight: 600, color: '#FFFFFF', letterSpacing: '-0.005em' }}>{accessory.name}</h3>
|
||||
<p style={{ margin: 0, color: '#C9CCDE', fontSize: '0.84rem', lineHeight: 1.55, flex: 1 }}>{accessory.tagline}</p>
|
||||
<span style={{ marginTop: '0.5rem', display: 'inline-flex', alignItems: 'center', gap: '0.4rem', fontSize: '0.74rem', fontWeight: 700, letterSpacing: '0.12em', textTransform: 'uppercase', color: '#DEE0F0' }}>
|
||||
View accessory <ArrowRight size={13} strokeWidth={2} />
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
57
src/app/accessories/page.tsx
Normal file
57
src/app/accessories/page.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import type { Metadata } from 'next';
|
||||
import Link from 'next/link';
|
||||
import ChevronRight from 'lucide-react/dist/esm/icons/chevron-right';
|
||||
import { Navbar } from '@/components/Navbar';
|
||||
import { FooterAndContact } from '@/components/FooterAndContact';
|
||||
import { AccessoriesShowcase } from '@/components/robotics/AccessoriesShowcase';
|
||||
import { ACCESSORIES } from '@/data/accessories';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Robotics Accessories · Dexterous Hands, Arms, Sensors, IoT | YS Lootah Robotics',
|
||||
description:
|
||||
'Browse Unitree dexterous hands, robotic arms, perception sensors, and Pudu dispatch, docking, modular attachments, and building IoT modules — available in the UAE through YS Lootah Robotics.',
|
||||
alternates: { canonical: '/accessories/' },
|
||||
};
|
||||
|
||||
export default function AccessoriesIndexPage() {
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
|
||||
<main style={{ paddingTop: 'clamp(5rem, 9vw, 7rem)', paddingBottom: 'clamp(3rem, 6vw, 5rem)' }}>
|
||||
<div
|
||||
className="container-wide"
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 'clamp(2rem, 4vw, 3rem)',
|
||||
}}
|
||||
>
|
||||
<nav aria-label="Breadcrumb" style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem', alignItems: 'center', fontSize: '0.7rem', letterSpacing: '0.16em', textTransform: 'uppercase', color: '#8891C7' }}>
|
||||
<Link href="/" style={{ color: '#8891C7', textDecoration: 'none' }}>Home</Link>
|
||||
<span aria-hidden style={{ display: 'inline-flex', alignItems: 'center', color: '#4a4f63' }}><ChevronRight size={12} /></span>
|
||||
<Link href="/robots/" style={{ color: '#8891C7', textDecoration: 'none' }}>Robots</Link>
|
||||
<span aria-hidden style={{ display: 'inline-flex', alignItems: 'center', color: '#4a4f63' }}><ChevronRight size={12} /></span>
|
||||
<span aria-current="page" style={{ color: '#FFFFFF' }}>Accessories</span>
|
||||
</nav>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem', maxWidth: 760 }}>
|
||||
<span className="eyebrow">Add-ons · Accessories</span>
|
||||
<h1 style={{ margin: 0, fontSize: 'clamp(1.8rem, 4vw, 2.6rem)', fontWeight: 400, lineHeight: 1.1, letterSpacing: '-0.02em' }}>
|
||||
<span className="text-gradient" style={{ fontWeight: 500 }}>
|
||||
{ACCESSORIES.length} accessories — dexterous hands, arms, sensors, IoT modules.
|
||||
</span>
|
||||
</h1>
|
||||
<p style={{ margin: 0, color: '#DEE0F0', fontSize: 'clamp(0.95rem, 2vw, 1.05rem)', lineHeight: 1.65 }}>
|
||||
Robot platforms get their full capability from accessories. Browse Unitree dexterous hands and arms, perception sensors, and Pudu dispatch, docking, modular attachments, and building IoT modules — all available in the UAE through YS Lootah Robotics.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<AccessoriesShowcase />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<FooterAndContact />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -432,6 +432,170 @@ export default function BuSunaidahPage() {
|
||||
}
|
||||
.bs-pillar p { margin: 0; color: #C9CCDE; font-size: 0.88rem; line-height: 1.55; }
|
||||
|
||||
/* REEL */
|
||||
.bs-reel {
|
||||
position: relative;
|
||||
aspect-ratio: 16 / 9;
|
||||
width: 100%;
|
||||
border-radius: 22px;
|
||||
overflow: hidden;
|
||||
isolation: isolate;
|
||||
border: 1px solid rgba(74, 102, 216, 0.28);
|
||||
background:
|
||||
radial-gradient(ellipse 70% 100% at 0% 0%, rgba(58, 85, 196, 0.22), transparent 55%),
|
||||
radial-gradient(ellipse 60% 80% at 100% 100%, rgba(136, 145, 199, 0.16), transparent 60%),
|
||||
linear-gradient(180deg, #0a0a0e 0%, #050508 100%);
|
||||
box-shadow: 0 30px 80px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
.bs-reel-glow {
|
||||
position: absolute;
|
||||
inset: -20% -10% auto auto;
|
||||
width: 60%; height: 80%;
|
||||
background: radial-gradient(ellipse 60% 60% at 50% 50%, rgba(58, 85, 196, 0.5), transparent 70%);
|
||||
filter: blur(40px);
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
.bs-corner {
|
||||
position: absolute;
|
||||
width: 14px; height: 14px;
|
||||
z-index: 3;
|
||||
pointer-events: none;
|
||||
}
|
||||
.bs-corner-tl { top: 8px; left: 8px; border-top: 1px solid rgba(222, 224, 240, 0.32); border-left: 1px solid rgba(222, 224, 240, 0.32); }
|
||||
.bs-corner-tr { top: 8px; right: 8px; border-top: 1px solid rgba(222, 224, 240, 0.32); border-right: 1px solid rgba(222, 224, 240, 0.32); }
|
||||
.bs-corner-bl { bottom: 8px; left: 8px; border-bottom: 1px solid rgba(222, 224, 240, 0.32); border-left: 1px solid rgba(222, 224, 240, 0.32); }
|
||||
.bs-corner-br { bottom: 8px; right: 8px; border-bottom: 1px solid rgba(222, 224, 240, 0.32); border-right: 1px solid rgba(222, 224, 240, 0.32); }
|
||||
.bs-reel-media {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%; height: 100%;
|
||||
border: 0;
|
||||
background: #050508;
|
||||
z-index: 1;
|
||||
}
|
||||
.bs-reel-placeholder { position: absolute; inset: 0; z-index: 1; }
|
||||
.bs-reel-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(180deg, rgba(8, 8, 12, 0.0) 35%, rgba(8, 8, 12, 0.85) 100%);
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
}
|
||||
.bs-reel-cta {
|
||||
position: absolute;
|
||||
left: clamp(1rem, 2.5vw, 1.5rem);
|
||||
bottom: clamp(1rem, 2.5vw, 1.5rem);
|
||||
right: clamp(1rem, 2.5vw, 1.5rem);
|
||||
z-index: 3;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.9rem;
|
||||
}
|
||||
.bs-reel-play {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
width: 48px; height: 48px;
|
||||
border-radius: 999px;
|
||||
background:
|
||||
radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.32), transparent 60%),
|
||||
linear-gradient(135deg, #5a76e8, #273F94);
|
||||
color: #FFFFFF;
|
||||
border: 1px solid rgba(255, 255, 255, 0.22);
|
||||
box-shadow: 0 18px 32px rgba(58, 85, 196, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||||
flex: none;
|
||||
}
|
||||
.bs-reel-eyebrow {
|
||||
display: block;
|
||||
font-size: 0.62rem;
|
||||
letter-spacing: 0.28em;
|
||||
text-transform: uppercase;
|
||||
font-weight: 800;
|
||||
color: #DEE0F0;
|
||||
margin-bottom: 0.2rem;
|
||||
}
|
||||
.bs-reel-caption {
|
||||
display: block;
|
||||
color: #FFFFFF;
|
||||
font-size: clamp(0.95rem, 1.6vw, 1.05rem);
|
||||
font-weight: 500;
|
||||
letter-spacing: -0.005em;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* PRESS & MEDIA */
|
||||
.bs-press-wall {
|
||||
list-style: none; margin: 0; padding: 0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 0.75rem;
|
||||
}
|
||||
@media (min-width: 640px) { .bs-press-wall { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
|
||||
@media (min-width: 1000px) { .bs-press-wall { grid-template-columns: repeat(5, minmax(0, 1fr)); } }
|
||||
.bs-press-cell {
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
min-height: 76px;
|
||||
padding: 0.9rem 1rem;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(222, 224, 240, 0.10);
|
||||
background: linear-gradient(135deg, rgba(20, 19, 26, 0.78), rgba(10, 10, 14, 0.94));
|
||||
transition: border-color 0.3s, transform 0.3s;
|
||||
}
|
||||
.bs-press-cell:hover { border-color: rgba(74, 102, 216, 0.35); transform: translateY(-2px); }
|
||||
.bs-press-cell a {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
.bs-press-cell img {
|
||||
filter: grayscale(1) brightness(1.2);
|
||||
opacity: 0.78;
|
||||
transition: filter 0.3s, opacity 0.3s;
|
||||
}
|
||||
.bs-press-cell:hover img { filter: grayscale(0) brightness(1); opacity: 1; }
|
||||
.bs-press-fallback {
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
color: #C9CCDE;
|
||||
}
|
||||
|
||||
.bs-press-quotes {
|
||||
display: grid; grid-template-columns: minmax(0, 1fr); gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
@media (min-width: 720px) { .bs-press-quotes { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
||||
@media (min-width: 1100px) { .bs-press-quotes { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
|
||||
.bs-press-quote {
|
||||
margin: 0;
|
||||
display: flex; flex-direction: column; gap: 0.65rem;
|
||||
padding: 1.15rem 1.2rem 1.15rem;
|
||||
border-radius: 18px;
|
||||
border: 1px solid rgba(222, 224, 240, 0.10);
|
||||
background:
|
||||
radial-gradient(ellipse 75% 50% at 100% 0%, color-mix(in srgb, var(--acc) 14%, transparent), transparent 60%),
|
||||
linear-gradient(180deg, rgba(22, 21, 30, 0.9), rgba(10, 10, 14, 0.96));
|
||||
}
|
||||
.bs-press-quote-mark {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
width: 30px; height: 30px;
|
||||
border-radius: 999px;
|
||||
color: color-mix(in srgb, var(--acc) 82%, white);
|
||||
background: color-mix(in srgb, var(--acc) 14%, rgba(14, 13, 18, 0.6));
|
||||
border: 1px solid color-mix(in srgb, var(--acc) 32%, transparent);
|
||||
}
|
||||
.bs-press-quote p { margin: 0; color: #ECEEF6; font-size: 0.97rem; line-height: 1.65; }
|
||||
.bs-press-quote footer {
|
||||
color: #8891C7;
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
}
|
||||
.bs-press-quote footer a { color: #8891C7; text-decoration: none; }
|
||||
.bs-press-quote footer a:hover { color: #FFFFFF; }
|
||||
.bs-press-date { color: #5a627e; }
|
||||
|
||||
/* INSTAGRAM LINK */
|
||||
.bs-ig-link {
|
||||
display: inline-flex; align-items: center; gap: 0.5rem;
|
||||
|
||||
@ -1,68 +1,306 @@
|
||||
import type { Metadata } from 'next';
|
||||
import Link from 'next/link';
|
||||
import ChevronRight from 'lucide-react/dist/esm/icons/chevron-right';
|
||||
import { Navbar } from '@/components/Navbar';
|
||||
import { FooterAndContact } from '@/components/FooterAndContact';
|
||||
import { CatalogClient } from './CatalogClient';
|
||||
import { AccessoriesShowcase } from '@/components/robotics/AccessoriesShowcase';
|
||||
import { RoboticsSplineShowcase } from '@/components/sections/robotics-spline-showcase';
|
||||
import { CTAButton } from '@/components/ui/CTAButton';
|
||||
import { ROBOTS } from '@/data/robots';
|
||||
import { ACCESSORIES } from '@/data/accessories';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Robots Catalog YS Lootah Robotics Dubai',
|
||||
title: 'Robots Catalog · Unitree & Pudu in UAE | YS Lootah Robotics',
|
||||
description:
|
||||
'Explore selected humanoid, quadruped, industrial, service, delivery, hospitality, and cleaning robots — plus dexterous hands, arms, sensors, and accessories — from Unitree and Pudu available in the UAE through YS Lootah Robotics.',
|
||||
alternates: { canonical: '/robots/' },
|
||||
};
|
||||
|
||||
const unitreeCount = ROBOTS.filter((r) => r.brand === 'unitree').length;
|
||||
const puduCount = ROBOTS.filter((r) => r.brand === 'pudu').length;
|
||||
|
||||
const HERO_STATS: { value: string; label: string }[] = [
|
||||
{ value: String(ROBOTS.length), label: 'Robot models' },
|
||||
{ value: String(ACCESSORIES.length), label: 'Accessories' },
|
||||
{ value: String(unitreeCount), label: 'Unitree' },
|
||||
{ value: String(puduCount), label: 'Pudu' },
|
||||
];
|
||||
|
||||
export default function RobotsPage() {
|
||||
return (
|
||||
<>
|
||||
<Navbar />
|
||||
<main>
|
||||
<div style={{ paddingTop: 'clamp(5rem, 8vw, 7rem)' }}>
|
||||
<RoboticsSplineShowcase />
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="container-wide"
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 'clamp(2.5rem, 5vw, 4rem)',
|
||||
paddingTop: 'clamp(3rem, 6vw, 5rem)',
|
||||
paddingBottom: 'clamp(4rem, 8vw, 6rem)',
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem', maxWidth: 760 }}>
|
||||
<span className="eyebrow">Catalog · Filter & Browse</span>
|
||||
<h2 style={{ margin: 0, fontSize: 'clamp(1.6rem, 3.6vw, 2.4rem)', fontWeight: 400, lineHeight: 1.1, letterSpacing: '-0.02em' }}>
|
||||
<span className="text-gradient" style={{ fontWeight: 500 }}>
|
||||
{ROBOTS.length} robots across the UAE portfolio.
|
||||
</span>
|
||||
</h2>
|
||||
<p style={{ margin: 0, color: '#DEE0F0', fontSize: 'clamp(0.95rem, 2vw, 1.05rem)', lineHeight: 1.7 }}>
|
||||
Filter by brand or category, then request a UAE quotation or book a live demo at our Dubai showroom.
|
||||
</p>
|
||||
</div>
|
||||
<main className="rb-main">
|
||||
<div className="container-wide rb-stack">
|
||||
{/* BREADCRUMBS */}
|
||||
<nav aria-label="Breadcrumb" className="rb-crumbs">
|
||||
<ol>
|
||||
<li><Link href="/">Home</Link></li>
|
||||
<li aria-hidden><ChevronRight size={12} /></li>
|
||||
<li aria-current="page">Robots</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<CatalogClient />
|
||||
{/* HERO */}
|
||||
<section className="rb-hero" aria-labelledby="rb-hero-title">
|
||||
<div className="rb-hero-glow" aria-hidden />
|
||||
<div className="rb-hero-glow-b" aria-hidden />
|
||||
<div className="rb-hero-grid" aria-hidden />
|
||||
|
||||
<section id="accessories" style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem', scrollMarginTop: 'clamp(80px, 12vh, 120px)' }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.85rem', maxWidth: 760 }}>
|
||||
<div className="rb-hero-inner">
|
||||
<div className="rb-hero-copy">
|
||||
<span className="eyebrow rb-hero-eyebrow">
|
||||
<span className="rb-dot" />
|
||||
Catalog · UAE Portfolio · Dubai
|
||||
</span>
|
||||
<h1 id="rb-hero-title" className="rb-hero-title">
|
||||
<span className="text-gradient">
|
||||
{ROBOTS.length} robots + {ACCESSORIES.length} accessories.
|
||||
</span>
|
||||
</h1>
|
||||
<p className="rb-hero-sub">
|
||||
Selected Unitree and Pudu Robotics platforms — humanoid, quadruped, industrial, delivery, service, and cleaning — plus dexterous hands, arms, sensors, and building IoT modules. Available in the UAE through YS Lootah Robotics.
|
||||
</p>
|
||||
|
||||
<div className="rb-hero-actions">
|
||||
<CTAButton href="#catalog" variant="primary" size="lg" arrow="right">
|
||||
Browse the Catalog
|
||||
</CTAButton>
|
||||
<CTAButton href="/book-demo/" variant="secondary" size="lg" arrow="right">
|
||||
Book a Live Demo
|
||||
</CTAButton>
|
||||
</div>
|
||||
|
||||
<ul className="rb-jump" role="list" aria-label="Quick links">
|
||||
<li><a href="#catalog">Robots</a></li>
|
||||
<li><a href="#accessories">Accessories</a></li>
|
||||
<li><Link href="/configure/">Configure G1</Link></li>
|
||||
<li><Link href="/brands/">Brands</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<aside className="rb-hero-stats" aria-label="Catalog stats">
|
||||
{HERO_STATS.map((s) => (
|
||||
<div key={s.label} className="rb-stat">
|
||||
<span className="rb-stat-value">{s.value}</span>
|
||||
<span className="rb-stat-label">{s.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CATALOG */}
|
||||
<section id="catalog" className="rb-catalog">
|
||||
<header className="rb-section-head">
|
||||
<span className="eyebrow">Filter & Browse</span>
|
||||
<h2>
|
||||
<span className="text-gradient">{ROBOTS.length} robots across the UAE portfolio.</span>
|
||||
</h2>
|
||||
<p>Filter by brand or category, then request a UAE quotation or book a live demo at our Dubai showroom.</p>
|
||||
</header>
|
||||
<CatalogClient />
|
||||
</section>
|
||||
|
||||
{/* ACCESSORIES */}
|
||||
<section id="accessories" className="rb-accessories">
|
||||
<header className="rb-section-head">
|
||||
<span className="eyebrow">Add-ons · Accessories</span>
|
||||
<h2 style={{ margin: 0, fontSize: 'clamp(1.5rem, 3.4vw, 2.2rem)', fontWeight: 400, lineHeight: 1.1, letterSpacing: '-0.02em' }}>
|
||||
<span className="text-gradient" style={{ fontWeight: 500 }}>
|
||||
<h2>
|
||||
<span className="text-gradient">
|
||||
{ACCESSORIES.length} accessories — dexterous hands, arms, sensors, IoT modules.
|
||||
</span>
|
||||
</h2>
|
||||
<p style={{ margin: 0, color: '#DEE0F0', fontSize: 'clamp(0.92rem, 2vw, 1.02rem)', lineHeight: 1.65 }}>
|
||||
Robot platforms get their full capability from accessories. Browse Unitree dexterous hands and arms, perception sensors, and Pudu dispatch, docking, modular attachments, and building IoT modules.
|
||||
</p>
|
||||
</div>
|
||||
<p>Robot platforms get their full capability from accessories. Each one has its own detail page with features, compatibility, and inquiry form.</p>
|
||||
</header>
|
||||
<AccessoriesShowcase />
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<FooterAndContact />
|
||||
|
||||
<style>{`
|
||||
.rb-main {
|
||||
padding-top: clamp(5rem, 9vw, 7rem);
|
||||
padding-bottom: clamp(3rem, 6vw, 5rem);
|
||||
}
|
||||
.rb-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: clamp(2.5rem, 5vw, 4rem);
|
||||
}
|
||||
|
||||
/* BREADCRUMBS */
|
||||
.rb-crumbs ol {
|
||||
list-style: none; margin: 0; padding: 0;
|
||||
display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem;
|
||||
font-size: 0.7rem; letter-spacing: 0.16em; text-transform: uppercase;
|
||||
color: #8891C7;
|
||||
}
|
||||
.rb-crumbs a { color: #8891C7; text-decoration: none; transition: color 0.25s; }
|
||||
.rb-crumbs a:hover { color: #FFFFFF; }
|
||||
.rb-crumbs li[aria-current="page"] { color: #FFFFFF; }
|
||||
.rb-crumbs li[aria-hidden] { display: inline-flex; align-items: center; color: #4a4f63; }
|
||||
|
||||
/* HERO */
|
||||
.rb-hero {
|
||||
position: relative; overflow: hidden;
|
||||
border-radius: 26px;
|
||||
border: 1px solid rgba(74, 102, 216, 0.20);
|
||||
background:
|
||||
radial-gradient(ellipse 70% 100% at 0% 0%, rgba(58, 85, 196, 0.20), transparent 55%),
|
||||
radial-gradient(ellipse 60% 80% at 100% 100%, rgba(136, 145, 199, 0.16), transparent 60%),
|
||||
linear-gradient(135deg, rgba(14, 13, 22, 0.95), rgba(6, 6, 10, 0.97));
|
||||
box-shadow: 0 30px 90px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.04);
|
||||
}
|
||||
.rb-hero-glow {
|
||||
position: absolute; width: 580px; height: 580px; border-radius: 999px;
|
||||
top: -220px; right: -180px;
|
||||
background: radial-gradient(circle, rgba(120, 140, 255, 0.34), transparent 70%);
|
||||
filter: blur(120px); opacity: 0.55; pointer-events: none;
|
||||
}
|
||||
.rb-hero-glow-b {
|
||||
position: absolute; width: 460px; height: 460px; border-radius: 999px;
|
||||
bottom: -200px; left: -150px;
|
||||
background: radial-gradient(circle, rgba(74, 102, 216, 0.30), transparent 70%);
|
||||
filter: blur(110px); opacity: 0.45; pointer-events: none;
|
||||
}
|
||||
.rb-hero-grid {
|
||||
position: absolute; inset: 0;
|
||||
background:
|
||||
linear-gradient(rgba(74, 102, 216, 0.05) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(74, 102, 216, 0.05) 1px, transparent 1px);
|
||||
background-size: 56px 56px;
|
||||
mask-image: radial-gradient(ellipse 70% 90% at 30% 40%, #000 25%, transparent 80%);
|
||||
-webkit-mask-image: radial-gradient(ellipse 70% 90% at 30% 40%, #000 25%, transparent 80%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.rb-hero-inner {
|
||||
position: relative; z-index: 1;
|
||||
display: grid; grid-template-columns: minmax(0, 1fr);
|
||||
gap: clamp(1.75rem, 3.5vw, 2.75rem);
|
||||
padding: clamp(1.8rem, 4vw, 3.25rem);
|
||||
align-items: center;
|
||||
}
|
||||
.rb-hero-copy { display: flex; flex-direction: column; gap: 1rem; max-width: 680px; min-width: 0; }
|
||||
.rb-hero-eyebrow {
|
||||
display: inline-flex; align-items: center; gap: 0.55rem;
|
||||
width: fit-content;
|
||||
}
|
||||
.rb-dot {
|
||||
width: 7px; height: 7px; border-radius: 999px; background: #DEE0F0;
|
||||
box-shadow: 0 0 14px rgba(222, 224, 240, 0.85);
|
||||
}
|
||||
.rb-hero-title {
|
||||
margin: 0;
|
||||
font-size: clamp(2rem, 5vw, 3.2rem);
|
||||
font-weight: 300; letter-spacing: -0.03em; line-height: 1.05;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.rb-hero-title :global(.text-gradient) { font-weight: 500; }
|
||||
.rb-hero-sub {
|
||||
margin: 0; color: #DEE0F0;
|
||||
font-size: clamp(0.95rem, 1.8vw, 1.08rem);
|
||||
line-height: 1.65;
|
||||
max-width: 600px;
|
||||
}
|
||||
.rb-hero-actions {
|
||||
display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 0.5rem;
|
||||
}
|
||||
.rb-jump {
|
||||
list-style: none; margin: 0.5rem 0 0; padding: 0;
|
||||
display: flex; flex-wrap: wrap; gap: 0.4rem;
|
||||
}
|
||||
.rb-jump li a {
|
||||
display: inline-flex; align-items: center;
|
||||
padding: 0.45rem 0.85rem;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(222, 224, 240, 0.14);
|
||||
background: rgba(20, 19, 26, 0.6);
|
||||
color: #DEE0F0;
|
||||
font-size: 0.7rem; font-weight: 700;
|
||||
letter-spacing: 0.12em; text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
transition: all 0.25s ease;
|
||||
}
|
||||
.rb-jump li a:hover {
|
||||
color: #FFFFFF;
|
||||
border-color: rgba(74, 102, 216, 0.5);
|
||||
background: rgba(28, 27, 38, 0.7);
|
||||
}
|
||||
|
||||
/* HERO STATS */
|
||||
.rb-hero-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 0.6rem;
|
||||
}
|
||||
.rb-stat {
|
||||
position: relative;
|
||||
display: flex; flex-direction: column; gap: 0.3rem;
|
||||
padding: 0.95rem 1rem;
|
||||
border-radius: 16px;
|
||||
border: 1px solid rgba(120, 140, 255, 0.22);
|
||||
background:
|
||||
linear-gradient(135deg, rgba(80, 110, 255, 0.12), rgba(255, 255, 255, 0.03));
|
||||
overflow: hidden;
|
||||
min-height: 86px;
|
||||
transition: border-color 0.3s, transform 0.3s, box-shadow 0.3s;
|
||||
}
|
||||
.rb-stat::before {
|
||||
content: ''; position: absolute;
|
||||
top: 0; right: 14px; width: 24px; height: 1px;
|
||||
background: linear-gradient(90deg, transparent, rgba(180, 195, 255, 0.55));
|
||||
}
|
||||
.rb-stat:hover {
|
||||
border-color: rgba(120, 140, 255, 0.45);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 10px 22px rgba(0, 0, 0, 0.38), 0 0 18px rgba(74, 102, 216, 0.18);
|
||||
}
|
||||
.rb-stat-value {
|
||||
font-size: clamp(1.55rem, 2.6vw, 2rem);
|
||||
font-weight: 500; letter-spacing: -0.025em; line-height: 1;
|
||||
background: linear-gradient(180deg, #FFFFFF, #B5BDDB);
|
||||
-webkit-background-clip: text; background-clip: text; color: transparent;
|
||||
}
|
||||
.rb-stat-label {
|
||||
font-size: 0.64rem; letter-spacing: 0.22em;
|
||||
text-transform: uppercase; font-weight: 700; color: #A6B2D8;
|
||||
}
|
||||
|
||||
@media (min-width: 920px) {
|
||||
.rb-hero-inner { grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr); }
|
||||
}
|
||||
|
||||
/* SECTION HEADERS */
|
||||
.rb-catalog,
|
||||
.rb-accessories {
|
||||
display: flex; flex-direction: column; gap: clamp(1.25rem, 2.5vw, 1.75rem);
|
||||
scroll-margin-top: clamp(80px, 12vh, 120px);
|
||||
}
|
||||
.rb-section-head {
|
||||
display: flex; flex-direction: column; gap: 0.6rem;
|
||||
max-width: 760px;
|
||||
}
|
||||
.rb-section-head h2 {
|
||||
margin: 0;
|
||||
font-size: clamp(1.5rem, 3vw, 2.1rem);
|
||||
font-weight: 400; letter-spacing: -0.02em; line-height: 1.15;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.rb-section-head h2 :global(.text-gradient) { font-weight: 500; }
|
||||
.rb-section-head p {
|
||||
margin: 0; color: #DEE0F0;
|
||||
font-size: clamp(0.92rem, 2vw, 1.02rem);
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
.rb-hero-actions .cta-btn { width: 100%; justify-content: space-between; }
|
||||
}
|
||||
`}</style>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
import { useMemo, useState } from 'react';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { ACCESSORIES, GROUP_LABELS, type AccessoryGroup, type Accessory } from '@/data/accessories';
|
||||
|
||||
type Filter = AccessoryGroup | 'all';
|
||||
@ -83,10 +84,9 @@ function FilterChip({
|
||||
function AccessoryCard({ accessory }: { accessory: Accessory }) {
|
||||
const accent = accessory.accent;
|
||||
return (
|
||||
<a
|
||||
href={accessory.officialUrl}
|
||||
target={accessory.officialUrl?.startsWith('http') ? '_blank' : undefined}
|
||||
rel="noopener noreferrer"
|
||||
<Link
|
||||
href={`/accessories/${accessory.slug}/`}
|
||||
aria-label={`View details for ${accessory.name}`}
|
||||
style={{ textDecoration: 'none', color: 'inherit' }}
|
||||
>
|
||||
<article
|
||||
@ -218,6 +218,6 @@ function AccessoryCard({ accessory }: { accessory: Accessory }) {
|
||||
0 0 28px ${accent}33 !important;
|
||||
}
|
||||
`}</style>
|
||||
</a>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { BU_SUNAIDAH_URL, BU_SUNAIDAH_PORTRAIT } from '@/data/bu-sunaidah';
|
||||
import { InstagramGlyph } from '@/components/icons/InstagramGlyph';
|
||||
|
||||
export function BuSunaidahSection() {
|
||||
return (
|
||||
@ -97,9 +100,12 @@ export function BuSunaidahSection() {
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
<Link
|
||||
href="/bu-sunaidah/"
|
||||
aria-label="Open the Bu Sunaidah page"
|
||||
style={{
|
||||
position: 'relative',
|
||||
display: 'block',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
borderRadius: '1.75rem',
|
||||
@ -108,15 +114,84 @@ export function BuSunaidahSection() {
|
||||
background:
|
||||
'radial-gradient(ellipse 70% 60% at 50% 55%, rgba(222, 224, 240,0.18), transparent 60%), linear-gradient(180deg, rgba(28, 27, 33,0.85), rgba(5,5,5,0.95))',
|
||||
boxShadow: '0 30px 100px rgba(0,0,0,0.7)',
|
||||
textDecoration: 'none',
|
||||
color: 'inherit',
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src="/images/robots/unitree-g1.png"
|
||||
alt="Bu Sunaidah Emirati-inspired robotics character based on a Unitree G1 humanoid"
|
||||
fill
|
||||
sizes="(max-width: 768px) 90vw, 460px"
|
||||
style={{ objectFit: 'contain', padding: 'clamp(1.5rem, 4vw, 3rem)' }}
|
||||
/>
|
||||
{BU_SUNAIDAH_PORTRAIT ? (
|
||||
<Image
|
||||
src={BU_SUNAIDAH_PORTRAIT.src}
|
||||
alt={BU_SUNAIDAH_PORTRAIT.alt}
|
||||
fill
|
||||
sizes="(max-width: 768px) 90vw, 460px"
|
||||
style={{ objectFit: 'cover', objectPosition: 'center' }}
|
||||
/>
|
||||
) : (
|
||||
<a
|
||||
href={BU_SUNAIDAH_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
aria-label="Open @bu.sunaidah on Instagram in a new tab"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: '1.1rem',
|
||||
textDecoration: 'none',
|
||||
color: '#FBFBFD',
|
||||
textAlign: 'center',
|
||||
padding: 'clamp(1.25rem, 4vw, 2rem)',
|
||||
background:
|
||||
'radial-gradient(ellipse 60% 60% at 50% 35%, rgba(58, 85, 196, 0.25), transparent 65%)',
|
||||
}}
|
||||
>
|
||||
<span
|
||||
aria-hidden
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: 78,
|
||||
height: 78,
|
||||
borderRadius: 22,
|
||||
color: '#FFFFFF',
|
||||
border: '1px solid rgba(222, 224, 240, 0.28)',
|
||||
background:
|
||||
'radial-gradient(ellipse 60% 60% at 30% 30%, rgba(255, 84, 165, 0.55), transparent 60%), radial-gradient(ellipse 60% 60% at 70% 70%, rgba(255, 165, 84, 0.45), transparent 60%), linear-gradient(135deg, rgba(58, 85, 196, 0.65), rgba(20, 20, 28, 0.85))',
|
||||
boxShadow: '0 18px 40px rgba(0,0,0,0.55)',
|
||||
}}
|
||||
>
|
||||
<InstagramGlyph size={34} />
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: '0.66rem',
|
||||
letterSpacing: '0.28em',
|
||||
textTransform: 'uppercase',
|
||||
fontWeight: 700,
|
||||
color: '#8891C7',
|
||||
}}
|
||||
>
|
||||
Portrait coming soon
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: '1.05rem',
|
||||
fontWeight: 500,
|
||||
letterSpacing: '-0.005em',
|
||||
lineHeight: 1.4,
|
||||
maxWidth: 280,
|
||||
}}
|
||||
>
|
||||
View Bu Sunaidah's latest moments on Instagram
|
||||
</span>
|
||||
</a>
|
||||
)}
|
||||
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
@ -127,6 +202,7 @@ export function BuSunaidahSection() {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: '0.75rem',
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
>
|
||||
<span
|
||||
@ -161,7 +237,7 @@ export function BuSunaidahSection() {
|
||||
Bu Sunaidah
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -81,7 +81,7 @@ export const ACCESSORIES: Accessory[] = [
|
||||
'±22° four-finger lateral swing',
|
||||
'Backdrivable, low-damping reducers',
|
||||
],
|
||||
image: '/images/accessories/unitree-dex5-1.gif',
|
||||
image: '/images/accessories/unitree-dex5-1.png',
|
||||
accent: GOLD_BRAND,
|
||||
officialUrl: 'https://www.unitree.com/Dex5-1',
|
||||
compatibility: ['Unitree G1', 'Unitree H1', 'Unitree H2', 'Research arms'],
|
||||
|
||||
@ -16,6 +16,19 @@ export type InstagramPost = {
|
||||
export const BU_SUNAIDAH_HANDLE = 'bu.sunaidah';
|
||||
export const BU_SUNAIDAH_URL = `https://www.instagram.com/${BU_SUNAIDAH_HANDLE}`;
|
||||
|
||||
/**
|
||||
* Portrait shown in the home-page Bu Sunaidah section and detail page hero.
|
||||
*
|
||||
* Set to a local path (e.g. '/bu-sunaidah/portrait.jpg') once the official
|
||||
* Bu Sunaidah photo has been saved into /public/bu-sunaidah/.
|
||||
*
|
||||
* Leave as null to render the "View on Instagram" placeholder card.
|
||||
*
|
||||
* IMPORTANT: do NOT hotlink Instagram CDN images in production — save the
|
||||
* approved photo locally inside the repo first.
|
||||
*/
|
||||
export const BU_SUNAIDAH_PORTRAIT: { src: string; alt: string } | null = null;
|
||||
|
||||
// TODO: populate with curated post shortcodes from @bu.sunaidah.
|
||||
// Each entry renders as a 16:20 Instagram embed iframe.
|
||||
export const BU_SUNAIDAH_POSTS: InstagramPost[] = [];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user