/* global React, ReactDOM */ const { useState, useEffect, useMemo } = React; /* ─────────── content (real data, sourced from brief) ─────────── */ // Twelve named bridal gowns (from the live bridal feed) const BRIDAL = [ { id: "SELENA", name: "Selena", price: 16500, img: "media/bridal-01-selena.webp", note: "Off-shoulder organza, sculpted bow tail", collection: "L'Amour en Fleurs" }, { id: "LEONIE", name: "Léonie", price: 16500, img: "media/bridal-02-leonie.webp", note: "Strapless sheath, appliqué floral veil", collection: "L'Amour en Fleurs" }, { id: "CELESTINE", name: "Céléstine", price: 22000, img: "media/bridal-03-celestine.webp", note: "Strapless A-line, three-dimensional bloom", collection: "Garden Tales" }, { id: "SOPHIA", name: "Sophia", price: 23000, img: "media/bridal-04-sophia.webp", note: "Strapless ball, magnolia embroidery", collection: "L'Amour Sucré" }, { id: "DALIA", name: "Dalia", price: 24000, img: "media/bridal-05-dalia.webp", note: "Hand-beaded bodice, voluminous veil", collection: "L'Amour Sucré" }, { id: "ZOE", name: "Zoé", price: 27000, img: "media/bridal-06-zoe.webp", note: "Sheer bateau lace, slim belt detail", collection: "Garden Tales" }, { id: "SOLENE", name: "Solène", price: 29000, img: "media/bridal-07-solene.webp", note: "Strapless pearl ball, sheer gloves", collection: "L'Amour en Fleurs" }, { id: "LUNA", name: "Luna", price: 46000, img: "media/bridal-08-luna.webp", note: "Off-shoulder beaded chiffon, full skirt", collection: "L'Amour en Fleurs" }, { id: "AYLA", name: "Ayla", price: 44000, img: "media/bridal-09-ayla.webp", note: "Strapless beaded couture, tulle train", collection: "Garden Tales" }, { id: "LILLY", name: "Lilly", price: 29000, img: "media/bridal-10-lilly.webp", note: "Long-sleeve micro-bead, structured A-line", collection: "L'Amour Sucré" }, { id: "GAIA", name: "Gaia", price: 28000, img: "media/bridal-11-gaia.webp", note: "Lace ballgown, off-shoulder detailing", collection: "L'Amour en Fleurs" }, { id: "AMBER", name: "Amber", price: 32000, img: "media/bridal-12-amber.webp", note: "Floral appliqué, cascading butterfly veil", collection: "Garden Tales" }, ]; // Eveningwear (from media manifest) const EVENING = [ { id: "EV1", name: "Strapless Hand-Beaded Gown", price: 16800, img: "media/ev-01-hand-beaded-shawl.webp", note: "With organza shawl, made to order" }, { id: "EV2", name: "Pleated Beaded-Flower Gown", price: 15600, img: "media/ev-02-pleated-flowers.webp", note: "Structured plissé, hand-beaded flora" }, { id: "EV3", name: "Mermaid Lace Gown", price: 10300, img: "media/ev-03-mermaid-lace.webp", note: "Strapless lace, bow-tail detail" }, { id: "EV4", name: "Linear Off-Shoulder Beaded", price: 17900, img: "media/ev-04-linear-beaded.webp", note: "Hand-stitched linear beadwork" }, { id: "EV5", name: "Halter Beaded Top & Skirt", price: 9900, img: "media/ev-05-halter-flared.webp", note: "Halter bodice, flared crepe skirt" }, { id: "EV6", name: "Heart-Shaped Beaded Gown", price: 15400, img: "media/ev-06-heart-beaded.webp", note: "Sweetheart neck, full bead constellation" }, { id: "EV7", name: "Off-Shoulder Plissé Tulle", price: 8400, img: "media/ev-07-plisse-tulle.webp", note: "Structured plissé, voluminous skirt" }, { id: "EV8", name: "Wave-Draped Cage Gown", price: 8800, img: "media/ev-08-cage-flowers.webp", note: "Hand-draped wave, three-dimensional flowers" }, ]; // 8 featured pieces for the curated grid — mix bridal + eveningwear const FEATURED = [ { src: BRIDAL[7], type: "BRIDAL" }, // Luna { src: EVENING[3], type: "EVENING" }, // Linear Off-Shoulder { src: BRIDAL[5], type: "BRIDAL" }, // Zoé { src: EVENING[1], type: "EVENING" }, // Pleated Flowers { src: BRIDAL[2], type: "BRIDAL" }, // Céléstine { src: EVENING[5], type: "EVENING" }, // Heart Beaded { src: BRIDAL[10], type: "BRIDAL" }, // Gaia { src: EVENING[0], type: "EVENING" }, // Hand-Beaded Shawl ]; const SHOWROOMS = [ { city: "Dubai", tag: "Evening Wear · d3", addr: "Dubai Design District\nBuilding 8, Showroom 310 · 3rd Floor", rows: [ ["Phone", "+971 58 508 7555"], ["Hours", "Mon–Fri 11:00 – 19:00\nSaturday 11:00 – 18:00", "flagged"], ["By", "Appointment only"], ], schedulista: "1074577152", waText: "Hi%20Sandy%20Nour%2C%20I%27d%20like%20to%20visit%20the%20d3%20eveningwear%20showroom.", map: "https://maps.app.goo.gl/iwV9eFLQCt2dmu6h8", }, { city: "Dubai", tag: "Bridal · Jebel Ali", addr: "National Industries Park\nJebel Ali", rows: [ ["Phone", "+971 58 514 0008"], ["Hours", "Mon–Fri 11:00 – 19:00\nSaturday 11:00 – 18:00", "flagged"], ["By", "Appointment only"], ], schedulista: "1074642641", waText: "Hi%20Sandy%20Nour%2C%20I%27d%20like%20to%20visit%20the%20Jebel%20Ali%20bridal%20showroom.", map: "https://maps.app.goo.gl/", }, { city: "Beirut", tag: "Bridal & Eveningwear", addr: "Nahr el Mot, Mar Mtanios Street\nRazico Center · 2nd Floor", rows: [ ["Phone", "+961 70 885 955"], ["Hours", "Mon–Fri 10:00 – 18:00"], ["By", "Appointment only"], ], schedulista: "1074645528", waText: "Hi%20Sandy%20Nour%2C%20I%27d%20like%20to%20visit%20the%20Beirut%20atelier.", map: "https://maps.app.goo.gl/", }, ]; const TIMELINE = [ ["MILAN", "MA, Istituto Marangoni"], ["BEIRUT", "Atelier & sample-room, est. mid-2000s"], ["2017", "Eponymous bridal and eveningwear lines launched"], ["TODAY", "230+ international stockists, 50+ artisans and designers"], ]; const SCHEDULISTA = "https://www.schedulista.com/schedule/sandynour/choose_time"; const WA_BASE = "https://wa.me/971522693605"; /* ─────────── i18n ─── only a handful of strings flipped on toggle ─── */ const I18N = { en: { eyebrow: "Sandy Nour · Bridal & Eveningwear", title1: "Contemporary", title2: "fairytales", title3: "woven into timeless wardrobes.", sub: "An editorial private-showroom layer over a 306-piece live USD catalogue — hand-beaded by 50+ artisans, from a Beirut atelier with roots in the mid-2000s.", bookFitting: "Book a private fitting", whatsapp: "WhatsApp the atelier", scroll: "Scroll · Spring–Summer 26", side: "House of Sandy Nour · couture maison", featured: "Featured pieces", bridal: "Bridal", evening: "Evening Wear", bespoke: "Bespoke", designerLink:"The Designer", showrooms: "Showrooms", }, ar: { eyebrow: "ساندي نور · فساتين زفاف وسهرة", title1: "حكايات", title2: "معاصرة", title3: "تُنسج في خزائن خالدة.", sub: "طبقة صالة عرض خاصة فوق كتالوج حيّ يضمّ 306 قطعة بالدولار — مطرّزة يدوياً بأيدي أكثر من 50 حرفياً، من أتيليه بيروتي تأسس في منتصف العقد الأول من الألفية.", bookFitting: "احجزي قياساً خاصاً", whatsapp: "واتساب الأتيليه", scroll: "تمرير · ربيع–صيف ٢٦", side: "دار ساندي نور", featured: "قطع مختارة", bridal: "زفاف", evening: "سهرة", bespoke: "تفصيل", designerLink:"المصممة", showrooms: "صالات العرض", }, }; const fmtPrice = (n) => new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 0 }).format(n); /* ─────────── components ─────────── */ function Nav({ t, lang, setLang, tweaks }) { const links = [ ["Bridal", "#bridal"], ["Evening Wear", "#evening"], ["Bespoke", "#bespoke"], ["The Designer", "#designer"], ["Showrooms", "#showrooms"], ]; return (
); } function Hero({ t, tweaks }) { return (
{t.side}
{t.eyebrow}

{t.title1}{" "} {t.title2}
{t.title3}

{t.sub}

Selena · Bridal
L'Amour en Fleurs · SS26
USD 16,500 · made to order
{t.scroll}
); } function TrustSpine() { const items = [ { n: "MA", sub: "Istituto Marangoni, Milan" }, { n: "2000s", sub: "Beirut atelier, est. mid-2000s" }, { n: "230+", sub: "International stockists" }, { n: "50+", sub: "Artisans & designers" }, { n: "d3", sub: "Dubai Design District presence" }, ]; return (
{items.map((it, i) => (
{it.n.includes("&") ? it.n.split("&").map((part, idx) => ( {idx > 0 && &} {part} )) : it.n}
{it.sub}
))}
); } function CollectionSplit({ t }) { return (
01 · {t.bridal} 53 gowns

For her walk
toward forever

Made-to-order bridal couture from the atelier — hand-beaded silks, sculpted tulle, and trains drafted to your measurements.

Enter the bridal world
02 · {t.evening} 242 pieces

For her nights
of consequence

Hand-beaded eveningwear from USD 6,600 — galas, weddings of the week, the kind of evening you'll want to remember.

Enter eveningwear
); } function FeaturedGrid({ t, tweaks }) { const priceClass = tweaks.priceMode === "hidden" ? "hide-price" : tweaks.priceMode === "hover" ? "hover-price" : ""; const items = useMemo(() => { if (tweaks.density === "compact") { return [...FEATURED, { src: BRIDAL[0], type: "BRIDAL" }, { src: EVENING[6], type: "EVENING" }, { src: BRIDAL[9], type: "BRIDAL" }, { src: EVENING[7], type: "EVENING" }]; } return FEATURED; }, [tweaks.density]); return ( ); } function Bespoke() { const steps = [ ["01", "Private consultation at the atelier"], ["02", "Measurements & sketch review"], ["03", "Fabric & beadwork selection"], ["04", "Two fittings before final hand-over"], ]; return (
02 · BESPOKE & MADE TO ORDER

Cut, beaded,
and finished to you.

Every silhouette in the house is a starting point. Adjust the neckline, re-route the embroidery, change the colour, shift the train. Tailoring is finished by hand at the Beirut atelier; alterations are included on made-to-order commissions.

    {steps.map(([num, label]) => (
  • {num} {label}
  • ))}
Book a consultation The atelier story
); } function Showrooms() { return (
03 · THREE SHOWROOMS · BY APPOINTMENT

Visit the maison

Each room serves a different conversation — eveningwear in d3, bridal at Jebel Ali, and the founding atelier in Beirut. Bookings flow through Schedulista, with a WhatsApp fallback per room.

{SHOWROOMS.map((s, i) => (
{s.city}
No. 0{i + 1} · {s.tag}
{s.addr.split("\n").map((line, idx) => ( {line} {idx < s.addr.split("\n").length - 1 &&
}
))}
{s.rows.map(([k, v, flag]) => (
{k}
{v.split("\n").map((line, idx) => ( {line} {idx < v.split("\n").length - 1 &&
}
))} {flag && ⚑ pending}
))}
Book a fitting WhatsApp
))}
); } function Designer() { return (
04 · THE DESIGNER

"I draft a gown the way I'd draft a letter — slowly, by hand, until every line says exactly what it needs to say, and nothing more."

SN Sandy Nour, founder
    {TIMELINE.map(([yr, txt]) => (
  • {yr} {txt}
  • ))}
Read the atelier story
Designer portrait client-supplied · pending
Atelier · Beirut
); } function AppointmentBand() { return (
05 · BY APPOINTMENT

Sit with us for an afternoon.

Private fittings are unhurried — fabric on the table, beadwork in hand, a sketchpad open between us. Pick a showroom, leave the rest to the atelier.

Book a private fitting WhatsApp the atelier
{SHOWROOMS.map((s, i) => (
0{i + 1}
{s.city}

{s.tag}

))}
); } function Footer() { return ( ); } function WhatsAppFab() { return ( ); } /* ─────────── Tweaks panel — wired to the host edit mode ─────────── */ const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "accent": "antique", "background": "ivory", "priceMode": "always", "density": "editorial" }/*EDITMODE-END*/; function applyTweaks(tweaks) { const root = document.documentElement; const gold = { antique: ["#B89A66", "#8A6D2F", "#C9A24B"], champagne: ["#D7BE8E", "#A38649", "#E7C879"], buff: ["#A98D5E", "#735727", "#B89958"], }[tweaks.accent] || ["#B89A66", "#8A6D2F", "#C9A24B"]; root.style.setProperty("--gold", gold[0]); root.style.setProperty("--gold-deep", gold[1]); root.style.setProperty("--gold-bright",gold[2]); const bg = { ivory: ["#FBF8F4", "#F4EFE7", "#FFFFFF"], paper: ["#FFFFFF", "#F6F4F0", "#FFFFFF"], espresso: ["#161210", "#1F1A17", "#241E1A"], }[tweaks.background] || ["#FBF8F4", "#F4EFE7", "#FFFFFF"]; root.style.setProperty("--bg", bg[0]); root.style.setProperty("--bg-deep", bg[1]); root.style.setProperty("--surface", bg[2]); if (tweaks.background === "espresso") { root.style.setProperty("--text", "#F4EFE7"); root.style.setProperty("--text-soft", "#D6CDBF"); root.style.setProperty("--muted", "#9A9087"); root.style.setProperty("--hairline", "#2A2421"); root.style.setProperty("--hairline-strong", "#3A332D"); } else { root.style.setProperty("--text", "#1F1A17"); root.style.setProperty("--text-soft", "#3A322C"); root.style.setProperty("--muted", "#6B635B"); root.style.setProperty("--hairline", "#E7E0D6"); root.style.setProperty("--hairline-strong", "#D6CDBF"); } document.body.classList.toggle("dense", tweaks.density === "compact"); } /* ─────────── root ─────────── */ function App() { const [lang, setLang] = useState("en"); const t = I18N[lang]; const { useTweaks, TweaksPanel, TweakSection, TweakRadio, TweakSelect } = window; const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS); useEffect(() => { applyTweaks(tweaks); }, [tweaks]); useEffect(() => { document.documentElement.lang = lang; document.documentElement.dir = lang === "ar" ? "rtl" : "ltr"; }, [lang]); return ( <>