Refactor code structure for improved readability and maintainability; removed redundant code blocks and optimized function calls.
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-20 18:03:42 +04:00
parent bfe589be27
commit 729ab71c2c
65 changed files with 217 additions and 216 deletions

View File

@ -19,14 +19,14 @@ Per-element rules. Apply when designing or reviewing the corresponding component
- Same shape as primary.
**Ghost**
- `text-bone hover:text-gold-light` no border, no fill.
- `text-bone hover:text-gold-light` no border, no fill.
**Forbidden**
- Bright red CTA buttons (unless brand color is red).
- Square corners on action buttons.
- Solid `#000` or `#fff` backgrounds.
- Loading text inside button that breaks layout use a spinner icon + word swap with fixed min-width.
- Success/error message **inside** button render outside as a banner.
- Loading text inside button that breaks layout use a spinner icon + word swap with fixed min-width.
- Success/error message **inside** button render outside as a banner.
---
@ -82,7 +82,7 @@ Per-element rules. Apply when designing or reviewing the corresponding component
- Always `text-balance` on h2 for clean wrapping.
- `text-pretty` on subtitle.
- Use SectionLabel with sequential index per page (01, 02, 03…). No mono font.
- One gold-accent phrase max per heading overdoing gold cheapens it.
- One gold-accent phrase max per heading overdoing gold cheapens it.
---
@ -99,7 +99,7 @@ Per-element rules. Apply when designing or reviewing the corresponding component
## Footer
**Layout 4 columns, no duplication**
**Layout 4 columns, no duplication**
| Column 1 | Column 2 | Column 3 | Column 4 |
|---|---|---|---|
@ -109,7 +109,7 @@ Per-element rules. Apply when designing or reviewing the corresponding component
**Rules**
- No page link appears in two columns.
- Privacy Policy lives only under Legal.
- App badges only in Legal & App column (footer) official SVGs at 132 px width, 8 px gap.
- App badges only in Legal & App column (footer) official SVGs at 132 px width, 8 px gap.
- External links open in new tab with rel attrs.
- Background: `bg-obsidian` with subtle "LUXAM"-style watermark behind content (translate-y-[18%], opacity 0.025).
- Bottom row: `© YYYY {brand}. All rights reserved.` + tagline + agency credit (subtle, gold-underlined on hover).
@ -128,7 +128,7 @@ Per-element rules. Apply when designing or reviewing the corresponding component
focus:shadow-[0_0_0_3px_rgba(212,164,55,0.18)]
aria-[invalid=true]:border-[#a3261c]/60
```
- Honeypot field: hidden `<input name="website">` silently succeed if filled.
- Honeypot field: hidden `<input name="website">` silently succeed if filled.
- Validation: server-side via `submitContact()` action; client gets `state.fieldErrors` + `state.message`.
- Submission states: `useActionState` + `useFormStatus`. Spinner inside button, success/error banner **outside** button.
@ -142,9 +142,9 @@ Per-element rules. Apply when designing or reviewing the corresponding component
- Wrapper for emphasis: `glass-gold size-11 grid place-items-center rounded-full`.
- **Forbidden**: oversized icons (>32 px) standalone, multi-color icon sets, cartoonish flat icons.
**No lucide WhatsApp glyph** use custom inline SVG from `snippets/WhatsAppLink.tsx`.
**No lucide WhatsApp glyph** use custom inline SVG from `snippets/WhatsAppLink.tsx`.
**No lucide Instagram glyph in v1.16** use custom inline SVG (see footer).
**No lucide Instagram glyph in v1.16** use custom inline SVG (see footer).
---
@ -229,8 +229,8 @@ Per-element rules. Apply when designing or reviewing the corresponding component
## Don't
- Don't add hover effects without `transition-*` for smoothness.
- Don't use `transform-origin` defaults on rotated icon discs keep them small (8-12°).
- Don't use `whileHover={{ scale: 1.1 }}` on cards too theatrical. Use `-translate-y-1` instead.
- Don't use `transform-origin` defaults on rotated icon discs keep them small (8-12°).
- Don't use `whileHover={{ scale: 1.1 }}` on cards too theatrical. Use `-translate-y-1` instead.
- Don't add box-shadows with multiple layers and bright colors. One subtle amber glow max.
- Don't introduce a new font family per page stick to display + sans.
- Don't introduce a new font family per page stick to display + sans.
- Don't render placeholder images (`via.placeholder.com`, gray boxes) in production code. Always use real assets the user provided.

View File

@ -1,6 +1,6 @@
# Component Snippets
Overview of the reusable TSX snippets in `snippets/`. Each is a copy-paste base for a new project adapt brand tokens + content via your own `lib/content.ts`.
Overview of the reusable TSX snippets in `snippets/`. Each is a copy-paste base for a new project adapt brand tokens + content via your own `lib/content.ts`.
| File | Purpose | Depends on |
|---|---|---|
@ -18,7 +18,7 @@ Overview of the reusable TSX snippets in `snippets/`. Each is a copy-paste base
1. Copy the file you need into your project's `src/components/` (or wherever your component layer lives).
2. Update import paths (`@/lib/cn`, `@/components/ui/...`) to match your project alias.
3. Wire data: the snippets accept props or read from a `lib/content.ts` you control.
4. Confirm Tailwind v4 `@theme` tokens (`obsidian`, `gold`, `bone`, `mist`, etc.) exist see `DESIGN_SYSTEM.md`.
4. Confirm Tailwind v4 `@theme` tokens (`obsidian`, `gold`, `bone`, `mist`, etc.) exist see `DESIGN_SYSTEM.md`.
5. Confirm Framer Motion + lucide-react installed:
```bash
npm i framer-motion lucide-react

View File

@ -8,7 +8,7 @@ Reusable design tokens + scales extracted from the Luxam reference build. Drop i
```css
@theme {
/* Backgrounds obsidian → graphite layering */
/* Backgrounds obsidian → graphite layering */
--color-obsidian: #08080A; /* page background */
--color-graphite: #15161A; /* dark glass card */
--color-graphite-2: #1C1D22; /* slightly lifted card variant */
@ -17,7 +17,7 @@ Reusable design tokens + scales extracted from the Luxam reference build. Drop i
--color-bone: #F4F0E6; /* primary text on dark */
--color-mist: #C7C2B5; /* secondary/muted text */
/* Brand gold spectrum (use sparingly + structurally) */
/* Brand gold spectrum (use sparingly + structurally) */
--color-gold: #D4A437; /* core brand gold */
--color-gold-light: #F2C25B; /* hover, highlight, gradient stops */
--color-gold-deep: #7A4A12; /* gradient base */
@ -178,14 +178,14 @@ Every card/button uses the same hover vocabulary:
6. Optional: top hairline gold gradient fade-in.
7. Icon disc gets `rotate-[8deg]` + amber halo shadow.
Apply via `group` + `group-hover:` Tailwind variants never per-element JS listeners.
Apply via `group` + `group-hover:` Tailwind variants never per-element JS listeners.
---
## Container widths
```tsx
// components/ui/Container.tsx three variants
// components/ui/Container.tsx three variants
size="default" → max-w-7xl
size="wide" → max-w-[1480px]
size="narrow" → max-w-3xl

View File

@ -43,7 +43,7 @@ Do NOT invent links. Ask if missing.
Inspect {{PAGE_ROUTE}}. Keep the content + intent but rebuild it to match the design DNA in .claude/skills/premium-frontend-designer/.
Requirements:
- Reuse existing primitives (Section, Container, Button, Reveal, SectionLabel, etc.) don't duplicate.
- Reuse existing primitives (Section, Container, Button, Reveal, SectionLabel, etc.) don't duplicate.
- Apply the cardGrid + cardCell motion variants for grid entrances.
- Add a single gold-accent phrase per h2.
- Centralize section padding via the Section primitive (no per-page overrides).
@ -176,7 +176,7 @@ Walk through the responsive checklist in .claude/skills/premium-frontend-designe
Test at 320 / 375 / 390 / 414 / 768 / 1024 viewports.
Don't redesign only fix layout, spacing, overflow, clipping. Preserve the visual identity.
Don't redesign only fix layout, spacing, overflow, clipping. Preserve the visual identity.
Return a table of issues + fixes (Before / After columns).
```
@ -194,7 +194,7 @@ Audit and refine typography across all pages:
4. Tracking: reduce all tracking-[0.32em+] on small labels to tracking-[0.22em].
5. Nav links: text-sm font-medium tracking-[0.005em] (slightly larger + medium weight, near-zero tracking).
6. BrandStrip marquee: smaller (text-xs md:text-sm uppercase) so it doesn't compete with hero.
7. Section labels via SectionLabel primitive never inline.
7. Section labels via SectionLabel primitive never inline.
8. text-balance on h1/h2, text-pretty on body paragraphs.
Confirm:
@ -270,5 +270,5 @@ Specifically check:
- Is spacing centralized via Section primitive?
- Is motion subtle (no bounces, no scale > 1.05 on hover)?
Apply only the changes needed don't rewrite working sections. Return a list of refinements made.
Apply only the changes needed don't rewrite working sections. Return a list of refinements made.
```

View File

@ -24,13 +24,13 @@ Run through every item before declaring a page done.
- [ ] Logo left, menu/CTA right on mobile.
- [ ] Hamburger button is `size-10` minimum (touch target).
- [ ] Mobile menu drawer closes when a link is clicked.
- [ ] No sticky-nav overlap on anchored sections `scroll-mt-24 md:scroll-mt-28` on `<Section>`.
- [ ] No sticky-nav overlap on anchored sections `scroll-mt-24 md:scroll-mt-28` on `<Section>`.
- [ ] Logo height responsive: `h-11 sm:h-14 md:h-16`.
## Hero
- [ ] H1 clamp: `clamp(2.4rem, 7vw, 6.4rem)` never larger upper bound.
- [ ] `leading-[1.0]` on serif H1 (not `0.95` clips ascenders).
- [ ] H1 clamp: `clamp(2.4rem, 7vw, 6.4rem)` never larger upper bound.
- [ ] `leading-[1.0]` on serif H1 (not `0.95` clips ascenders).
- [ ] Top padding generous: `pt-32 sm:pt-36 md:pt-44` (clears sticky nav + breathing room).
- [ ] CTAs use `flex flex-wrap items-center gap-4` so they stack on narrow screens.
- [ ] Stats panel: collapse 1-col-tall mobile layouts to a horizontal 3-col compact row.
@ -38,7 +38,7 @@ Run through every item before declaring a page done.
## Cards & grids
- [ ] Mobile: `grid-cols-1`. Tablet: `sm:grid-cols-2`. Desktop: `lg:grid-cols-N` matched to card count.
- [ ] Avoid `lg:grid-cols-3` for a 5-card list (leaves orphan) use 5 or 2-row layout matched to count.
- [ ] Avoid `lg:grid-cols-3` for a 5-card list (leaves orphan) use 5 or 2-row layout matched to count.
- [ ] Aspect-ratio used over fixed heights: `aspect-[4/3]` or `aspect-[3/4]`.
## Typography
@ -54,7 +54,7 @@ Run through every item before declaring a page done.
- [ ] Hero LCP image gets `priority`.
- [ ] `object-cover` only when crop is visually intentional.
- [ ] `object-contain` for logos.
- [ ] Alt text meaningful never "image" or filename.
- [ ] Alt text meaningful never "image" or filename.
## Forms

View File

@ -7,7 +7,7 @@ description: Build, redesign, and polish premium dark/gold luxury websites with
You are a senior frontend architect + UI/UX designer + implementation engineer.
Your job: create production-ready, premium, cinematic, dark/gold luxury websites that feel high-end, trustworthy, and conversion-focused the way the Luxam reference site does.
Your job: create production-ready, premium, cinematic, dark/gold luxury websites that feel high-end, trustworthy, and conversion-focused the way the Luxam reference site does.
This skill captures the design DNA, component patterns, prompt templates, and snippets so any future project can match the same quality bar.
@ -30,16 +30,16 @@ Refuse silently if the project explicitly uses a different design language (e.g.
## Visual principles
1. **Dark cinematic background** obsidian-class blacks (`#08080A`), graphite layering (`#15161A` / `#1C1D22`). Never pure white. Never bright primary colors as backgrounds.
2. **Gold/amber accents** restrained, structural, single-tone gradient. Use on numbers, labels, dividers, hover states, hero highlights. Never bright yellow. Never multi-color rainbow gradients.
1. **Dark cinematic background** obsidian-class blacks (`#08080A`), graphite layering (`#15161A` / `#1C1D22`). Never pure white. Never bright primary colors as backgrounds.
2. **Gold/amber accents** restrained, structural, single-tone gradient. Use on numbers, labels, dividers, hover states, hero highlights. Never bright yellow. Never multi-color rainbow gradients.
3. **Editorial typography pairing**
- Serif (Cormorant Garamond / Playfair / Libre Baskerville) for hero H1 + section H2 only.
- Sans (Geist / Inter / Manrope / Satoshi) for nav, body, buttons, labels, metadata.
- No monospace on user-facing copy or long values.
4. **Glass + grain** semi-transparent `bg-bone/[0.03]` cards with `backdrop-blur`. Subtle noise/grain overlays to break flat surfaces.
5. **Cinematic motion** Framer Motion: slow, restrained. `cubic-bezier(0.16, 1, 0.3, 1)`, 0.8-1.2s entrance, `useReducedMotion()` respected.
6. **Responsive from the start** every layout works at 320 / 375 / 768 / 1024 / 1440 viewports.
7. **No corporate basic** no boxy stock layouts, no flat colorful cards, no generic stock-photo hero, no cheap icon dumps.
4. **Glass + grain** semi-transparent `bg-bone/[0.03]` cards with `backdrop-blur`. Subtle noise/grain overlays to break flat surfaces.
5. **Cinematic motion** Framer Motion: slow, restrained. `cubic-bezier(0.16, 1, 0.3, 1)`, 0.8-1.2s entrance, `useReducedMotion()` respected.
6. **Responsive from the start** every layout works at 320 / 375 / 768 / 1024 / 1440 viewports.
7. **No corporate basic** no boxy stock layouts, no flat colorful cards, no generic stock-photo hero, no cheap icon dumps.
Read `DESIGN_SYSTEM.md` for the full token palette + typography clamps.
@ -47,13 +47,13 @@ Read `DESIGN_SYSTEM.md` for the full token palette + typography clamps.
## Coding principles
1. **Inspect before changing.** Always read the existing files, lockfile, framework version, and existing primitives. Reuse don't duplicate.
1. **Inspect before changing.** Always read the existing files, lockfile, framework version, and existing primitives. Reuse don't duplicate.
2. **Preserve project identity.** Don't rewrite the whole brand for a small request.
3. **Real content only.** Never invent links, social handles, certifications, addresses, phone numbers, or legal claims. Ask if missing.
4. **External links** carry `target="_blank"` + `rel="noopener noreferrer"`.
5. **Reusable primitives first.** Look for existing `Section`, `Container`, `Button`, `Reveal`, `SectionLabel`, `GlassCard` before writing new ones.
6. **Clean imports.** Remove unused icons / helpers after edits.
7. **One source of truth** for spacing push to a shared `Section` primitive instead of overriding per page.
7. **One source of truth** for spacing push to a shared `Section` primitive instead of overriding per page.
8. **Typecheck after every change.** `npx tsc --noEmit` must pass.
9. **No backwards-compat shims.** Don't leave `// removed` comments, dead aliases, or "for future" stubs.
10. **No commented-out code** in committed work.
@ -71,7 +71,7 @@ Read `COMPONENT_RULES.md` for per-element rules (buttons, cards, footer, forms,
- Modal/lightbox: `role="dialog" aria-modal="true"`, `Escape` closes, scroll-lock on body.
- Decorative SVGs: `aria-hidden`.
- Color contrast: body text `text-bone` (≈ #F4F0E6) on obsidian = 16:1 ratio, well above WCAG AA.
- `prefers-reduced-motion` respected via Framer's `useReducedMotion()` disable transforms/parallax, keep opacity fades.
- `prefers-reduced-motion` respected via Framer's `useReducedMotion()` disable transforms/parallax, keep opacity fades.
---
@ -83,7 +83,7 @@ Test mentally (or in browser) at: **320, 375, 390, 414, 768, 1024, 1440**.
- Section padding: `py-16 md:py-20 lg:py-24` (uniform).
- Anchored sections: `scroll-mt-24 md:scroll-mt-28` for sticky-nav clearance.
- `body { overflow-x: hidden }` in `globals.css` as a global guard.
- Sticky decorations (halos, watermarks, blurs) must not introduce horizontal scroll use `overflow-hidden` on parent + `translate` instead of fixed widths.
- Sticky decorations (halos, watermarks, blurs) must not introduce horizontal scroll use `overflow-hidden` on parent + `translate` instead of fixed widths.
- Mobile cards stack `grid-cols-1 sm:grid-cols-2 lg:grid-cols-N`.
Read `RESPONSIVE_CHECKLIST.md` before shipping any page.
@ -94,12 +94,12 @@ Read `RESPONSIVE_CHECKLIST.md` before shipping any page.
Every meaningful change returns:
1. **Files changed** explicit list with `NEW` / `MODIFIED` / `DELETED` markers.
1. **Files changed** explicit list with `NEW` / `MODIFIED` / `DELETED` markers.
2. **What was kept** vs **what was replaced** when redesigning.
3. **Receipts** `HTTP 200`, typecheck clean, key content rendered (grep output).
4. **Responsive breakdown** desktop / tablet / mobile behavior.
5. **Confirmations** explicit yes/no on every checklist item the user asked.
6. **No placeholder leaks** confirm zero `href="#"`, `href=""`, `href="/#"` remain in modified files.
3. **Receipts** `HTTP 200`, typecheck clean, key content rendered (grep output).
4. **Responsive breakdown** desktop / tablet / mobile behavior.
5. **Confirmations** explicit yes/no on every checklist item the user asked.
6. **No placeholder leaks** confirm zero `href="#"`, `href=""`, `href="/#"` remain in modified files.
---
@ -123,21 +123,21 @@ Every meaningful change returns:
## Brand-specific config (Luxam reference)
`examples/luxam-config.md` holds the exact Luxam contact details, WhatsApp URL, app store links, agency credit, etc. **Do not copy these values into other projects** use them only as the shape pattern. Each new project gets its own `lib/content.ts` populated from real values the user provides.
`examples/luxam-config.md` holds the exact Luxam contact details, WhatsApp URL, app store links, agency credit, etc. **Do not copy these values into other projects** use them only as the shape pattern. Each new project gets its own `lib/content.ts` populated from real values the user provides.
---
## File map of this skill
```
SKILL.md this file (entry point, principles, when to use)
DESIGN_SYSTEM.md color tokens, typography, spacing scale, motion
COMPONENT_RULES.md per-component rules (buttons, cards, footer, forms, icons)
RESPONSIVE_CHECKLIST.md pre-ship checklist for every viewport
PROMPT_TEMPLATES.md copy-paste prompts for common tasks
COMPONENT_SNIPPETS.md high-level component overview
snippets/ 8 reusable TSX component files
examples/luxam-config.md Luxam-specific values (reference, not boilerplate)
SKILL.md this file (entry point, principles, when to use)
DESIGN_SYSTEM.md color tokens, typography, spacing scale, motion
COMPONENT_RULES.md per-component rules (buttons, cards, footer, forms, icons)
RESPONSIVE_CHECKLIST.md pre-ship checklist for every viewport
PROMPT_TEMPLATES.md copy-paste prompts for common tasks
COMPONENT_SNIPPETS.md high-level component overview
snippets/ 8 reusable TSX component files
examples/luxam-config.md Luxam-specific values (reference, not boilerplate)
```
Read the relevant section(s) for the task at hand. Don't dump the whole skill into the user's reply surface only the rules + snippets relevant to the change being made.
Read the relevant section(s) for the task at hand. Don't dump the whole skill into the user's reply surface only the rules + snippets relevant to the change being made.

View File

@ -1,6 +1,6 @@
# Luxam Reference Config
Concrete values used in the Luxam project. **Reference only** do not paste into other projects unless the user explicitly says these are also their values.
Concrete values used in the Luxam project. **Reference only** do not paste into other projects unless the user explicitly says these are also their values.
## `src/lib/content.ts`
@ -115,4 +115,4 @@ Valid from 11 August 2025
Valid to 10 August 2026
```
For any new project that surfaces compliance docs, follow the same data shape: number, standard, issuer, certified entity, site, scope, material, validity dates. Pull only from official documents never invent.
For any new project that surfaces compliance docs, follow the same data shape: number, standard, issuer, certified entity, site, scope, material, validity dates. Pull only from official documents never invent.

View File

@ -12,7 +12,7 @@
* appStore: "https://apps.apple.com/ae/app/.../id...",
* };
*
* next/image blocks raw SVG by default in Next 16 keep plain <img>.
* next/image blocks raw SVG by default in Next 16 keep plain <img>.
*/
import { app } from "@/lib/content";

View File

@ -1,7 +1,7 @@
"use client";
/**
* 4-image certificate gallery each tile links to a PDF in a new tab.
* 4-image certificate gallery each tile links to a PDF in a new tab.
*
* Expected data shape:
* export type CertPage = {
@ -65,7 +65,7 @@ function CertCard({ src, alt, title, page, document, href }: CertPage) {
href={href}
target="_blank"
rel="noopener noreferrer"
aria-label={`${title} ${page}. Opens PDF in a new tab.`}
aria-label={`${title} ${page}. Opens PDF in a new tab.`}
className="group relative isolate flex h-full flex-col overflow-hidden rounded-2xl border border-bone/10 bg-graphite/40 backdrop-blur-sm transition-all duration-500 hover:-translate-y-1 hover:border-gold/35 hover:bg-graphite/60 focus:outline-none focus-visible:border-gold/60 focus-visible:ring-2 focus-visible:ring-gold/40"
>
<div className="relative aspect-[3/4] w-full overflow-hidden bg-obsidian">

View File

@ -59,7 +59,7 @@ export function Footer({ navigationLinks, serviceItems, legalLinks, logo }: Prop
<footer className="relative overflow-hidden border-t border-bone/10 bg-obsidian pb-8 pt-11 md:pb-10 md:pt-14 lg:pb-12 lg:pt-[72px]">
<div className="relative z-10 mx-auto max-w-[1480px] px-6 md:px-10">
<div className="grid gap-10 lg:grid-cols-12 lg:gap-12">
{/* Col 1 Brand + contact */}
{/* Col 1 Brand + contact */}
<div className="space-y-5 lg:col-span-4">
<a href="/" className="inline-flex items-center gap-4">
{logo}
@ -152,7 +152,7 @@ export function Footer({ navigationLinks, serviceItems, legalLinks, logo }: Prop
</div>
</div>
{/* Watermark contained inside footer */}
{/* Watermark contained inside footer */}
<div
aria-hidden
className="pointer-events-none absolute inset-x-0 bottom-0 z-0 flex select-none justify-center overflow-hidden"

View File

@ -8,7 +8,7 @@ type Props = {
eyebrow?: string;
index?: string;
title: ReactNode;
/** The gold-accent phrase rendered inside the title pass as JSX or separate prop. */
/** The gold-accent phrase rendered inside the title pass as JSX or separate prop. */
accent?: ReactNode;
subtitle?: ReactNode;
align?: "left" | "center";

View File

@ -3,12 +3,12 @@ node_modules
.git
.gitignore
# Local env files set these as environment variables in Coolify instead
# Local env files set these as environment variables in Coolify instead
.env
.env.local
.env.*.local
# SQLite database files mount /app/prisma as a persistent volume in Coolify
# SQLite database files mount /app/prisma as a persistent volume in Coolify
prisma/*.db
prisma/*.db-shm
prisma/*.db-wal

View File

@ -1,5 +1,5 @@
# ─────────────────────────────────────────────────────────────────────────────
# Stripe https://dashboard.stripe.com/apikeys
# Stripe https://dashboard.stripe.com/apikeys
# ─────────────────────────────────────────────────────────────────────────────
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_SECRET_KEY=sk_live_...
@ -11,7 +11,7 @@ STRIPE_WEBHOOK_SECRET=whsec_...
ADMIN_JWT_SECRET=change_me_generate_with_openssl_rand_hex_32
# ─────────────────────────────────────────────────────────────────────────────
# Database SQLite via libsql
# Database SQLite via libsql
# In Coolify: set to file:/app/prisma/lootah.db
# and mount /app/prisma as a persistent volume
# ─────────────────────────────────────────────────────────────────────────────

View File

@ -11,7 +11,7 @@ Add password authentication to the admin dashboard, enable full CRUD for persona
- Create `prisma/schema.prisma` with SQLite provider
- Models: `AdminUser` (id, username, passwordHash, createdAt), `AppSettings` (key, value)
- Run `npx prisma db push` to create the database
- Create `src/lib/prisma.ts` singleton Prisma client
- Create `src/lib/prisma.ts` singleton Prisma client
2. **Create seed script** `prisma/seed.ts`
- Creates default admin user with bcrypt-hashed password
@ -19,10 +19,10 @@ Add password authentication to the admin dashboard, enable full CRUD for persona
- Run with `npx prisma db seed`
3. **Create admin auth API routes**
- `src/app/api/admin/login/route.ts` POST, accepts `{ username, password }`, verifies bcrypt hash from DB, returns JWT in httpOnly cookie (JWT secret from DB)
- `src/app/api/admin/verify/route.ts` GET, checks JWT cookie validity
- `src/app/api/admin/logout/route.ts` POST, clears auth cookie
- `src/app/api/admin/change-password/route.ts` POST, accepts `{ currentPassword, newPassword }`, updates hash in DB
- `src/app/api/admin/login/route.ts` POST, accepts `{ username, password }`, verifies bcrypt hash from DB, returns JWT in httpOnly cookie (JWT secret from DB)
- `src/app/api/admin/verify/route.ts` GET, checks JWT cookie validity
- `src/app/api/admin/logout/route.ts` POST, clears auth cookie
- `src/app/api/admin/change-password/route.ts` POST, accepts `{ currentPassword, newPassword }`, updates hash in DB
4. **Create admin middleware** `src/middleware.ts`
- Protects all `/admin/*` routes (except `/admin/login/`)
@ -34,7 +34,7 @@ Add password authentication to the admin dashboard, enable full CRUD for persona
- Calls login API, redirects to `/admin/` on success
- Shows error on wrong credentials
6. **Add JWT_SECRET to .env.local** (only this one password is in DB)
6. **Add JWT_SECRET to .env.local** (only this one password is in DB)
- Alternative: store JWT secret in DB and load at startup into a module-level cache
### Dependencies to install
@ -43,12 +43,12 @@ Add password authentication to the admin dashboard, enable full CRUD for persona
## Phase 2: Full CRUD for Pricing Items + Personas
### Steps (depends on Phase 1)
6. **Add/Remove pricing items in admin** Update `src/app/admin/page.tsx`
6. **Add/Remove pricing items in admin** Update `src/app/admin/page.tsx`
- "Add Item" button with fields: id (auto-slug from label), label, price
- Delete button per row (with confirmation)
- Update `usePricingStore` to support `addItem()` and `removeItem()` actions
7. **Persona management section** New section in admin page
7. **Persona management section** New section in admin page
- List current personas with edit capability (label, description, colors)
- Add new persona form
- Delete persona button
@ -58,7 +58,7 @@ Add password authentication to the admin dashboard, enable full CRUD for persona
## Phase 3: Additional Admin Features
### Steps (parallel with Phase 2)
8. **Orders dashboard section** New section in admin page
8. **Orders dashboard section** New section in admin page
- Show orders from Stripe API (list recent payments)
- Display: order ID, customer email, amount, status, date
- New API route `src/app/api/admin/orders/route.ts` to fetch from Stripe
@ -77,18 +77,18 @@ Add password authentication to the admin dashboard, enable full CRUD for persona
11. **Logout button** in admin header
## Relevant Files
- `src/app/admin/page.tsx` Main admin dashboard (add CRUD UI, analytics)
- `src/app/admin/login/page.tsx` New login page
- `src/app/api/admin/login/route.ts` New auth API
- `src/app/api/admin/verify/route.ts` New verify API
- `src/app/api/admin/orders/route.ts` New orders API
- `src/middleware.ts` New, protects admin routes
- `src/lib/prisma.ts` New, Prisma client singleton
- `prisma/schema.prisma` New, database schema (AdminUser, AppSettings)
- `prisma/seed.ts` New, seeds default admin user
- `src/store/usePricingStore.ts` Add `addItem()`, `removeItem()` actions
- `src/components/ConfigPanel.tsx` Persona options currently hardcoded (PERSONA_OPTIONS const)
- `.env.local` Add ADMIN_PASSWORD, ADMIN_JWT_SECRET
- `src/app/admin/page.tsx` Main admin dashboard (add CRUD UI, analytics)
- `src/app/admin/login/page.tsx` New login page
- `src/app/api/admin/login/route.ts` New auth API
- `src/app/api/admin/verify/route.ts` New verify API
- `src/app/api/admin/orders/route.ts` New orders API
- `src/middleware.ts` New, protects admin routes
- `src/lib/prisma.ts` New, Prisma client singleton
- `prisma/schema.prisma` New, database schema (AdminUser, AppSettings)
- `prisma/seed.ts` New, seeds default admin user
- `src/store/usePricingStore.ts` Add `addItem()`, `removeItem()` actions
- `src/components/ConfigPanel.tsx` Persona options currently hardcoded (PERSONA_OPTIONS const)
- `.env.local` Add ADMIN_PASSWORD, ADMIN_JWT_SECRET
## Verification
1. Visit `/admin/` without login → redirected to `/admin/login/`
@ -102,13 +102,13 @@ Add password authentication to the admin dashboard, enable full CRUD for persona
9. Run `npx vitest run` → all existing tests pass
## Decisions
- **Database: Prisma + SQLite** file-based, no external server needed, real ORM
- **Database: Prisma + SQLite** file-based, no external server needed, real ORM
- Admin password stored as bcrypt hash in DB (not in env vars)
- JWT secret stored in AppSettings table in DB
- JWT in httpOnly cookie secure, no localStorage tokens
- jose library for JWT lightweight, edge-compatible (works in Next.js middleware)
- Personas need to move from hardcoded array to store-driven breaking change in ConfigPanel
- Stripe orders fetched via Stripe API directly no local DB needed for orders
- JWT in httpOnly cookie secure, no localStorage tokens
- jose library for JWT lightweight, edge-compatible (works in Next.js middleware)
- Personas need to move from hardcoded array to store-driven breaking change in ConfigPanel
- Stripe orders fetched via Stripe API directly no local DB needed for orders
## Further Considerations
1. **Persona storage**: Currently hardcoded in ConfigPanel. Moving to a store means ConfigPanel reads from store dynamically. This is required for admin CRUD to work. **Recommended: create persona store with localStorage persistence (same pattern as pricing store)**

2
.gitignore vendored
View File

@ -38,6 +38,6 @@ next-env.d.ts
/src/generated/prisma
# Local design references not part of project
# Local design references not part of project
/references

View File

@ -1,9 +1,9 @@
# Changelog Lootah Robotics G1 Configurator
> تاريخ التغييرات 20 أبريل 2026
# Changelog Lootah Robotics G1 Configurator
> تاريخ التغييرات 20 أبريل 2026
---
## 2ff21c5 perf: compress GLBs 75%, add Draco decoder, loading spinner for attire
## 2ff21c5 perf: compress GLBs 75%, add Draco decoder, loading spinner for attire
### المشكلة
- الموبايلات القديمة كانت تعلّق لأوقات طويلة عند تحميل الأزياء الجديدة
@ -25,7 +25,7 @@
---
## b2a484f fix: dynamic attire buttons in ScrollOverlays + mobile touch support
## b2a484f fix: dynamic attire buttons in ScrollOverlays + mobile touch support
### المشكلة
- أزرار الأزياء في صفحة الـ Landing (Kandura, Vest, Suit) كانت مكتوبة بشكل ثابت (hardcoded)
@ -41,7 +41,7 @@
---
## 320b77b fix: contacts API - use ADMIN_JWT_SECRET env var
## 320b77b fix: contacts API - use ADMIN_JWT_SECRET env var
### المشكلة
- صفحة Contacts في الأدمن كانت ترجع خطأ 500
@ -58,7 +58,7 @@
---
## 25ffbf4 feat: add favicon and app icons for PWA support
## 25ffbf4 feat: add favicon and app icons for PWA support
### التغييرات
| الملف | التغيير |
@ -73,7 +73,7 @@
---
## e686d41 fix: use configStore.getState().setPersonaAttire in ScrollOverlays
## e686d41 fix: use configStore.getState().setPersonaAttire in ScrollOverlays
### المشكلة
- بناء Docker كان يفشل مع خطأ TypeScript:
@ -93,7 +93,7 @@
---
## e159965 feat: add GET endpoint to retrieve contact requests with admin authentication
## e159965 feat: add GET endpoint to retrieve contact requests with admin authentication
### التغييرات
| الملف | التغيير |
@ -114,10 +114,10 @@ STRIPE_SECRET_KEY= # للمدفوعات
### هيكل Stores
| Store | الوصف |
|---|---|
| `configStore` (vanilla Zustand) | الألوان والزي النشط يدعم `.getState()` |
| `configStore` (vanilla Zustand) | الألوان والزي النشط يدعم `.getState()` |
| `useConfigStore` (React hook) | wrapper لـ `configStore` للاستخدام داخل components |
| `personaStore` (vanilla Zustand) | قائمة الأزياء تُحمَّل من API عند التهيئة |
| `pricingStore` | أسعار العناصر تُزامَن مع قاعدة البيانات |
| `personaStore` (vanilla Zustand) | قائمة الأزياء تُحمَّل من API عند التهيئة |
| `pricingStore` | أسعار العناصر تُزامَن مع قاعدة البيانات |
### تدفق الأزياء المرفوعة
1. الأدمن يرفع `.glb` من لوحة التحكم

View File

@ -15,7 +15,7 @@ ENV NEXT_TELEMETRY_DISABLED=1
# Generate Prisma client (reads prisma/schema.prisma + prisma.config.ts)
RUN npx prisma generate
# Build Next.js produces .next/standalone (set in next.config.mjs)
# Build Next.js produces .next/standalone (set in next.config.mjs)
RUN npm run build
# ── Stage 3: production runner ─────────────────────────────────────────────────

View File

@ -9,7 +9,7 @@ echo "→ Syncing database schema..."
# db push creates the SQLite file and syncs tables to match schema.prisma
/app/node_modules/.bin/prisma db push
echo "→ Seeding database (idempotent pricing rows are upserted every deploy)..."
echo "→ Seeding database (idempotent pricing rows are upserted every deploy)..."
/app/node_modules/.bin/tsx /app/prisma/seed.ts
echo "→ Starting Next.js on port ${PORT:-3000}..."

View File

@ -47,7 +47,7 @@ async function main() {
console.log('✓ JWT secret already exists, skipping.');
}
// Pricing items upserted on every deploy so the code is the source of truth.
// Pricing items upserted on every deploy so the code is the source of truth.
// Prices in AED. USD → AED at 3.6725 (CBUAE peg).
// Basic = Unitree G1 retail ($16k) + $5k markup = $21k ≈ 77,125 AED.
// EDU = $40k flat ≈ 146,900 AED.

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

View File

@ -10,9 +10,9 @@ import { FounderSection } from '@/components/robotics/FounderSection';
import { ServicesGrid } from '@/components/robotics/ServicesGrid';
export const metadata: Metadata = {
title: 'About YS Lootah Robotics Exclusive UAE Access to Unitree & Pudu',
title: 'About YS Lootah Robotics Exclusive UAE Access to Unitree & Pudu',
description:
'YS Lootah Robotics is part of the Yousuf Saeed Lootah Investment Group a trusted UAE robotics partner delivering AI, automation, and intelligent robotics across Dubai and the UAE.',
'YS Lootah Robotics is part of the Yousuf Saeed Lootah Investment Group a trusted UAE robotics partner delivering AI, automation, and intelligent robotics across Dubai and the UAE.',
};
export default function AboutPage() {
@ -28,7 +28,7 @@ export default function AboutPage() {
<span className="text-gradient" style={{ fontWeight: 500 }}>In Tech We Innovate. In Trust We Lead.</span>
</h1>
<p style={{ margin: 0, color: '#DEE0F0', fontSize: 'clamp(0.95rem, 2vw, 1.05rem)', lineHeight: 1.7 }}>
YS Lootah Robotics is part of the Yousuf Saeed Lootah Investment Group a trusted UAE technology and robotics partner helping businesses innovate, automate, and deploy intelligent robotic solutions. The UAE&apos;s dedicated destination for selected Unitree and Pudu Robotics solutions.
YS Lootah Robotics is part of the Yousuf Saeed Lootah Investment Group a trusted UAE technology and robotics partner helping businesses innovate, automate, and deploy intelligent robotic solutions. The UAE&apos;s dedicated destination for selected Unitree and Pudu Robotics solutions.
</p>
</div>

View File

@ -56,7 +56,7 @@ export default function AdminLoginPage() {
Admin Login
</h1>
<p style={{ fontSize: '0.8rem', color: '#8891C7', margin: 0 }}>
Lootah Robotics G1 Configurator
Lootah Robotics G1 Configurator
</p>
</div>

View File

@ -108,7 +108,7 @@ export default function AdminPage() {
}
}
} else {
// DB is empty push local items to server so they persist for all users
// DB is empty push local items to server so they persist for all users
if (localItems.length > 0) {
await fetch('/api/admin/pricing/', {
method: 'POST',
@ -631,7 +631,7 @@ export default function AdminPage() {
{/* GLB file picker */}
<div style={{ marginTop: '0.625rem' }}>
<label style={labelStyle}>3D Model .glb file (optional)</label>
<label style={labelStyle}>3D Model .glb file (optional)</label>
<label style={{
display: 'flex',
alignItems: 'center',

View File

@ -19,7 +19,7 @@ async function verifyAdmin() {
// GET /api/admin/pricing/ → all pricing items from DB
export async function GET() {
// Public endpoint no auth required so the storefront can read prices
// Public endpoint no auth required so the storefront can read prices
const items = await prisma.pricingItem.findMany({ orderBy: { sortOrder: 'asc' } });
return NextResponse.json({ items });
}

View File

@ -52,7 +52,7 @@ export async function POST(request: Request) {
return NextResponse.json({ error: 'Only .glb files are allowed' }, { status: 400 });
}
// Sanitize the item ID convert underscores to hyphens, keep lowercase letters, digits, hyphens
// Sanitize the item ID convert underscores to hyphens, keep lowercase letters, digits, hyphens
const safeId = itemId.toLowerCase().replace(/_/g, '-').replace(/[^a-z0-9-]/g, '');
if (!safeId) {
return NextResponse.json({ error: 'Invalid itemId' }, { status: 400 });

View File

@ -33,7 +33,7 @@ export async function POST(request: Request) {
stripeStatus = pi.status;
stripeMetadata = (pi.metadata ?? {}) as Record<string, string>;
} catch {
// Stripe unreachable save with client-submitted data
// Stripe unreachable save with client-submitted data
}
const m = stripeMetadata;

View File

@ -4,9 +4,9 @@ import { FooterAndContact } from '@/components/FooterAndContact';
import { InquiryForm } from '@/components/robotics/InquiryForm';
export const metadata: Metadata = {
title: 'Book a Robotics Demo in Dubai YS Lootah Robotics',
title: 'Book a Robotics Demo in Dubai YS Lootah Robotics',
description:
'Book a live robot demo at our Dubai showroom or at your venue. See selected Unitree and Pudu Robotics solutions in action available exclusively in the UAE through YS Lootah Robotics.',
'Book a live robot demo at our Dubai showroom or at your venue. See selected Unitree and Pudu Robotics solutions in action available exclusively in the UAE through YS Lootah Robotics.',
};
export default function BookDemoPage() {
@ -28,7 +28,7 @@ export default function BookDemoPage() {
<ul style={{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
{[
'Choose a humanoid, quadruped, or service robot to see live',
'Designed around your industry restaurant, mall, hotel, security, etc.',
'Designed around your industry restaurant, mall, hotel, security, etc.',
'Walk through configuration and deployment options',
'Get UAE-specific pricing and availability',
].map((b) => (

View File

@ -7,9 +7,9 @@ import { MotionSection } from '@/components/ui/MotionSection';
import { DemoCTA } from '@/components/robotics/DemoCTA';
export const metadata: Metadata = {
title: 'Robotics Brands Unitree & Pudu | Exclusive UAE Access via YS Lootah Robotics',
title: 'Robotics Brands Unitree & Pudu | Exclusive UAE Access via YS Lootah Robotics',
description:
'Selected Unitree and Pudu Robotics solutions available exclusively in the UAE through YS Lootah Robotics. Humanoid, quadruped, service, delivery, and cleaning robots.',
'Selected Unitree and Pudu Robotics solutions available exclusively in the UAE through YS Lootah Robotics. Humanoid, quadruped, service, delivery, and cleaning robots.',
};
const ORDER: RobotBrand[] = ['unitree', 'pudu'];
@ -25,11 +25,11 @@ export default function BrandsPage() {
<span className="eyebrow">Exclusive UAE Access · Dubai</span>
<h1 style={{ margin: 0, fontSize: 'clamp(2rem, 5vw, 3.4rem)', fontWeight: 300, lineHeight: 1.05, letterSpacing: '-0.03em' }}>
<span className="text-gradient" style={{ fontWeight: 500 }}>
Selected Unitree and Pudu solutions exclusively in the UAE.
Selected Unitree and Pudu solutions exclusively in the UAE.
</span>
</h1>
<p style={{ margin: 0, color: '#DEE0F0', fontSize: 'clamp(0.95rem, 2vw, 1.05rem)', lineHeight: 1.7 }}>
YS Lootah Robotics holds exclusive UAE sales rights for selected Unitree and Pudu Robotics solutions with on-the-ground sales, demo, and deployment support across Dubai and the UAE.
YS Lootah Robotics holds exclusive UAE sales rights for selected Unitree and Pudu Robotics solutions with on-the-ground sales, demo, and deployment support across Dubai and the UAE.
</p>
<p style={{ margin: 0, color: '#6a73a5', fontSize: '0.82rem', lineHeight: 1.6 }}>
Brand names and product trademarks are property of their respective owners. Available exclusively in the UAE through YS Lootah Robotics.
@ -84,7 +84,7 @@ export default function BrandsPage() {
<DemoCTA
title="Not sure which brand fits?"
description="Tell us about your venue, timeline, and use case. We will recommend a brand and model and book a live demo at our Dubai showroom."
description="Tell us about your venue, timeline, and use case. We will recommend a brand and model and book a live demo at our Dubai showroom."
/>
</div>
</main>

View File

@ -35,7 +35,7 @@ export default function ConfigurePage() {
<span className="text-gradient" style={{ fontWeight: 500 }}>Configure your robot.</span>
</h1>
<p style={{ margin: 0, color: '#8891C7', fontSize: 'clamp(0.85rem, 1.6vw, 0.95rem)', lineHeight: 1.6, maxWidth: 720 }}>
Choose persona, attire, colors, and accessories visualize your Unitree G1 humanoid before you request a quotation from YS Lootah Robotics.
Choose persona, attire, colors, and accessories visualize your Unitree G1 humanoid before you request a quotation from YS Lootah Robotics.
</p>
</div>
</header>

View File

@ -4,9 +4,9 @@ import { FooterAndContact } from '@/components/FooterAndContact';
import { InquiryForm } from '@/components/robotics/InquiryForm';
export const metadata: Metadata = {
title: 'Contact YS Lootah Robotics Dubai Robotics Sales & Demo Inquiries',
title: 'Contact YS Lootah Robotics Dubai Robotics Sales & Demo Inquiries',
description:
'Dubai robotics sales, support, and demo inquiries. Contact YS Lootah Robotics the exclusive UAE destination for selected Unitree and Pudu Robotics solutions by phone, email, or WhatsApp.',
'Dubai robotics sales, support, and demo inquiries. Contact YS Lootah Robotics the exclusive UAE destination for selected Unitree and Pudu Robotics solutions by phone, email, or WhatsApp.',
};
export default function ContactPage() {

View File

@ -1,7 +1,7 @@
@import "tailwindcss";
@theme {
/* === Luxury robotics Graphite + Royal Blue + Silver === */
/* === Luxury robotics Graphite + Royal Blue + Silver === */
--color-bg: #0a0a0c;
--color-bg-2: #16151a;
--color-bg-3: #1c1b21;

View File

@ -6,7 +6,7 @@ import { DemoCTA } from '@/components/robotics/DemoCTA';
import { MotionSection } from '@/components/ui/MotionSection';
export const metadata: Metadata = {
title: 'Robotics for UAE Industries YS Lootah Robotics Dubai',
title: 'Robotics for UAE Industries YS Lootah Robotics Dubai',
description:
'Robotics solutions for hospitality, restaurants, hotels, healthcare, education, security, warehouses, smart buildings, and government across the UAE.',
};
@ -26,7 +26,7 @@ export default function IndustriesPage() {
</span>
</h1>
<p style={{ margin: 0, color: '#DEE0F0', fontSize: 'clamp(0.95rem, 2vw, 1.05rem)', lineHeight: 1.7 }}>
We deploy humanoid, quadruped, and service robots across industries that are reshaping how the UAE operates every venue is matched to the right robot.
We deploy humanoid, quadruped, and service robots across industries that are reshaping how the UAE operates every venue is matched to the right robot.
</p>
</div>
@ -36,7 +36,7 @@ export default function IndustriesPage() {
<DemoCTA
title="Don't see your industry?"
description="If your venue is unusual, complex, or one-of-a-kind that's exactly when we like to talk. Let's design the robotics fit."
description="If your venue is unusual, complex, or one-of-a-kind that's exactly when we like to talk. Let's design the robotics fit."
/>
</div>
</main>

View File

@ -6,7 +6,7 @@ import "./globals.css";
export const metadata: Metadata = {
metadataBase: new URL('https://yslootahrobotics.com'),
title: {
default: 'YS Lootah Robotics Exclusive UAE Access to Unitree & Pudu Robotics',
default: 'YS Lootah Robotics Exclusive UAE Access to Unitree & Pudu Robotics',
template: '%s | YS Lootah Robotics',
},
description:
@ -28,7 +28,7 @@ export const metadata: Metadata = {
],
openGraph: {
type: 'website',
title: 'YS Lootah Robotics Exclusive UAE Access to Unitree & Pudu Robotics',
title: 'YS Lootah Robotics Exclusive UAE Access to Unitree & Pudu Robotics',
description:
'Advanced robotics. Exclusive UAE access. Selected Unitree and Pudu solutions available through YS Lootah Robotics in Dubai.',
locale: 'en_AE',
@ -36,7 +36,7 @@ export const metadata: Metadata = {
},
twitter: {
card: 'summary_large_image',
title: 'YS Lootah Robotics Exclusive UAE Access to Unitree & Pudu Robotics',
title: 'YS Lootah Robotics Exclusive UAE Access to Unitree & Pudu Robotics',
description:
'The UAEs dedicated destination for Unitree and Pudu Robotics sales, demo inquiries, and business deployment.',
},

View File

@ -20,7 +20,7 @@ import { MotionSection } from '@/components/ui/MotionSection';
import { FEATURED_ROBOTS } from '@/data/robots';
export const metadata: Metadata = {
title: 'YS Lootah Robotics Exclusive UAE Access to Unitree & Pudu Robotics',
title: 'YS Lootah Robotics Exclusive UAE Access to Unitree & Pudu Robotics',
description:
'YS Lootah Robotics is the exclusive UAE sales destination for selected Unitree and Pudu Robotics solutions. Explore, configure, book demos, and deploy advanced robots across Dubai and the UAE.',
keywords: [
@ -65,30 +65,30 @@ export default function HomePage() {
<div className="container-wide" style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
<SectionHeading
eyebrow="Featured robotics brands"
title="Unitree & Pudu available exclusively in the UAE."
description="We hold exclusive UAE sales rights for selected Unitree and Pudu Robotics solutions curated for proven performance in the field and full local support across Dubai."
title="Unitree & Pudu available exclusively in the UAE."
description="We hold exclusive UAE sales rights for selected Unitree and Pudu Robotics solutions curated for proven performance in the field and full local support across Dubai."
/>
<BrandShowcase />
</div>
</MotionSection>
<MotionSection style={{ padding: 'clamp(3rem, 6vw, 5rem) 0' }} id="why">
<MotionSection style={{ padding: 'clamp(3rem, 6vw, 5rem) 0 clamp(1.5rem, 3vw, 2.5rem)' }} id="why">
<div className="container-wide" style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
<SectionHeading
eyebrow="Why YS Lootah Robotics"
title="A premium robotics partner not a marketplace."
title="A premium robotics partner not a marketplace."
description="From inquiry to live deployment, we manage your robotics program end to end with a UAE-based team and exclusive access to Unitree and Pudu portfolios."
/>
<BentoGrid />
</div>
</MotionSection>
<MotionSection style={{ padding: 'clamp(3rem, 6vw, 5rem) 0' }} id="categories">
<MotionSection style={{ padding: 'clamp(1.5rem, 3vw, 2.5rem) 0 clamp(3rem, 6vw, 5rem)' }} id="categories">
<div className="container-wide" style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
<SectionHeading
eyebrow="Robot categories"
title="From quadruped patrol to humanoid concierge."
description="Find the right robot for your business humanoid for events and education, quadruped for inspection and security, service and delivery for hospitality."
description="Find the right robot for your business humanoid for events and education, quadruped for inspection and security, service and delivery for hospitality."
/>
<RobotCategoryGrid />
</div>
@ -126,7 +126,7 @@ export default function HomePage() {
<SectionHeading
eyebrow="Our solutions"
title="Intelligent robotics across the full lifecycle."
description="From autonomous robotics and intelligent automation to motion programming, SDKs, and remote diagnostics a complete robotics partner for UAE businesses."
description="From autonomous robotics and intelligent automation to motion programming, SDKs, and remote diagnostics a complete robotics partner for UAE businesses."
/>
<ServicesGrid />
</div>
@ -149,7 +149,7 @@ export default function HomePage() {
<SectionHeading
eyebrow="How we work"
title="From inquiry to live deployment."
description="Three steps from your first message to a robot running in your venue fully supported by our Dubai team."
description="Three steps from your first message to a robot running in your venue fully supported by our Dubai team."
/>
<HowItWorks />
</div>
@ -160,7 +160,7 @@ export default function HomePage() {
<SectionHeading
eyebrow="Industries served"
title="Robotics solutions for UAE businesses."
description="From restaurants to security operators to government innovation programs we deliver robotics fits for the venue."
description="From restaurants to security operators to government innovation programs we deliver robotics fits for the venue."
/>
<IndustryUseCases limit={8} />
</div>

View File

@ -21,10 +21,10 @@ export function generateStaticParams() {
export async function generateMetadata({ params }: { params: Promise<Params> }): Promise<Metadata> {
const { slug } = await params;
const robot = getRobotBySlug(slug);
if (!robot) return { title: 'Robot not found YS Lootah Robotics' };
if (!robot) return { title: 'Robot not found YS Lootah Robotics' };
return {
title: `${robot.brandLabel} ${robot.name} Available in Dubai | YS Lootah Robotics`,
description: `${robot.shortDescription} Available through YS Lootah Robotics in Dubai request a price or book a live demo.`,
title: `${robot.brandLabel} ${robot.name} Available in Dubai | YS Lootah Robotics`,
description: `${robot.shortDescription} Available through YS Lootah Robotics in Dubai request a price or book a live demo.`,
keywords: [robot.brandLabel, robot.name, 'Dubai', 'UAE', CATEGORY_LABELS[robot.category], 'robotics'],
};
}
@ -167,7 +167,7 @@ export default async function RobotDetailPage({ params }: { params: Promise<Para
<span className="eyebrow">Talk to an advisor</span>
<h3 style={{ margin: 0, fontSize: '1.25rem', fontWeight: 600 }}>Prefer a quick conversation?</h3>
<p style={{ margin: 0, color: '#DEE0F0', lineHeight: 1.7 }}>
Call our Dubai team or message us on WhatsApp we will share availability and demo slots for {robot.name}.
Call our Dubai team or message us on WhatsApp we will share availability and demo slots for {robot.name}.
</p>
<a className="btn btn-ghost" href="tel:+971559482728">Call +971 55 948 2728</a>
<a className="btn btn-outline" href="https://wa.me/971559482728" target="_blank" rel="noopener noreferrer">WhatsApp us</a>
@ -179,7 +179,7 @@ export default async function RobotDetailPage({ params }: { params: Promise<Para
<MotionSection>
<ConfigureCTA
title={`Configure ${robot.name} for your business.`}
description="Choose persona, attire, color, and accessories visualize your deployment before requesting a quotation."
description="Choose persona, attire, color, and accessories visualize your deployment before requesting a quotation."
/>
</MotionSection>

View File

@ -6,9 +6,9 @@ import { RoboticsSplineShowcase } from '@/components/sections/robotics-spline-sh
import { ROBOTS } from '@/data/robots';
export const metadata: Metadata = {
title: 'Robots Catalog YS Lootah Robotics Dubai',
title: 'Robots Catalog YS Lootah Robotics Dubai',
description:
'Explore selected humanoid, quadruped, service, delivery, hospitality, and cleaning robots from Unitree and Pudu available exclusively in the UAE through YS Lootah Robotics.',
'Explore selected humanoid, quadruped, service, delivery, hospitality, and cleaning robots from Unitree and Pudu available exclusively in the UAE through YS Lootah Robotics.',
};
export default function RobotsPage() {

View File

@ -73,7 +73,7 @@ export function FooterAndContact() {
In Tech We Innovate · In Trust We Lead
</p>
<p style={{ margin: 0, color: '#8891C7', lineHeight: 1.7, fontSize: '0.95rem' }}>
Innovating today for a smarter tomorrow. YS Lootah Robotics the UAE&apos;s dedicated destination for selected Unitree and Pudu Robotics solutions.
Innovating today for a smarter tomorrow. YS Lootah Robotics the UAE&apos;s dedicated destination for selected Unitree and Pudu Robotics solutions.
</p>
<div style={{ display: 'flex', gap: '0.625rem', marginTop: '1.5rem' }}>

View File

@ -67,7 +67,7 @@ export function Navbar() {
transition: 'padding 0.3s ease',
}}
>
<Link href="/" style={{ textDecoration: 'none', display: 'flex', alignItems: 'center', gap: '0.65rem', minWidth: 0 }} onClick={() => setMobileOpen(false)} aria-label="YS Lootah Robotics Home">
<Link href="/" style={{ textDecoration: 'none', display: 'flex', alignItems: 'center', gap: '0.65rem', minWidth: 0 }} onClick={() => setMobileOpen(false)} aria-label="YS Lootah Robotics Home">
<span
style={{
position: 'relative',

View File

@ -16,7 +16,7 @@ const STATIC_ATTIRE_GLB: Record<string, string> = {
'business-suit': '/Suit.glb',
};
// Same chassis GLB for both variants differ in software/licensing (EDU = open-source
// Same chassis GLB for both variants differ in software/licensing (EDU = open-source
// education / research build), not in geometry.
const BODY_GLB: Record<'basic' | 'edu', string> = {
basic: '/Unitree_G1.glb',

View File

@ -24,7 +24,7 @@ export function PaymentStep() {
return;
}
// Move to review actual confirmation happens on "Place Order"
// Move to review actual confirmation happens on "Place Order"
orderStore.getState().setPayment({ status: 'idle' });
orderStore.getState().setStep('review');
setIsLoading(false);

View File

@ -46,7 +46,7 @@ export function ReviewStep() {
return;
}
// Payment succeeded save order to DB and upload snapshot
// Payment succeeded save order to DB and upload snapshot
const paymentIntentId = orderStore.getState().payment.paymentIntentId;
const s = orderStore.getState().shipping;
const configSummary = orderStore.getState().personaSummary;

View File

@ -34,7 +34,7 @@ export function BentoGrid() {
<div style={{ position: 'relative', display: 'flex', flexDirection: 'column', gap: '0.75rem', maxWidth: '50%' }}>
<span className="eyebrow">Flagship · Dubai</span>
<h3 style={{ margin: 0, fontSize: 'clamp(1.4rem, 2.6vw, 2rem)', fontWeight: 500, letterSpacing: '-0.02em', lineHeight: 1.1 }}>
<span className="text-gradient">Unitree G1 humanoid live in our Dubai showroom.</span>
<span className="text-gradient">Unitree G1 humanoid live in our Dubai showroom.</span>
</h3>
<p style={{ margin: 0, color: '#DEE0F0', fontSize: '0.92rem', lineHeight: 1.6 }}>
Configure persona, attire, and accessories. Then book a live demo.
@ -52,7 +52,7 @@ export function BentoGrid() {
<span className="text-gradient">Fast quotes for UAE businesses.</span>
</h3>
<p style={{ margin: 0, color: '#8891C7', fontSize: '0.88rem', lineHeight: 1.55 }}>
Tell us your use case we respond within one business day with availability and pricing.
Tell us your use case we respond within one business day with availability and pricing.
</p>
</Tile>
@ -69,7 +69,7 @@ export function BentoGrid() {
<Tile col="span 4" row="span 1" accent="#DEE0F0" icon={SOC_PATHS.brain}>
<span className="eyebrow">Exclusive UAE access</span>
<h3 style={{ margin: 0, fontSize: 'clamp(1.05rem, 1.8vw, 1.3rem)', fontWeight: 500, letterSpacing: '-0.01em', lineHeight: 1.2 }}>
<span className="text-gradient">Unitree · Pudu one Dubai team.</span>
<span className="text-gradient">Unitree · Pudu one Dubai team.</span>
</h3>
</Tile>

View File

@ -15,14 +15,14 @@ type BrandVisual = {
const BRAND_VISUALS: Record<RobotBrand, BrandVisual> = {
unitree: {
description:
'Quadruped and humanoid robotics platforms available exclusively in the UAE through YS Lootah Robotics.',
'Quadruped and humanoid robotics platforms available exclusively in the UAE through YS Lootah Robotics.',
chips: ['Quadruped', 'Humanoid', 'Inspection', 'Research'],
primary: { src: '/images/robots/unitree-g1.png', alt: 'Unitree G1 humanoid robot' },
secondary: { src: '/images/robots/unitree-go2.png', alt: 'Unitree Go2 quadruped robot' },
},
pudu: {
description:
'Service, delivery, cleaning, and hospitality robotics available exclusively in the UAE through YS Lootah Robotics.',
'Service, delivery, cleaning, and hospitality robotics available exclusively in the UAE through YS Lootah Robotics.',
chips: ['Service', 'Delivery', 'Cleaning', 'Hospitality'],
primary: { src: '/images/robots/pudu-bellabot.svg', alt: 'Pudu BellaBot delivery robot' },
secondary: { src: '/images/robots/pudu-kettybot.svg', alt: 'Pudu KettyBot service robot' },
@ -128,7 +128,7 @@ function BrandCard({
}}
/>
{/* LEFT copy */}
{/* LEFT copy */}
<div
style={{
position: 'relative',
@ -255,7 +255,7 @@ function BrandCard({
</span>
</div>
{/* RIGHT product visual */}
{/* RIGHT product visual */}
<div
style={{
position: 'relative',

View File

@ -41,7 +41,7 @@ export function BuSunaidahSection() {
</span>
</h2>
<p style={{ margin: 0, color: '#DEE0F0', lineHeight: 1.7, fontSize: '1rem' }}>
An Emirati culture-inspired robotics character Bu Sunaidah is YS Lootah Robotics&apos; community and storytelling ambassador. Designed to celebrate UAE heritage at media events, exhibitions, and innovation programs, Bu Sunaidah shows what intelligent automation looks like when it speaks the language of the region.
An Emirati culture-inspired robotics character Bu Sunaidah is YS Lootah Robotics&apos; community and storytelling ambassador. Designed to celebrate UAE heritage at media events, exhibitions, and innovation programs, Bu Sunaidah shows what intelligent automation looks like when it speaks the language of the region.
</p>
<ul style={{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: '0.6rem' }}>
{[
@ -112,7 +112,7 @@ export function BuSunaidahSection() {
>
<Image
src="/images/robots/unitree-g1.png"
alt="Bu Sunaidah Emirati-inspired robotics character based on a Unitree G1 humanoid"
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)' }}

View File

@ -100,7 +100,7 @@ export function CompanyStory() {
alignItems: 'stretch',
}}
>
{/* Left narrative */}
{/* Left narrative */}
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.25rem', minWidth: 0 }}>
<span className="eyebrow">About YS Lootah Robotics</span>
@ -186,7 +186,7 @@ export function CompanyStory() {
</div>
</div>
{/* Right capability modules */}
{/* Right capability modules */}
<div
style={{
position: 'relative',

View File

@ -11,7 +11,7 @@ type Props = {
export function ConfigureCTA({
title = 'Configure a robotics solution for your business.',
description = 'Build a tailored robot choose persona, attire, color, accessories, and accessories. Visualize before you request a quote.',
description = 'Build a tailored robot choose persona, attire, color, accessories, and accessories. Visualize before you request a quote.',
href = '/configure/',
ctaLabel = 'Start configuration',
}: Props) {

View File

@ -36,7 +36,7 @@ export function ExclusiveAccessSection() {
className="exa-grid"
style={{ position: 'relative', display: 'grid', gap: 'clamp(1.5rem, 3.5vw, 2.5rem)', alignItems: 'stretch' }}
>
{/* Left copy */}
{/* Left copy */}
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.25rem', minWidth: 0 }}>
<span
style={{
@ -66,7 +66,7 @@ export function ExclusiveAccessSection() {
</h2>
<p style={{ margin: 0, color: '#DEE0F0', fontSize: 'clamp(0.92rem, 1.8vw, 1rem)', lineHeight: 1.7, maxWidth: 520 }}>
Selected solutions delivered to UAE businesses, venues, and innovators from inquiry to live deployment, fully supported in Dubai.
Selected solutions delivered to UAE businesses, venues, and innovators from inquiry to live deployment, fully supported in Dubai.
</p>
<ul style={{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
@ -91,7 +91,7 @@ export function ExclusiveAccessSection() {
</div>
</div>
{/* Right premium showroom grid */}
{/* Right premium showroom grid */}
<div
style={{
display: 'grid',
@ -212,7 +212,7 @@ function BrandShowcaseTile({
}}
/>
{/* secondary robot back-left */}
{/* secondary robot back-left */}
<div
style={{
position: 'absolute',
@ -235,7 +235,7 @@ function BrandShowcaseTile({
/>
</div>
{/* primary robot front-right, slightly larger */}
{/* primary robot front-right, slightly larger */}
<div
style={{
position: 'absolute',
@ -312,7 +312,7 @@ function TerritoryTile() {
boxShadow: '0 12px 36px rgba(0,0,0,0.55), inset 0 1px 0 rgba(222,224,240,0.06)',
}}
>
{/* abstract UAE "territory" graphic ring map style */}
{/* abstract UAE "territory" graphic ring map style */}
<svg
aria-hidden
viewBox="0 0 220 140"
@ -345,7 +345,7 @@ function TerritoryTile() {
</span>
</div>
<span style={{ position: 'relative', fontSize: '0.72rem', color: '#DEE0F0', lineHeight: 1.4, marginTop: 'auto' }}>
Exclusive sales rights selected Unitree & Pudu solutions.
Exclusive sales rights selected Unitree & Pudu solutions.
</span>
</div>
);

View File

@ -40,7 +40,7 @@ export function FounderSection() {
Founder · Yousuf Saeed Lootah Investment Group
</p>
<p style={{ margin: 0, color: '#DEE0F0', lineHeight: 1.7, fontSize: '1rem' }}>
Under the leadership of Mr. Yousif Bin Saeed Al Lootah, the group has championed innovation, trust, and long-term value creation across the UAE from technology and robotics to investment and enterprise. YS Lootah Robotics carries that vision into the future of intelligent automation.
Under the leadership of Mr. Yousif Bin Saeed Al Lootah, the group has championed innovation, trust, and long-term value creation across the UAE from technology and robotics to investment and enterprise. YS Lootah Robotics carries that vision into the future of intelligent automation.
</p>
<p style={{ margin: 0, color: '#8891C7', lineHeight: 1.7, fontSize: '0.92rem' }}>
<em>Innovating today for a smarter tomorrow.</em>

View File

@ -155,7 +155,7 @@ export function Hero3DRobotics() {
</h1>
<p style={{ margin: 0, color: '#DEE0F0', fontSize: 'clamp(1rem, 2vw, 1.18rem)', lineHeight: 1.7, maxWidth: 600, fontWeight: 300 }}>
YS Lootah Robotics is the exclusive UAE sales destination for selected Unitree and Pudu Robotics solutions helping businesses explore, configure, book demos, and deploy advanced robots across Dubai and the UAE.
YS Lootah Robotics is the exclusive UAE sales destination for selected Unitree and Pudu Robotics solutions helping businesses explore, configure, book demos, and deploy advanced robots across Dubai and the UAE.
</p>
<p style={{ margin: 0, color: '#BFC3E2', fontSize: '0.78rem', letterSpacing: '0.32em', textTransform: 'uppercase', fontWeight: 600 }}>
In Tech We Innovate · In Trust We Lead
@ -309,7 +309,7 @@ export function Hero3DRobotics() {
}}
/>
{/* Robot image main */}
{/* Robot image main */}
{FEATURED_ROBOTS.map((r, idx) => (
<div
key={r.id}
@ -416,7 +416,7 @@ export function Hero3DRobotics() {
}}
/>
{/* Edge glass glare silver/blue rim light */}
{/* Edge glass glare silver/blue rim light */}
<div
aria-hidden
style={{
@ -444,7 +444,7 @@ export function Hero3DRobotics() {
}}
/>
{/* Floating labels keyed to active robot + parallax */}
{/* Floating labels keyed to active robot + parallax */}
<div className="hero-labels" aria-hidden>
{labels.map((l, i) => {
/* labels drift opposite to tilt for parallax depth */

View File

@ -4,7 +4,7 @@ const STEPS = [
{
n: '01',
title: 'Discover',
body: 'Tell us about your venue and use case. We recommend a brand and model humanoid, quadruped, service, delivery, cleaning, or commercial.',
body: 'Tell us about your venue and use case. We recommend a brand and model humanoid, quadruped, service, delivery, cleaning, or commercial.',
accent: '#DEE0F0',
},
{
@ -16,7 +16,7 @@ const STEPS = [
{
n: '03',
title: 'Deploy & support',
body: 'We handle procurement, setup, training, and ongoing service across the UAE so your team can run, not maintain.',
body: 'We handle procurement, setup, training, and ongoing service across the UAE so your team can run, not maintain.',
accent: '#8891C7',
},
];

View File

@ -85,7 +85,7 @@ export function InquiryForm({ defaultRobot = '', defaultCategory = '', intent =
<form onSubmit={onSubmit} style={{ display: 'grid', gap: '1rem' }}>
{status === 'success' && (
<div style={{ padding: '0.875rem 1rem', borderRadius: 12, background: 'rgba(34,197,94,0.1)', border: '1px solid rgba(34,197,94,0.3)', color: '#86efac' }}>
Thank you your message was sent. Our team will reach out shortly.
Thank you your message was sent. Our team will reach out shortly.
</div>
)}
{status === 'error' && (

View File

@ -20,7 +20,7 @@ const SERVICES: Service[] = [
},
{
title: 'Collaborative & Flexible Robotics Integration',
body: 'Integration of robotic systems into existing workflows collaborative, flexible, business-friendly.',
body: 'Integration of robotic systems into existing workflows collaborative, flexible, business-friendly.',
icon: 'M12 2v6m0 8v6M4.93 4.93l4.24 4.24m5.66 5.66 4.24 4.24M2 12h6m8 0h6M4.93 19.07l4.24-4.24m5.66-5.66 4.24-4.24',
},
{

View File

@ -3,25 +3,25 @@
const REASONS = [
{
title: 'Exclusive UAE portfolio',
body: 'Selected Unitree and Pudu Robotics solutions available exclusively in the UAE through YS Lootah Robotics for hospitality, security, education, and industry.',
body: 'Selected Unitree and Pudu Robotics solutions available exclusively in the UAE through YS Lootah Robotics for hospitality, security, education, and industry.',
accent: '#DEE0F0',
icon: 'M3 3h7v7H3V3Zm0 11h7v7H3v-7Zm11-11h7v7h-7V3Zm0 11h7v7h-7v-7Z',
},
{
title: 'Dubai sales and support',
body: 'A local Dubai team manages your inquiry, demo, deployment, and on-the-ground support not just a website with a contact form.',
body: 'A local Dubai team manages your inquiry, demo, deployment, and on-the-ground support not just a website with a contact form.',
accent: '#273F94',
icon: 'M12 2 2 7v6c0 5 4 9 10 11 6-2 10-6 10-11V7l-10-5Z',
},
{
title: 'Custom configuration',
body: 'Configure persona, attire, and accessories for humanoid robots visualize your deployment before you order with our 3D configurator.',
body: 'Configure persona, attire, and accessories for humanoid robots visualize your deployment before you order with our 3D configurator.',
accent: '#8891C7',
icon: 'M12 4a4 4 0 0 1 4 4v1h5v6h-5v1a4 4 0 0 1-8 0v-1H3V9h5V8a4 4 0 0 1 4-4Z',
},
{
title: 'End-to-end deployment',
body: 'From procurement and configuration to delivery, installation, training, and ongoing support we handle the full lifecycle.',
body: 'From procurement and configuration to delivery, installation, training, and ongoing support we handle the full lifecycle.',
accent: '#DEE0F0',
icon: 'M3 12h4l3-7 4 14 3-7h4',
},

View File

@ -80,7 +80,7 @@ export function RoboticsScrollShowcase() {
function ConsoleInterior() {
return (
<div className="relative h-full w-full">
{/* left sidebar category rail */}
{/* left sidebar category rail */}
<aside className="hidden h-full w-44 flex-col gap-1.5 border-r border-[#DEE0F0]/10 bg-[#0a0a0c]/60 p-3 md:flex md:absolute md:inset-y-0 md:left-0">
<div className="px-2 py-2 text-[9px] font-semibold uppercase tracking-[0.28em] text-[#8891C7]">
Categories

View File

@ -121,7 +121,7 @@ export function RoboticsSplineShowcase() {
color: 'rgba(222,224,240,0.82)',
}}
>
Browse selected Unitree and Pudu Robotics solutions available exclusively in the UAE through YS Lootah Robotics humanoid, quadruped, service, delivery, and cleaning robots ready for Dubai businesses.
Browse selected Unitree and Pudu Robotics solutions available exclusively in the UAE through YS Lootah Robotics humanoid, quadruped, service, delivery, and cleaning robots ready for Dubai businesses.
</p>
<div
@ -319,7 +319,7 @@ export function RoboticsSplineShowcase() {
<SplineScene scene={SPLINE_SCENE} className="relative z-[2] h-full w-full" />
<span className="sr-only">
Interactive 3D scene placeholder Spline reference robot for development. Replace
Interactive 3D scene placeholder Spline reference robot for development. Replace
with approved YS Lootah Robotics custom Spline scene when available.
</span>
</div>

View File

@ -47,6 +47,7 @@ export function MotionSection({ children, className = '', id, delay = 0, style }
opacity: visible ? 1 : 0,
transform: visible ? 'translateY(0)' : 'translateY(28px)',
transition: `opacity 0.9s cubic-bezier(0.16,1,0.3,1) ${delay}s, transform 0.9s cubic-bezier(0.16,1,0.3,1) ${delay}s`,
scrollMarginTop: '6rem',
...style,
}}
>

View File

@ -43,15 +43,15 @@ const GOLD_BRONZE = '#8891C7';
export const BRANDS: Record<RobotBrand, { name: string; tagline: string; description: string; url: string; accent: string }> = {
unitree: {
name: 'Unitree Robotics',
tagline: 'Quadrupeds and humanoids available exclusively in the UAE through YS Lootah Robotics.',
tagline: 'Quadrupeds and humanoids available exclusively in the UAE through YS Lootah Robotics.',
description:
'High-performance bipedal and four-legged robots engineered for mobility, perception, and real-world deployment. Selected Unitree solutions are available exclusively in the UAE through YS Lootah Robotics with on-the-ground sales, demo, and deployment support in Dubai.',
'High-performance bipedal and four-legged robots engineered for mobility, perception, and real-world deployment. Selected Unitree solutions are available exclusively in the UAE through YS Lootah Robotics with on-the-ground sales, demo, and deployment support in Dubai.',
url: 'https://www.unitree.com/',
accent: GOLD_CHAMPAGNE,
},
pudu: {
name: 'Pudu Robotics',
tagline: 'Service, delivery, and cleaning robotics available exclusively in the UAE through YS Lootah Robotics.',
tagline: 'Service, delivery, and cleaning robotics available exclusively in the UAE through YS Lootah Robotics.',
description:
'A global leader in commercial service robotics. Selected Pudu Robotics solutions are available exclusively in the UAE through YS Lootah Robotics for restaurants, hotels, malls, hospitals, and offices.',
url: 'https://www.pudurobotics.com/',
@ -84,7 +84,7 @@ export const ROBOTS: Robot[] = [
category: 'humanoid',
categories: ['humanoid'],
shortDescription:
'A research-grade humanoid with agile locomotion, dexterous hands, and a developer-friendly platform available exclusively in the UAE through YS Lootah Robotics.',
'A research-grade humanoid with agile locomotion, dexterous hands, and a developer-friendly platform available exclusively in the UAE through YS Lootah Robotics.',
longDescription:
'The G1 is a next-generation bipedal humanoid designed for AI research, education, and real-world deployment. It can walk, run, balance, and manipulate objects. Through our exclusive UAE access, YS Lootah Robotics delivers fully tailored persona configurations and on-the-ground deployment support across Dubai and the UAE.',
features: [
@ -92,7 +92,7 @@ export const ROBOTS: Robot[] = [
'Dynamic balance and high-mobility locomotion',
'Dexterous hand options for manipulation research',
'Open developer SDK for custom behaviors',
'Configurable persona kandura, suit, vest, doctor, security guard',
'Configurable persona kandura, suit, vest, doctor, security guard',
],
useCases: [
'Robotics R&D and AI training labs',
@ -123,9 +123,9 @@ export const ROBOTS: Robot[] = [
category: 'humanoid',
categories: ['humanoid'],
shortDescription:
'Full-size bipedal humanoid engineered for athletic motion, heavy-duty research, and demanding deployment scenarios available exclusively in the UAE through YS Lootah Robotics.',
'Full-size bipedal humanoid engineered for athletic motion, heavy-duty research, and demanding deployment scenarios available exclusively in the UAE through YS Lootah Robotics.',
longDescription:
'The Unitree H2 is built for organizations that need a robust, full-scale humanoid combining powerful actuators with high-bandwidth perception. Ideal for advanced labs and industrial pilots across the UAE, with full local support from YS Lootah Robotics.',
'The Unitree H2 is built for organizations that need a robust, full-scale humanoid combining powerful actuators with high-bandwidth perception. Ideal for advanced labs and industrial pilots across the UAE, with full local support from YS Lootah Robotics.',
features: [
'Full adult-scale humanoid frame',
'High-torque actuators for dynamic motion',
@ -153,7 +153,7 @@ export const ROBOTS: Robot[] = [
category: 'humanoid',
categories: ['humanoid'],
shortDescription:
'A purpose-built humanoid that opens advanced robotics to startups, educators, and innovators available exclusively in the UAE through YS Lootah Robotics.',
'A purpose-built humanoid that opens advanced robotics to startups, educators, and innovators available exclusively in the UAE through YS Lootah Robotics.',
longDescription:
'Engineered for accessibility without compromising capability, the R1 brings advanced humanoid robotics within reach of startups, educators, and innovators across the UAE.',
features: ['Compact form factor', 'Mobility-focused design', 'Developer-friendly stack', 'Open integration paths'],
@ -174,7 +174,7 @@ export const ROBOTS: Robot[] = [
category: 'quadruped',
categories: ['quadruped', 'commercial', 'inspection'],
shortDescription:
'A versatile quadruped platform with high-bandwidth perception available exclusively in the UAE through YS Lootah Robotics for inspection, security, and mobile AI deployments.',
'A versatile quadruped platform with high-bandwidth perception available exclusively in the UAE through YS Lootah Robotics for inspection, security, and mobile AI deployments.',
longDescription:
'Unitree Go2 is a compact quadruped designed for mobility-first applications. With a powerful AI stack, LiDAR options, and developer SDK, it is the most widely deployed legged robot in commercial robotics worldwide.',
features: [
@ -210,7 +210,7 @@ export const ROBOTS: Robot[] = [
category: 'quadruped',
categories: ['quadruped', 'commercial', 'inspection'],
shortDescription:
'A rugged industrial-grade quadruped available exclusively in the UAE through YS Lootah Robotics for energy, utilities, and infrastructure operators.',
'A rugged industrial-grade quadruped available exclusively in the UAE through YS Lootah Robotics for energy, utilities, and infrastructure operators.',
longDescription:
'The Unitree B2 brings industrial reliability to legged robotics. With higher payload capacity and IP-rated build quality, it suits energy, utilities, and infrastructure operators across the UAE.',
features: [
@ -245,9 +245,9 @@ export const ROBOTS: Robot[] = [
category: 'quadruped',
categories: ['quadruped', 'inspection'],
shortDescription:
'A balanced quadruped platform available exclusively in the UAE through YS Lootah Robotics for organizations that need a step up in capability.',
'A balanced quadruped platform available exclusively in the UAE through YS Lootah Robotics for organizations that need a step up in capability.',
longDescription:
'The A2 sits between Go2 and B2 combining longer endurance and a richer sensor payload with a manageable size for UAE deployments.',
'The A2 sits between Go2 and B2 combining longer endurance and a richer sensor payload with a manageable size for UAE deployments.',
features: ['Extended autonomy', 'Sensor-rich payload bay', 'Robust outdoor capability'],
useCases: ['Site monitoring', 'Inspection at scale', 'Research deployments'],
specs: [SPEC_CONSULT, SPEC_PLACEHOLDER],
@ -266,9 +266,9 @@ export const ROBOTS: Robot[] = [
category: 'commercial',
categories: ['commercial', 'inspection'],
shortDescription:
'A perception-first robotics platform combining LiDAR, vision, and compute available exclusively in the UAE through YS Lootah Robotics for autonomous mobility research.',
'A perception-first robotics platform combining LiDAR, vision, and compute available exclusively in the UAE through YS Lootah Robotics for autonomous mobility research.',
longDescription:
'AS2 brings together the sensor and compute stack Unitree pairs with its robots useful for research teams building autonomy on top of legged or wheeled platforms.',
'AS2 brings together the sensor and compute stack Unitree pairs with its robots useful for research teams building autonomy on top of legged or wheeled platforms.',
features: ['LiDAR + vision fusion', 'On-board compute', 'Modular integration'],
useCases: ['Autonomy research', 'Inspection autonomy stacks', 'Mobile robotics labs'],
specs: [SPEC_CONSULT, SPEC_PLACEHOLDER],
@ -287,7 +287,7 @@ export const ROBOTS: Robot[] = [
category: 'delivery',
categories: ['delivery', 'hospitality', 'service'],
shortDescription:
'A multi-tray delivery robot with an expressive interface available exclusively in the UAE through YS Lootah Robotics for hospitality venues across Dubai.',
'A multi-tray delivery robot with an expressive interface available exclusively in the UAE through YS Lootah Robotics for hospitality venues across Dubai.',
longDescription:
'BellaBot delivers food, beverages, and goods with charm, efficiency, and remarkable safety performance. Deploy across restaurants, hotels, and venues in Dubai and the UAE with exclusive local support from YS Lootah Robotics.',
features: [
@ -324,9 +324,9 @@ export const ROBOTS: Robot[] = [
category: 'service',
categories: ['service', 'delivery', 'hospitality'],
shortDescription:
'A slim, billboard-equipped service robot available exclusively in the UAE through YS Lootah Robotics for high-traffic retail and dining spaces.',
'A slim, billboard-equipped service robot available exclusively in the UAE through YS Lootah Robotics for high-traffic retail and dining spaces.',
longDescription:
'KettyBot blends marketing and service. A large display lets venues advertise menus and promotions while the robot greets customers, guides them, and delivers orders perfect for high-traffic UAE retail and dining spaces.',
'KettyBot blends marketing and service. A large display lets venues advertise menus and promotions while the robot greets customers, guides them, and delivers orders perfect for high-traffic UAE retail and dining spaces.',
features: [
'Built-in advertising display',
'Compact footprint for narrow aisles',
@ -354,9 +354,9 @@ export const ROBOTS: Robot[] = [
category: 'cleaning',
categories: ['cleaning', 'commercial'],
shortDescription:
'An autonomous commercial cleaning platform available exclusively in the UAE through YS Lootah Robotics for malls, airports, hotels, and offices.',
'An autonomous commercial cleaning platform available exclusively in the UAE through YS Lootah Robotics for malls, airports, hotels, and offices.',
longDescription:
'CC1 is a multi-mode cleaning robot built for large commercial environments. Deploy across shopping malls, airports, hotels, and offices in the UAE fully supported by your local Pudu team.',
'CC1 is a multi-mode cleaning robot built for large commercial environments. Deploy across shopping malls, airports, hotels, and offices in the UAE fully supported by your local Pudu team.',
features: [
'Multi-mode cleaning: sweep, scrub, mop, vacuum',
'Autonomous mapping and routing',
@ -384,9 +384,9 @@ export const ROBOTS: Robot[] = [
category: 'delivery',
categories: ['delivery', 'commercial'],
shortDescription:
'Heavy-payload autonomous delivery robots available exclusively in the UAE through YS Lootah Robotics for warehouses, factories, and back-of-house operations.',
'Heavy-payload autonomous delivery robots available exclusively in the UAE through YS Lootah Robotics for warehouses, factories, and back-of-house operations.',
longDescription:
'The PUDU D-Series powers industrial delivery flows moving goods, parts, and trays across kitchens, hospitals, and warehouses with autonomous navigation and fleet coordination.',
'The PUDU D-Series powers industrial delivery flows moving goods, parts, and trays across kitchens, hospitals, and warehouses with autonomous navigation and fleet coordination.',
features: [
'High payload capacity',
'Multi-floor and elevator integration',

View File

@ -117,7 +117,7 @@
"features": {
"attire": {
"title": "أزياء قابلة للتخصيص",
"description": "ألبس روبوت G1 لأي مناسبة من الكندورة الإماراتية التقليدية إلى بدلات الأعمال وملابس السلامة الصناعية."
"description": "ألبس روبوت G1 لأي مناسبة من الكندورة الإماراتية التقليدية إلى بدلات الأعمال وملابس السلامة الصناعية."
},
"mobility": {
"title": "حركة متقدمة",

View File

@ -117,7 +117,7 @@
"features": {
"attire": {
"title": "Customizable Attire",
"description": "Dress your G1 for any occasion from traditional Emarati Kandura to professional business attire and industrial safety gear."
"description": "Dress your G1 for any occasion from traditional Emarati Kandura to professional business attire and industrial safety gear."
},
"mobility": {
"title": "Advanced Mobility",

View File

@ -124,7 +124,7 @@ export const personaStore = createStore<PersonaStore>((set, get) => ({
},
hydrate: () => {
// Guard: only hydrate once prevents race condition duplicates when
// Guard: only hydrate once prevents race condition duplicates when
// called from multiple components at the same time.
if (get().isHydrated) return;
set({ isHydrated: true });
@ -195,7 +195,7 @@ export const personaStore = createStore<PersonaStore>((set, get) => ({
saveToStorage(get().personas);
}
})
.catch(() => {}); // silent use local data as fallback
.catch(() => {}); // silent use local data as fallback
}
},
}));

View File

@ -151,7 +151,7 @@ export const pricingStore = createStore<PricingStore>((set, get) => ({
set({ items: merged });
}
})
.catch(() => {}); // silent use local data as fallback
.catch(() => {}); // silent use local data as fallback
}
},
}));