/* Studio 76 — The Imaginarium (standalone). Dream a piece → rendered in solid gold.
Beyond the edit box: Surprise me, a muse builder, and birthstone designs.
Talks only to imaginarium.php (brief → hero → wireframe → metal variants). */
const MOODS = ['Celestial','Midnight','Monsoon','Golden hour','Oceanic','Ember','Botanical','Gothic romance','New-age minimal','Maximalist'];
const ERAS = ['Art Deco','Victorian','Mughal','1970s','Y2K','Ancient Rome','Belle Époque','Future-organic'];
const GEMS = ['Emerald','Ruby','Sapphire','Diamond','Pearl','Opal','Moonstone','Black diamond','Pink tourmaline','Tanzanite'];
const FORMS = ['Ring','Necklace','Earrings','Cuff','Pendant','Anklet','Brooch','Tiara'];
const OCCASIONS = ['an engagement','an anniversary','a self-gift','an everyday heirloom','the red carpet','a wedding'];
const MONTHS = [
['Jan','Garnet'],['Feb','Amethyst'],['Mar','Aquamarine'],['Apr','Diamond'],
['May','Emerald'],['Jun','Moonstone'],['Jul','Ruby'],['Aug','Peridot'],
['Sep','Sapphire'],['Oct','Opal'],['Nov','Citrine'],['Dec','Tanzanite'],
];
/* curated, gorgeous seeds for Surprise me */
const DREAMS = [
'A celestial diamond choker scattered like a constellation across the collarbone',
'An Art Deco emerald cocktail ring, stepped and architectural, on a slim band',
'A monsoon-grey moonstone pendant that glows like rain on glass',
'Twin serpent ear-climbers in brushed gold with tiny ruby eyes',
'A Mughal-inspired polki necklace reimagined fine, delicate and barely-there',
'A single pear-cut sapphire suspended on a thread-thin chain, floating at the throat',
'A molten gold cuff that looks poured rather than made, holding one raw emerald',
'Stacking rings like the phases of the moon — pavé to bare polished gold',
'A Belle Époque garland melted into a delicate everyday tiara of white gold',
'Black diamond studs ringed in a whisper of white-gold thorns',
'A botanical vine bracelet, gold leaves curling around a line of tsavorites',
'An ember-orange spessartite solitaire in a fine six-prong, warm as candlelight',
];
const EXAMPLES = [
'A fine rose-gold pendant with a single pearl — modern and minimal',
'Delicate diamond ear-climbers, contemporary, barely-there',
'A slim stacking ring with a tiny emerald, new-age and clean',
];
const TONES = [
{ k:'original', label:'As designed' },
{ k:'yellow', label:'Yellow' },
{ k:'white', label:'White' },
{ k:'rose', label:'Rose' },
];
async function api(action, payload){
const r = await fetch('imaginarium.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(Object.assign({action},payload))});
let j={}; try{ j=await r.json(); }catch(e){}
if(!r.ok || j.error) throw new Error(j.error || ('Something went wrong ('+r.status+')'));
return j;
}
function pick(a){ return a[Math.floor(Math.random()*a.length)]; }
const isUrl = x => typeof x === 'string' && x !== 'loading';
function Imaginarium(){
const [tab, setTab] = React.useState('muse'); // muse | stars | words
const [muse, setMuse] = React.useState({ mood:null, era:null, gem:null, form:null, occ:null });
const [month, setMonth] = React.useState(null);
const [dream, setDream] = React.useState('');
const [phase, setPhase] = React.useState('idle'); // idle | thinking | result
const [concept, setConcept] = React.useState(null);
const [spec, setSpec] = React.useState(null);
const [hero, setHero] = React.useState(null);
const [variants, setVariants] = React.useState({});
const [wire, setWire] = React.useState(null);
const [active, setActive] = React.useState('original');
const [view, setView] = React.useState('render');
const [err, setErr] = React.useState('');
const heroRef = React.useRef(null);
function toggle(dim, val){ setMuse(m => Object.assign({}, m, { [dim]: m[dim] === val ? null : val })); }
function composeMuse(){
const m = muse;
var s = 'A ' + (m.form ? m.form.toLowerCase() : 'fine jewellery piece');
if (m.occ) s += ' for ' + m.occ;
var clauses = [];
if (m.mood) clauses.push(m.mood.toLowerCase() + ' in feeling');
if (m.era) clauses.push('inspired by ' + m.era);
if (m.gem) clauses.push('centred on ' + m.gem.toLowerCase());
if (clauses.length) s += ', ' + clauses.join(', ');
s += '. New-age, fine and wearable — made to be cast in solid gold.';
return s;
}
const museHasAny = muse.mood || muse.era || muse.gem || muse.form || muse.occ;
async function imagine(text){
const d = (text || '').trim();
if (d.length < 4){ setErr('Tell us a little more about the piece.'); return; }
setErr(''); setPhase('thinking');
setConcept(null); setSpec(null); setHero(null); heroRef.current = null;
setVariants({}); setWire(null); setActive('original'); setView('render');
try{
const b = await api('brief', { dream: d });
setConcept(b.concept); setSpec(b.spec); setPhase('result');
const h = await api('hero', { spec: b.spec });
heroRef.current = h; setHero(h); setVariants({ original: h.dataUrl });
// Blueprint + metal variants are generated ON DEMAND (when tapped) — no wasted renders.
}catch(e){ setErr(e.message || 'Could not imagine that — try again.'); setPhase('idle'); }
}
// on-demand: blueprint
function genWire(){
setView('blueprint');
if (wire || !heroRef.current) return;
setWire('loading');
api('wireframe', { id: heroRef.current.id }).then(w => setWire(w.dataUrl)).catch(() => setWire(null));
}
// on-demand: a metal variant (original is already the hero)
function genVariant(tone){
setActive(tone); setView('render');
if (tone === 'original' || variants[tone] || !heroRef.current) return;
setVariants(v => Object.assign({}, v, { [tone]: 'loading' }));
api('variant', { id: heroRef.current.id, tone })
.then(r => setVariants(v => Object.assign({}, v, { [tone]: r.dataUrl })))
.catch(() => setVariants(v => { const n = Object.assign({}, v); delete n[tone]; return n; }));
}
function surprise(){
// 70% a curated gem of a dream, 30% a freshly-rolled muse
if (Math.random() < 0.7){ imagine(pick(DREAMS)); return; }
const m = { form: pick(FORMS), gem: pick(GEMS), mood: pick(MOODS), era: pick(ERAS), occ: null };
setMuse(m); setTab('muse');
imagine('A ' + m.form.toLowerCase() + ', ' + m.mood.toLowerCase() + ' in feeling, inspired by ' + m.era + ', centred on ' + m.gem.toLowerCase() + '. New-age, fine and wearable — made to be cast in solid gold.');
}
function imagineBirthstone(){
const mo = MONTHS[month];
if (!mo) return;
imagine('A fine, contemporary piece designed around a ' + mo[1].toLowerCase() + ' — the birthstone of ' + ({Jan:'January',Feb:'February',Mar:'March',Apr:'April',May:'May',Jun:'June',Jul:'July',Aug:'August',Sep:'September',Oct:'October',Nov:'November',Dec:'December'})[mo[0]] + '. Let the stone lead, set in solid gold, new-age and wearable every day.');
}
const fmtStone = s => s ? [s.carat_estimate, s.cut, s.color, s.type].filter(Boolean).join(' ') : '';
const fmtAcc = a => (a && a.length) ? a.map(x => typeof x === 'string' ? x : [x.cut, x.color, x.type].filter(Boolean).join(' ')).filter(Boolean).join(', ') : '';
const mainSrc = view === 'blueprint' ? (isUrl(wire) ? wire : null) : (isUrl(variants[active]) ? variants[active] : (hero && hero.dataUrl));
const stageMsg = !concept ? 'Composing your concept…' : (!hero ? 'Rendering in solid gold…' : (view === 'blueprint' ? 'Drafting the blueprint…' : 'Rendering in solid gold…'));
const Row = ({l,v}) => v ?
Describe a piece you’ve always wanted — or let a muse spark it — and watch it render in solid gold, in seconds. No photo needed; just your imagination.
or spark your own
{tab === 'muse' && (
The form
{FORMS.map(f => )}
A feeling
{MOODS.map(f => )}
An era
{ERAS.map(f => )}
The stone
{GEMS.map(f => )}
For…
{OCCASIONS.map(f => )}
{museHasAny &&
Your muse: {composeMuse()}
}
{museHasAny && }
)}
{tab === 'stars' && (
Pick a birth month — we’ll design around its stone