/* Shared site chrome + widgets. DS components come from the bundle namespace. */
const DS = window.WeddingSalonDesignSystem_69a5c8;
const { Button, Icon, Card, TextInput, Select, Checkbox, TestimonialCard, Badge } = DS;
const BIZ = {
name: "Wedding Salon",
phoneDisplay: "054-834-6620",
phoneHref: "tel:+972548346620",
whatsapp: "https://wa.me/972548346620",
email: "latishovi@gmail.com",
instagram: "@weddingsalon",
};
const LANGS = ["he", "ru", "en"];
/* Language state lives on (lang + dir) and in localStorage. */
const LangCtx = React.createContext(null);
function LangProvider({ page, children }) {
const [lang, setLang] = React.useState(() => {
try { const s = localStorage.getItem("ws-lang"); if (LANGS.includes(s)) return s; } catch (e) {}
return "he";
});
const t = window.I18N[lang];
React.useEffect(() => {
document.documentElement.lang = lang;
document.documentElement.dir = t.dir;
const m = t.meta[page];
if (m) {
document.title = m[0];
const d = document.querySelector('meta[name="description"]');
if (d) d.setAttribute("content", m[1]);
}
try { localStorage.setItem("ws-lang", lang); } catch (e) {}
}, [lang, page, t]);
return {children};
}
const useT = () => React.useContext(LangCtx);
function LangSwitch() {
const { lang, setLang, t } = useT();
return (
{LANGS.map((code, i) => (
{i > 0 && }
))}
);
}
const navLinks = (t) => [
{ key: "home", label: t.nav.home, href: "index.html" },
{ key: "about", label: t.nav.about, href: "index.html#story" },
{ key: "services", label: t.nav.services, href: "index.html#services" },
{ key: "portfolio", label: t.nav.portfolio, href: "index.html#portfolio" },
{ key: "contact", label: t.nav.contact, href: "contact.html" },
];
/* Scroll reveal — one observer per opted-in element. */
function Reveal({ children, delay = 0, as = "div", className = "", style }) {
const ref = React.useRef(null);
React.useEffect(() => {
const el = ref.current;
if (!el) return;
const io = new IntersectionObserver(
(es) => es.forEach((e) => { if (e.isIntersecting) { setTimeout(() => el.classList.add("in"), delay); io.unobserve(el); } }),
{ rootMargin: "0px 0px -8% 0px", threshold: 0.05 }
);
io.observe(el);
return () => io.disconnect();
}, [delay]);
return React.createElement(as, { ref, className: "rv " + className, style }, children);
}
/* Stock photography (Unsplash, free license) keyed by slot id. Credit is required
and renders in the slot; a user-dropped photo replaces src and hides it. */
const PHOTOS = {
"home-hero": ["images/hero-dress.png"],
"svc-dress": ["photo-1521467752200-3bccf80f16ed", "Thomas AE", "thomasae"],
"svc-photo": ["photo-1591604466107-ec97de577aff", "Eugenia Pan'kiv", "eugenivy_now"],
"home-story": ["photo-1532454781337-fc3edff34f91", "Duo Chen", "duochenphotography"],
"pf-1": ["photo-1549416878-b9ca95e26903", "Maria Orlova", "orlovamaria"],
"pf-2": ["photo-1502955422409-06e43fd3eff3", "Anna Vi", "anna_vi_travel"],
"pf-4": ["photo-1599142296733-1c1f2073e6de", "Brittney Weng", "brittneyweng"],
"pf-5": ["photo-1585241920473-b472eb9ffbae", "Jonathan Borba", "jonathanborba"],
"pf-6": ["photo-1621369663005-68a558655fad", "Filipp Romanovski", "filipp_roman_photography"],
"pf-7": ["flagged/photo-1578317767641-c2a23b16b814", "NIKITA SHIROKOV", "shirokov"],
"contact-hero": ["photo-1492175742197-ed20dc5a6bed", "Petr Ovralov", "petrov"],
"dr-hero": ["images/hero-dress.png"],
"dr-intro": ["photo-1521467752200-3bccf80f16ed", "Thomas AE", "thomasae"],
"dr-g1": ["photo-1622277430358-f4d134452e2e", "Asdrubal luna", "infectedluna"],
"dr-g2": ["photo-1502955422409-06e43fd3eff3", "Anna Vi", "anna_vi_travel"],
"dr-g3": ["photo-1621369663005-68a558655fad", "Filipp Romanovski", "filipp_roman_photography"],
"dr-g4": ["photo-1591221662157-6f62de5508eb", "Cate Bligh", "catebligh"],
"dr-g5": ["photo-1572322633904-ebbd90cb2f9b", "Anthony Espinosa", "thony_espi"],
"dr-g6": ["photo-1569849324286-55b6c5f925e5", "Anaïs MURITH", "nanarose"],
"dr-d1": ["flagged/photo-1578317767641-c2a23b16b814", "NIKITA SHIROKOV", "shirokov"],
"dr-d2": ["photo-1599142296733-1c1f2073e6de", "Brittney Weng", "brittneyweng"],
"dr-d3": ["photo-1549416878-b9ca95e26903", "Maria Orlova", "orlovamaria"],
"dr-d4": ["photo-1532454781337-fc3edff34f91", "Duo Chen", "duochenphotography"],
"dr-d5": ["photo-1585241920473-b472eb9ffbae", "Jonathan Borba", "jonathanborba"],
"dr-d6": ["photo-1492175742197-ed20dc5a6bed", "Petr Ovralov", "petrov"],
"ph-hero": ["photo-1622277430358-f4d134452e2e", "Asdrubal luna", "infectedluna"],
"ph-intro": ["photo-1591604466107-ec97de577aff", "Eugenia Pan'kiv", "eugenivy_now"],
"ph-g1": ["photo-1549416878-b9ca95e26903", "Maria Orlova", "orlovamaria"],
"ph-g2": ["photo-1599142296733-1c1f2073e6de", "Brittney Weng", "brittneyweng"],
"ph-g3": ["photo-1585241920473-b472eb9ffbae", "Jonathan Borba", "jonathanborba"],
"ph-g4": ["photo-1502955422409-06e43fd3eff3", "Anna Vi", "anna_vi_travel"],
"ph-g5": ["photo-1521467752200-3bccf80f16ed", "Thomas AE", "thomasae"],
"ph-g6": ["photo-1621369663005-68a558655fad", "Filipp Romanovski", "filipp_roman_photography"],
"ph-g7": ["photo-1532454781337-fc3edff34f91", "Duo Chen", "duochenphotography"],
"ph-s1": ["photo-1492175742197-ed20dc5a6bed", "Petr Ovralov", "petrov"],
"ph-s2": ["photo-1532454781337-fc3edff34f91", "Duo Chen", "duochenphotography"],
"ph-s4": ["flagged/photo-1578317767641-c2a23b16b814", "NIKITA SHIROKOV", "shirokov"],
"svc-makeup": ["photo-1636023730877-233b9237d4ec", "Alena Plotnikova", "alena_horoshaya"],
"pf-3": ["photo-1709477542149-f4e0e21d590b", "LOLA AZIZADA", "kalpa_mahagamage"],
"mk-hero": ["photo-1594465919760-441fe5908ab0", "Amir Seilsepour", "amiresel"],
"mk-intro": ["photo-1636023730877-233b9237d4ec", "Alena Plotnikova", "alena_horoshaya"],
"mk-g1": ["photo-1709477542149-f4e0e21d590b", "LOLA AZIZADA", "kalpa_mahagamage"],
"mk-g2": ["photo-1709477542170-f11ee7d471a0", "LOLA AZIZADA", "kalpa_mahagamage"],
"mk-g3": ["photo-1630084775816-7abb7383ded5", "Vince Mariel Conlu", "vimarco"],
"mk-g4": ["photo-1512496015851-a90fb38ba796", "Jazmin Quaynor", "jazminantoinette"],
"mk-g5": ["photo-1511923199659-1c16881689de", "Jake Peterson", "jake101"],
"ph-s3": ["photo-1550005809-91ad75fb315f", "Olivia Bauso", "ohhbee"],
"rel-makeup": ["photo-1709477542170-f11ee7d471a0", "LOLA AZIZADA", "kalpa_mahagamage"],
"rel-dresses": ["photo-1521467752200-3bccf80f16ed", "Thomas AE", "thomasae"],
"rel-photo": ["photo-1585241920473-b472eb9ffbae", "Jonathan Borba", "jonathanborba"],
};
/* Cutout images on a blank ground read better fully visible than cropped. */
const CONTAIN = new Set(["home-hero", "dr-hero"]);
function Slot({ id, placeholder, radius = 16, style }) {
const p = PHOTOS[id];
const local = p && p.length === 1;
const src = p ? (local ? p[0] : "https://images.unsplash.com/" + p[0] + "?auto=format&fit=crop&w=1600&q=80") : undefined;
return (
);
}
function SiteHeader({ active }) {
const { t } = useT();
const links = navLinks(t);
const [scrolled, setScrolled] = React.useState(false);
const [open, setOpen] = React.useState(false);
React.useEffect(() => {
const on = () => setScrolled(window.scrollY > 24);
on(); window.addEventListener("scroll", on, { passive: true });
return () => window.removeEventListener("scroll", on);
}, []);
React.useEffect(() => {
document.body.style.overflow = open ? "hidden" : "";
const esc = (e) => e.key === "Escape" && setOpen(false);
window.addEventListener("keydown", esc);
return () => window.removeEventListener("keydown", esc);
}, [open]);
return (
{open && (
{BIZ.name}
)}
);
}
function FooterCol({ title, items }) {
return (
{title}
{items.map((it) =>
it.href
?
{it.label}
:
{it.label}
)}
);
}
function SiteFooter() {
const { t } = useT();
return (
);
}
function WhatsAppFab() {
const { t } = useT();
return ;
}
/* Lightbox reads the image currently shown in a slot (stock or user-dropped). */
function useLightbox() {
const { t } = useT();
const [shot, setShot] = React.useState(null);
React.useEffect(() => {
const esc = (e) => e.key === "Escape" && setShot(null);
window.addEventListener("keydown", esc);
return () => window.removeEventListener("keydown", esc);
}, []);
const open = (e) => {
const slot = e.currentTarget.querySelector("image-slot");
const img = slot && slot.shadowRoot && slot.shadowRoot.querySelector("img");
setShot({ src: img && img.getAttribute("src"), label: e.currentTarget.getAttribute("data-label") || "" });
};
const node = shot && (
setShot(null)}>
{shot.src
?

:
{t.pf.empty}
}
);
return [open, node];
}
/* Full inquiry form: validation, loading, success. */
function InquiryForm({ compact = false, onDone }) {
const { t, lang } = useT();
const f = t.form;
const [v, setV] = React.useState({ name: "", phone: "", email: "", date: "", service: "", note: "" });
const [err, setErr] = React.useState({});
const [state, setState] = React.useState("idle");
const set = (k) => (e) => setV({ ...v, [k]: e.target.value });
/* Service pages link in with ?service=dresses|makeup|photo, optionally
&dress= or &look=, which preselect the service and open the note. */
React.useEffect(() => {
const q = new URLSearchParams(location.search);
const idx = { dresses: 0, makeup: 1, photo: 2 }[q.get("service")];
const named = q.get("dress") || q.get("look");
setV((p) => ({
...p,
service: f.options[idx === undefined ? 0 : idx],
note: named && t.dresses ? t.dresses.askPrefix + " " + named + "." : p.note,
}));
setErr({});
}, [lang]);
const submit = (e) => {
e.preventDefault();
const next = {};
if (v.name.trim().length < 2) next.name = f.errName;
if (!/^[\d\s+\-()]{9,}$/.test(v.phone.trim())) next.phone = f.errPhone;
if (!compact && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(v.email.trim())) next.email = f.errEmail;
setErr(next);
if (Object.keys(next).length) return;
setState("sending");
setTimeout(() => { setState("sent"); onDone && onDone(); }, 900);
};
if (state === "sent") {
return (
{f.okTitle}
{f.okBody}
);
}
return (
);
}
Object.assign(window, { DS, BIZ, LANGS, LangProvider, useT, LangSwitch, navLinks, Reveal, Slot, SiteHeader, SiteFooter, WhatsAppFab, useLightbox, InquiryForm });