/* bespoke.jsx — five-step bespoke funnel */
const Bespoke = ({onConsult, setRoute}) => {
const [step, setStep] = React.useState(0);
const [data, setData] = React.useState({
category: '',
occasion: '',
references: [],
stoneType: '',
stoneShape: '',
stoneColor: '',
caratBand: '',
metal: '',
setting: '',
budget: '',
timing: '',
notes: '',
});
const set = (k, v) => setData((d) => ({...d, [k]: v}));
const toggleRef = (k) => setData((d) => {
const has = d.references.includes(k);
return {...d, references: has ? d.references.filter(x => x !== k) : [...d.references, k]};
});
const steps = [
'The Piece',
'The Stone',
'Metal & Setting',
'Brief & Budget',
'Review',
];
return (
{steps.map((s, i) => (
i < step && setStep(i)}>
{String(i + 1).padStart(2, '0')}
{s}
))}
{step === 0 &&
}
{step === 1 &&
}
{step === 2 &&
}
{step === 3 &&
}
{step === 4 &&
}
setStep(step - 1)} style={{opacity: step === 0 ? 0.3 : 1}}>
← Back
Step {step + 1} of {steps.length}
{step < steps.length - 1 ? (
setStep(step + 1)}>Continue
) : (
Send to Atelier
)}
);
};
// ===========================================================
// Bespoke Hero
// ===========================================================
const BespokeHero = () => (
Bespoke · Made for you
Begin a one-of-one .
Five short questions, then a consultant. Bring whatever you have — a sketch, a screenshot, an heirloom, a stone — and we'll meet it with a brief, three 3D-CAD renders, and a four-to-six-week atelier window.
);
// ===========================================================
// Step 1 — The Piece
// ===========================================================
const StepPiece = ({data, set, toggleRef}) => {
const categories = [
{id: 'engagement', name: 'Engagement ring', sub: 'Solitaire, halo, three-stone, cluster'},
{id: 'band', name: 'Wedding band', sub: 'Eternity, half-eternity, plain, mixed'},
{id: 'cocktail', name: 'Cocktail ring', sub: 'Statement, gemstone, novelty'},
{id: 'earrings', name: 'Earrings', sub: 'Studs, drops, hoops, climbers'},
{id: 'necklace', name: 'Necklace / Pendant', sub: 'Solitaire, station, heirloom motif'},
{id: 'bracelet', name: 'Bracelet / Bangle', sub: 'Tennis, link, cuff, eternity'},
{id: 'rework', name: 'Heirloom rework', sub: 'Bring an existing piece to begin'},
{id: 'other', name: 'Something else', sub: 'Brooch, anklet, pinky, charm — anything'},
];
const occasions = ['Engagement', 'Wedding', 'Anniversary', 'Birthday', 'Push present', 'Self-gift', 'Gift to a friend'];
const refs = ['aureveris', 'velantrix', 'magnolia', 'prismara', 'celeste', 'imperial-eagle'];
return (
What are we making?
One brief sentence is enough. We'll pull the rest out of you over coffee.
Category
{categories.map((c) => (
set('category', c.id)}
style={{padding: '18px 20px', textAlign: 'left', display: 'block'}}>
{c.name}
{c.sub}
))}
For what occasion?
{occasions.map((o) => (
set('occasion', o)}>{o}
))}
Reference pieces — pick a few (optional)
{refs.map((id) => {
const p = byId(id);
const selected = data.references.includes(id);
return (
toggleRef(id)} style={{
position: 'relative',
aspectRatio: '4/5',
padding: 0,
border: `1px solid ${selected ? 'var(--palace-gold)' : 'var(--ink-20)'}`,
background: 'var(--card-bg)',
cursor: 'pointer',
overflow: 'hidden',
outline: selected ? '2px solid var(--palace-gold)' : 'none',
outlineOffset: -3,
}}>
{p.name}
{selected && (
)}
);
})}
);
};
// ===========================================================
// Step 2 — The Stone
// ===========================================================
const StepStone = ({data, set}) => {
const stones = [
{id: 'diamond', name: 'Diamond', sub: 'Brilliant, emerald, oval, pear, marquise, princess'},
{id: 'sapphire', name: 'Sapphire', sub: 'Blue, pink, yellow, multi — most worn at the house'},
{id: 'emerald', name: 'Emerald', sub: 'Colombian & Zambian'},
{id: 'ruby', name: 'Ruby', sub: 'Burmese, pigeon-blood, oval & cushion'},
{id: 'spinel', name: 'Spinel', sub: 'Red, blue, cobalt — collectors\' choice'},
{id: 'mixed', name: 'A mix', sub: 'Halo of one, centre of another'},
{id: 'own', name: 'I have my own stone', sub: 'Bring it — we\'ll certify it'},
{id: 'open', name: 'Advise me', sub: 'Show me three options'},
];
const shapes = ['Round', 'Oval', 'Cushion', 'Emerald', 'Pear', 'Marquise', 'Princess', 'Heart', 'Asscher'];
const colors = ['Colourless / D–F', 'Near colourless / G–J', 'Blue sapphire', 'Pink sapphire', 'Yellow sapphire', 'Emerald green', 'Ruby red', 'Multi / rainbow'];
const carats = ['Up to 0.5ct', '0.5 – 1ct', '1 – 2ct', '2 – 3ct', '3 – 5ct', '5ct +', 'Advise me'];
return (
The stone, first.
The piece is drawn around it.
Stone type
{stones.map((s) => (
set('stoneType', s.id)} style={{padding: '16px 20px', textAlign: 'left', display: 'block'}}>
{s.name}
{s.sub}
))}
Shape
{shapes.map((s) => (
set('stoneShape', s)}>{s}
))}
Colour / hue
{colors.map((c) => (
set('stoneColor', c)}>{c}
))}
Carat band
{carats.map((c) => (
set('caratBand', c)}>{c}
))}
);
};
// ===========================================================
// Step 3 — Metal & Setting
// ===========================================================
const StepMetal = ({data, set}) => {
const metals = [
{id: 'yellow', name: '18k Yellow Gold', sub: 'Warm, traditional, holds colour in stones'},
{id: 'rose', name: '18k Rose Gold', sub: 'Pink-warm, complements diamond & morganite'},
{id: 'white', name: '18k White Gold', sub: 'Cool-grey, mainstream bridal'},
{id: 'platinum', name: 'Platinum 950', sub: 'Heaviest, hypoallergenic, lifetime'},
{id: 'mixed', name: 'Two-tone', sub: 'White centre, rose or yellow shank'},
];
const settings = [
{id: 'solitaire', name: 'Solitaire', d: 'Single stone, prong or bezel.'},
{id: 'halo', name: 'Halo', d: 'Pavé around centre stone.'},
{id: 'three', name: 'Three-stone', d: 'Centre flanked by accents.'},
{id: 'cluster', name: 'Cluster', d: 'Stones grouped as a single jewel.'},
{id: 'eternity', name: 'Eternity', d: 'Full circle of stones.'},
{id: 'bezel', name: 'Bezel / Tension', d: 'Modern, low-profile.'},
];
return (
Metal & setting.
If you're unsure, leave it to the bench.
Metal
{metals.map((m) => (
set('metal', m.id)} style={{padding: '16px 20px', textAlign: 'left', display: 'block', alignItems: 'center'}}>
{m.name}
{m.sub}
))}
Setting
{settings.map((s) => (
set('setting', s.id)} style={{padding: '20px 16px', textAlign: 'center', display: 'block'}}>
{s.name}
{s.d}
))}
);
};
const metalSwatch = (id) => ({
yellow: 'linear-gradient(135deg, #E8C56E, #B8923A)',
rose: 'linear-gradient(135deg, #E5B5A1, #BF8870)',
white: 'linear-gradient(135deg, #E8E8EE, #B8B8C2)',
platinum: 'linear-gradient(135deg, #DFE2E6, #9CA3AA)',
mixed: 'linear-gradient(90deg, #E8C56E 0% 33%, #E8E8EE 33% 66%, #E5B5A1 66% 100%)',
})[id];
const SettingDiagram = ({type, selected}) => {
const c = selected ? 'var(--palace-gold)' : 'var(--ink-40)';
return (
{type === 'solitaire' && (
)}
{type === 'halo' && (
)}
{type === 'three' && (
)}
{type === 'cluster' && (
{[[18,10],[26,10],[22,16],[18,22],[26,22]].map(([x,y], i) => )}
)}
{type === 'eternity' && (
{[6, 12, 18, 22, 26, 32, 38].map((x, i) => )}
)}
{type === 'bezel' && (
)}
);
};
// ===========================================================
// Step 4 — Brief & Budget
// ===========================================================
const StepBrief = ({data, set}) => {
const budgets = [
{id: '9-20', name: 'AED 9k – 20k', sub: 'A polished, considered first piece'},
{id: '20-50', name: 'AED 20k – 50k', sub: 'Signature & most bridal'},
{id: '50-100', name: 'AED 50k – 100k', sub: 'Centre stone, statement work'},
{id: '100+', name: 'AED 100k +', sub: 'Couture and high-jewellery'},
{id: 'open', name: 'Open / advise me', sub: 'Three options across bands'},
];
const timings = ['4–6 weeks (standard)', '6–10 weeks (more intricate)', 'I have a deadline', 'No rush'];
return (
The brief.
Budget bands and timing — both flexible, both worth saying out loud.
Comfortable budget
{budgets.map((b) => (
set('budget', b.id)} style={{padding: '14px 18px', textAlign: 'left', display: 'block'}}>
{b.name}
{b.sub}
))}
Timing
{timings.map((t) => (
set('timing', t)}>{t}
))}
A note for the bench
Bringing your own stone?
We'll certify it through GIA-equivalent labs, photograph it, and recommend three settings around it. No charge if you don't proceed.
);
};
// ===========================================================
// Step 5 — Review
// ===========================================================
const StepReview = ({data, setStep}) => {
const summary = [
{label: 'Category', value: data.category || '—', editStep: 0},
{label: 'Occasion', value: data.occasion || '—', editStep: 0},
{label: 'References', value: data.references.length ? data.references.map(id => byId(id)?.name).join(' · ') : 'None selected', editStep: 0},
{label: 'Stone', value: [data.stoneType, data.stoneShape, data.stoneColor, data.caratBand].filter(Boolean).join(' · ') || '—', editStep: 1},
{label: 'Metal & setting', value: [data.metal, data.setting].filter(Boolean).join(' · ') || '—', editStep: 2},
{label: 'Budget', value: data.budget || '—', editStep: 3},
{label: 'Timing', value: data.timing || '—', editStep: 3},
{label: 'Note', value: data.notes || '—', editStep: 3},
];
return (
Looks good?
A consultant will read this, pull stones, draft three 3D-CAD renders, and email you within 48 hours.
{summary.map((s) => (
{s.label}
{s.value}
setStep(s.editStep)}>Edit
))}
);
};
// ===========================================================
// Sidebar
// ===========================================================
const BespokeSidebar = ({data}) => {
return (
);
};
const PreviewArt = ({data}) => {
// Render a small "moodboard" representation
const refImgs = data.references.slice(0, 4).map(id => byId(id)?.img).filter(Boolean);
if (refImgs.length === 0) {
return (
Your moodboard
Pick references and a stone — we'll start painting it in.
);
}
return (
1 ? '1fr 1fr' : '1fr', gridTemplateRows: refImgs.length > 2 ? '1fr 1fr' : '1fr', gap: 2}}>
{refImgs.map((src, i) => (
))}
);
};
const PreviewRow = ({label, value}) => (
{label}
{value || '—'}
);
// ===========================================================
// Process detail (after stepper)
// ===========================================================
const BespokeProcess = () => (
From brief to hallmark .>}
sub="Roughly four to six weeks, three weekly check-ins, and one piece that didn't exist when the month began."
/>
{[
{n: '01', t: 'Brief', sub: 'Week 0', d: 'Private consult — references, stone, budget, sketch.'},
{n: '02', t: 'Stone curation', sub: 'Week 1', d: 'Three stones pulled, certified, brought to the table.'},
{n: '03', t: 'CAD renders', sub: 'Week 2', d: 'Three 3D-CAD renders. Refine to one signed-off design.'},
{n: '04', t: 'Atelier', sub: 'Weeks 3–5', d: 'Wax, cast, set, polish. Weekly photos from the bench.'},
{n: '05', t: 'Reveal', sub: 'Week 6', d: 'Hallmarked, certified, photographed — and yours.'},
].map((p) => (
{p.n}
{p.sub}
{p.t}
{p.d}
))}
);
// ===========================================================
// Consultant intro
// ===========================================================
const ConsultantIntro = ({onConsult}) => (
Your consultant
Sat across the table — not behind glass.
Bespoke pieces are walked through by a senior consultant from start to hallmark. You see the stones loose, you see the wax model, you handle the cast before it's set. Three weekly photo updates from the bench in Bangkok, one final reveal in Deira.
);
Object.assign(window, { Bespoke });