feat: add accessory detail and index pages with dynamic routing and metadata
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

+525
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 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
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 />
</>
);
}
+164
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;
+271 -33
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.
<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>
{/* 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 />
<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>
</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.
<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>
<CatalogClient />
<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>
<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 }}>
<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>
</>
);
}
@@ -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>
);
}
+81 -5
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',
}}
>
{BU_SUNAIDAH_PORTRAIT ? (
<Image
src="/images/robots/unitree-g1.png"
alt="Bu Sunaidah Emirati-inspired robotics character based on a Unitree G1 humanoid"
src={BU_SUNAIDAH_PORTRAIT.src}
alt={BU_SUNAIDAH_PORTRAIT.alt}
fill
sizes="(max-width: 768px) 90vw, 460px"
style={{ objectFit: 'contain', padding: 'clamp(1.5rem, 4vw, 3rem)' }}
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>
+1 -1
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'],
+13
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[] = [];