feat: add accessory detail and index pages with dynamic routing and metadata
Some checks are pending
CI/CD / test-and-build (push) Waiting to run
CI/CD / deploy (push) Blocked by required conditions

This commit is contained in:
Najjar\NajjarV02 2026-05-21 16:22:05 +04:00
parent 5271914b16
commit 2d32a1e722
10 changed files with 1124 additions and 51 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

View 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 youre 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>
);
}

View 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 />
</>
);
}

View File

@ -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;

View File

@ -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 &amp; 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>
</>
);
}

View File

@ -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>
);
}

View File

@ -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&apos;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>

View File

@ -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'],

View File

@ -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[] = [];