/* product.jsx — product detail */
const Product = ({productId, setProductId, setRoute, onConsult, onPiecedConsult}) => {
const product = byId(productId) || byId('aureveris');
const [metal, setMetal] = React.useState(product.metal);
const [size, setSize] = React.useState('US 6');
const [tab, setTab] = React.useState('story');
const [open, setOpen] = React.useState({stone: true, care: false, ship: false});
React.useEffect(() => { setMetal(product.metal); }, [productId]);
const sizes = ['US 4', 'US 5', 'US 6', 'US 7', 'US 8', 'US 9'];
const metals = ['yellow', 'white', 'rose', 'platinum'];
return (
{/* breadcrumb */}
{/* gallery */}
{/* info */}
{product.tag &&
{product.tag} }
{product.name}
{product.desc || 'A signature Palaces piece, made to order in our atelier.'}
{fmtAED(product.price)}
incl. VAT · prices linked to gold spot
{/* metal swatches */}
Metal
{metalName(metal)}
{metals.map((m) => (
setMetal(m)} title={metalName(m)} style={{
width: 44, height: 44, padding: 0,
background: metalSwatchBg(m),
border: metal === m ? '2px solid var(--royal-night)' : '1px solid var(--ink-20)',
cursor: 'pointer',
borderRadius: '50%',
}} aria-label={metalName(m)} />
))}
{/* size */}
Ring size
Size guide
{sizes.map((s) => (
setSize(s)}>{s}
))}
Other
{/* CTAs */}
onPiecedConsult(product.id)}>
Consult about this piece
· Made to order · 4–6 weeks ·
{/* expanders */}
setOpen({...open, stone: !open.stone})}>
setOpen({...open, care: !open.care})}>
Cleaned, re-polished and rhodium-plated complimentary for life. Bring the piece to either showroom or post it to us insured. Steam clean every six months; remove for swimming and gym.
setOpen({...open, ship: !open.ship})}>
UAE: 5–7 working days, complimentary.
Worldwide: 7–16 days, insured, door-to-door.
7-day exchange on stock pieces · made-to-order pieces are final sale.
{/* trust strip */}
{[
{t: 'Made to order', s: 'Cast & set in our atelier'},
{t: 'Certified', s: 'Stones documented & signed off'},
{t: 'Hallmarked', s: '18k / 750 Bangkok hallmark'},
{t: 'Lifetime care', s: 'Complimentary at both showrooms'},
].map((i) => (
))}
{/* story */}
{/* you may also love */}
);
};
// ===========================================================
// Gallery (thumbnails)
// ===========================================================
const ProductGallery = ({product}) => {
const others = PRODUCTS.filter(p => p.cat === product.cat && p.id !== product.id).slice(0, 3);
const thumbs = [product.img, ...others.map(p => p.img)];
const [active, setActive] = React.useState(0);
React.useEffect(() => { setActive(0); }, [product.id]);
const main = thumbs[active] || product.img;
return (
{thumbs.map((t, i) => (
setActive(i)} style={{
aspectRatio: '1/1',
border: active === i ? '1px solid var(--palace-gold)' : '1px solid var(--ink-10)',
background: 'var(--champagne)',
padding: 0,
overflow: 'hidden',
cursor: 'pointer',
}}>
))}
);
};
// ===========================================================
// Expander
// ===========================================================
const Expander = ({title, open, toggle, children}) => (
{title}
{open ? : }
{open &&
{children}
}
);
const SpecGrid = ({product}) => {
const rows = [
{label: 'Centre stone', value: stoneFor(product)},
{label: 'Setting', value: 'Pavé halo, claw prongs'},
{label: 'Metal', value: `18k ${metalName(product.metal)} · 750 hallmark`},
{label: 'Total carats', value: '2.18 ct (centre 0.85, accents 1.33)'},
{label: 'Certification', value: 'IGI lab report with each piece'},
{label: 'Origin', value: 'Designed Dubai · Cast Bangkok · Finished Deira'},
];
return (
{rows.map((r) => (
{r.label}
{r.value}
))}
);
};
const stoneFor = (product) => {
const map = {
aureveris: 'Sapphire cluster · multi-colour',
velantrix: 'Emerald cut · 2.10 ct centre',
seraphelis: 'Round brilliant · 1.20 ct centre',
'eagle-ring': 'Solid 18k yellow gold · pavé halo',
'eagle-pendant': 'Solid 18k yellow gold · pavé halo',
'imperial-eagle': 'Solid 18k · ruby, emerald, sapphire pavé',
celeste: 'Eternity band · brilliant pavé',
twilight: 'Pink tourmaline & sapphire cluster',
midnight: 'Blue sapphire pear · diamond halo',
prismara: 'Multi-tourmaline butterfly · pavé wings',
magnolia: 'Diamond marquise drops',
honeycrest: 'Enamel & pavé hoops',
'celestial-leaf': 'Diamond marquise leaves',
skyhold: 'Sapphire solitaire · 0.55 ct',
'radiant-horizon': 'Diamond baguette & pavé',
rosabella: 'Pink sapphire & diamond cluster',
sunlure: 'Yellow citrine & diamond',
'ember-hoops': 'Garnet & peridot hoops',
trilight: 'Diamond marquise leaf drops',
};
return map[product.id] || 'Mixed gemstone & diamond';
};
const metalSwatchBg = (m) => ({
yellow: 'linear-gradient(135deg, #F4D78C, #C9A24B 60%, #9A7929)',
white: 'linear-gradient(135deg, #F1F2F4, #C3C6CC 60%, #95989E)',
rose: 'linear-gradient(135deg, #F0CAB8, #D69A85 60%, #A86F5A)',
platinum: 'linear-gradient(135deg, #E6E9ED, #B0B6BD 60%, #80878E)',
})[m];
// ===========================================================
// Story
// ===========================================================
const StorySection = ({product}) => (
The piece
Drawn around the stone.
{product.name} began on the bench as a sketch in graphite. The shank tapers from 1.8mm at the back to 2.4mm at the shoulders to throw light into the halo. The pavé is bead-set by hand, one stone every nine minutes — never machine-cast.
{[
{n: '01', t: 'Sketch'},
{n: '02', t: 'CAD render'},
{n: '03', t: 'Wax & cast'},
].map((s) => (
))}
);
// ===========================================================
// You may also love
// ===========================================================
const YouMayLove = ({currentId, setProductId}) => {
const others = PRODUCTS.filter(p => p.id !== currentId).slice(0, 4);
return (
Worn alongside. >}
sub="Pieces that travel well with this one — pulled by stone, by metal, by the consultant."
/>
{others.map((p) => (
{ setProductId(id); window.scrollTo(0, 0); }} />
))}
);
};
Object.assign(window, { Product });