/* Maison Silya — Homepage: "A Private Viewing". The homepage is a descent through a darkened gallery into the light, in five movements. A fixed chapter rail tracks the descent and adapts to each room. Pieces are presented as spotlit works floating in black. */ /* A piece presented as a lit work in a dark room: warm halo behind, key light across the surface, a soft contact shadow grounding it. */ function LitPlate({ tone = 'stone-300', label = 'The piece', ratio = '4 / 5', image }) { return (
{image &&
}
{!image && {label}}
); } function ChapterRail({ active, dark, show, onJump }) { const items = [['I', t('home.r1')], ['II', t('home.r2')], ['III', t('home.r3')], ['IV', t('home.r4')], ['V', t('home.r5')]]; const base = dark ? 'rgba(247,243,236,0.55)' : 'rgba(34,29,22,0.45)'; const on = dark ? C('ivory') : C('ink'); return ( ); } function HomeScreen({ onNav, onPiece }) { const pieces = [ { name: 'Monolith I', prov: t('p.prov.nepal'), detail: '300 × 400 cm · Wool & silk', edition: t('ed.8'), tone: 'stone-300', image: 'media/monolith-i.jpg', line: t('p.monolith.line') }, { name: 'Sediment II', prov: t('p.prov.marrakech'), detail: '250 × 350 cm · Undyed wool', tone: 'stone-200', image: 'media/sediment-ii.jpg', line: t('p.sediment.line') }, { name: 'Aurelian', prov: (window.__MS_LANG === 'fr' ? 'Tuftée main' : 'Hand-tufted'), detail: '260 × 360 cm · Laine & or', tone: 'champagne', image: 'media/aurelian.jpg', line: t('p.vein.line'), tufted: true }, ]; const roman = ['I', 'II', 'III']; const goTuftedSingulieres = () => { onNav('tufted'); setTimeout(() => { location.hash = 'tufted/singulieres'; }, 60); }; const openP = (p) => { if (p.tufted) goTuftedSingulieres(); else onPiece(msPiece(p.name, p)); }; const [active, setActive] = React.useState(1); const [dark, setDark] = React.useState(true); const [show, setShow] = React.useState(false); React.useEffect(() => { const sc = document.getElementById('ms-scroll'); if (!sc) return; const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; /* III "La Présentation" begins at ALBA (the first piece shown), not at Monolith I — otherwise ALBA was wrongly attributed to movement II and III only lit from the second piece on. ALBA + the three pieces are one viewing, so III spans them all. */ const anchors = [ ['mvt-2', 2, true], ['mvt-alba', 3, true], ['mvt-4', 4, false], ['mvt-5', 5, false] ]; let raf = 0; const run = () => { raf = 0; const vh = window.innerHeight || 800; const y = sc.scrollTop + vh * 0.45; let act = 1, dk = true; anchors.forEach(([id, n, d]) => { const el = document.getElementById(id); if (el && el.offsetTop <= y) { act = n; dk = d; } }); setActive(act); setDark(dk); setShow(sc.scrollTop > vh * 0.55); if (!reduce) { const vc = vh / 2; document.querySelectorAll('.ms-home [data-par]').forEach((el) => { const r = el.getBoundingClientRect(); const off = (r.top + r.height / 2 - vc) / vh; el.style.transform = `translateY(${off * 26}px)`; }); } }; const onScroll = () => { if (!raf) raf = requestAnimationFrame(run); }; sc.addEventListener('scroll', onScroll, { passive: true }); run(); return () => { sc.removeEventListener('scroll', onScroll); if (raf) cancelAnimationFrame(raf); }; }, []); const jump = (n) => { const sc = document.getElementById('ms-scroll'); if (!sc) return; if (n === 1) { sc.scrollTo({ top: 0, behavior: 'smooth' }); return; } const el = document.getElementById('mvt-' + n); if (el) sc.scrollTo({ top: el.offsetTop, behavior: 'smooth' }); }; return (
{/* I — The Unveiling */} {/* II — The Manifesto, in the dark */}
{t('home.manifest_k')}

{t('home.manifest1')}

{t('home.manifest2')}

{/* II·5 — ALBA: the first flagship piece */}
onPiece({ name: 'ALBA', prov: t('p.prov.nepal'), detail: '250 × 170 cm', edition: t('alba.edition'), tone: 'stone-800', image: 'media/alba.jpg', desc: t('alba.desc'), materials: t('home.alba_mat'), pile: t('alba.pile'), knots: '150 / in²' })} style={{ margin: 0, gridColumn: 1, gridRow: 1, cursor: 'pointer' }} title="ALBA">
{t('home.alba_k')}

ALBA

{t('home.alba_sub')}

{t('home.alba_body')}

{[[t('sp.dimensions'), '250 × 170 cm'], [t('sp.knots'), '150 / in²'], [t('sp.materials'), t('home.alba_mat')]].map(([k, v]) => (
{k}
{v}
))}
onPiece({ name: 'ALBA', prov: t('p.prov.nepal'), detail: '250 × 170 cm', edition: t('alba.edition'), tone: 'stone-800', image: 'media/alba.jpg', desc: t('alba.desc'), materials: t('home.alba_mat'), pile: t('alba.pile'), knots: '150 / in²' })}>{t('home.alba_cta')} {t('home.alba_status')}
{/* III — The Viewing: pieces as lit works in the dark */} {pieces.map((p, i) => { const right = i % 2 === 1; return (
openP(p)} style={{ margin: 0, gridColumn: right ? 2 : 1, gridRow: 1, cursor: 'pointer' }} title={p.name}>
{t('home.plate')} {roman[i]} {p.edition && · {p.edition}}

{p.name}

{p.prov} · {p.detail.split(' · ')[0]}

{p.line}

openP(p)}>{t('home.view_this')}
); })} {/* close of the viewing — quiet link onward */}
onNav('signature')}>{t('home.enter')}
{/* IV — The Threshold: the gallery brightens to day */}

{t('home.threshold')}

{/* V — The Invitation */}
{t('home.inv_k')}

{t('home.inv_title')}

{t('home.inv_body')}

onNav('contact')}>{t('home.cta_view2')}
{/* three doors into the house */}
{[[t('home.door1'), t('home.door1_d'), 'signature'], [t('home.door2'), t('home.door2_d'), 'bespoke'], [t('home.door3'), t('home.door3_d'), 'philosophy']].map(([dt, dd, r], i) => ( ))}
{t('home.provenance')}
); } Object.assign(window, { HomeScreen });