// Sonq'o Wasi — SPA app
const { useState, useEffect, useMemo, useRef } = React;
// ── Assets ────────────────────────────────────────────────────────
const EMBLEM = "media/emblem.png";
const SPACE = (n) => "media/space/p" + String(n).padStart(2, "0") + ".jpg";
// Curated atmospheric photos of the house — every slot is a UNIQUE photo (no repeats site-wide)
const PHOTOS = {
homeStrip: 11,
homeBand: 25,
events: [41, 46, 51], // home event posters
houseOrigin: 43,
houseGallery: [4, 73, 79],
weddings: [0, 82],
posts: [6, 13, 30, 76], // journal covers
};
// Therapist portrait → cropped from each person's composite card, matched by Instagram handle
const FACE_BY_HANDLE = {
"@natashallerena": 0, "@somosnossacasa": 1, "@niceemariejourdan": 2,
"@marianaguimaraes_alquimias": 3, "@sabrinaramosalquimia": 4, "@marinaalmeidamayan": 5,
"@lucianaportellaterapeuta": 6, "@liviapaesbemestar": 7, "@ecodelica_art": 8,
"@andreelkind": 10, "@alcirblondet": 11, "@fala.mohana": 12,
"@alexandraflaviodoutoraufrj": 13, "@frpassarelli": 14, "@du.alecrim": 15,
"@eu.fernandatelles": 16, "@ginecologianatural": 17, "@bebelclark": 18,
"@caroljourdan.essencial": 19,
};
const FACE = (h) => h in FACE_BY_HANDLE
? "media/faces/f" + String(FACE_BY_HANDLE[h]).padStart(2, "0") + ".jpg"
: null;
// Therapist filter categories + tags (by handle)
const CATEGORIES = [
{ key: "all", label: { pt: "Todos", es: "Todas", en: "All" } },
{ key: "corpo", label: { pt: "Corpo & Movimento", es: "Cuerpo & Movimiento", en: "Body & Movement" } },
{ key: "som", label: { pt: "Som & Voz", es: "Sonido & Voz", en: "Sound & Voice" } },
{ key: "feminino", label: { pt: "Feminino", es: "Femenino", en: "Feminine" } },
{ key: "alquimia", label: { pt: "Alquimia & Ervas", es: "Alquimia & Hierbas", en: "Alchemy & Herbs" } },
{ key: "ritual", label: { pt: "Rituais & Medicinas", es: "Rituales & Medicinas", en: "Ritual & Medicine" } },
{ key: "energia", label: { pt: "Energia & Oráculos", es: "Energía & Oráculos", en: "Energy & Oracles" } },
{ key: "mente", label: { pt: "Mente & Espírito", es: "Mente & Espíritu", en: "Mind & Spirit" } },
{ key: "casa", label: { pt: "Casa & Espaço", es: "Casa & Espacio", en: "Home & Space" } },
];
const TAGS_BY_HANDLE = {
"@somosnossacasa": ["casa"],
"@natashallerena": ["som"],
"@sabrinaramosalquimia": ["alquimia"],
"@niceemariejourdan": ["energia", "mente"],
"@marianaguimaraes_alquimias": ["alquimia"],
"@marinaalmeidamayan": ["feminino", "ritual"],
"@ecodelica_art": ["energia", "ritual"],
"@lucianaportellaterapeuta": ["som", "feminino", "ritual"],
"@liviapaesbemestar": ["corpo"],
"@andreelkind": ["mente"],
"@alexandraflaviodoutoraufrj": ["mente", "ritual"],
"@alcirblondet": ["ritual", "corpo", "alquimia"],
"@fala.mohana": ["corpo"],
"@eu.fernandatelles": ["corpo", "feminino", "alquimia"],
"@frpassarelli": ["energia"],
"@du.alecrim": ["alquimia", "feminino", "energia"],
"@caroljourdan.essencial": ["feminino", "corpo"],
"@ginecologianatural": ["feminino"],
"@bebelclark": ["feminino", "energia", "mente"],
};
// ── Hooks ─────────────────────────────────────────────────────────
function useHashRoute() {
const [route, setRoute] = useState(() => (window.location.hash.replace("#/", "") || "home"));
useEffect(() => {
const handler = () => {
const next = window.location.hash.replace("#/", "") || "home";
setRoute(next);
window.scrollTo({ top: 0, behavior: "instant" });
};
window.addEventListener("hashchange", handler);
return () => window.removeEventListener("hashchange", handler);
}, []);
return [route, (r) => { window.location.hash = "#/" + r; }];
}
function useLang() {
const [lang, setLang] = useState(() => localStorage.getItem("sonqo_lang") || "pt");
useEffect(() => {
localStorage.setItem("sonqo_lang", lang);
document.documentElement.lang = lang;
}, [lang]);
return [lang, setLang];
}
// ── Atoms ─────────────────────────────────────────────────────────
function Glyph({ size = 24 }) {
return (
);
}
function Arrow({ size = 14 }) {
return (
);
}
function WhatsAppIcon() {
return (
);
}
function InstagramIcon() {
return (
);
}
// Ornamental ring echoing the ouroboros of the identity mark
function SerpentRing({ className }) {
const ticks = Array.from({ length: 72 }, (_, i) => {
const a = (i / 72) * Math.PI * 2;
const r2 = i % 6 === 0 ? 80 : 85;
return (
);
});
return (
{ticks}
);
}
function Nav({ route, go, lang, setLang }) {
const t = window.SONQO_I18N[lang];
const [open, setOpen] = useState(false);
const items = [
["home", t.nav.home],
["house", t.nav.house],
["manifesto", t.nav.manifesto],
["therapists", t.nav.therapists],
["events", t.nav.events],
["weddings", t.nav.weddings],
["join", t.nav.join],
["journal", t.nav.journal],
["contact", t.nav.contact],
["directions", t.nav.directions],
];
// Início lives on the emblem (click → home). Seja Associado stays in the mobile
// menu + footer; the desktop bar carries a lean 3-left / 4-right split.
const link = ([key, label]) => (
setOpen(false)}>{label}
);
const left = [items[1], items[2], items[3], items[4]]; // A Casa · Manifesto · Terapeutas · Eventos
const right = [items[5], items[7], items[8], items[9]]; // Casamentos · Diário · Contato · Como Chegar
return (
setOpen(!open)}>
{open ? "×" : "☰"} {open ? "" : "Menu"}
{left.map(link)}
{ go("home"); setOpen(false); }}>
Sonq'o Wasi
Espaço de Rezo, Arte & Estudo
{right.map(link)}
setLang("pt")}>PT
/
setLang("es")}>ES
/
setLang("en")}>EN
{items.map(link)}
);
}
function PageHero({ crumb, scriptText, serifText, lede, lang }) {
const t = window.SONQO_I18N[lang];
return (
{scriptText}
{serifText}
{lede &&
{lede}
}
);
}
// ── Pages ─────────────────────────────────────────────────────────
function HomePage({ go, lang }) {
const t = window.SONQO_I18N[lang];
const events = window.SONQO_EVENTS.slice(0, 3);
return (
{t.home.meta1}
{t.home.script}
{t.home.serif}
{t.home.hero_tagline}
{t.home.sub}
{t.home.meta2}
{t.home.meta3}
01 / {t.home.pillars_eyebrow}
{t.home.pillars_title}
{t.home.pillars_lede}
02 / {t.home.house_eyebrow}
{t.home.house_title_a}
{t.home.house_title_script}
{t.home.house_p1}
{t.home.house_p2}
{t.home.house_cta}
03 / {t.home.events_eyebrow}
{t.home.events_title}
{t.home.events_lede}
{t.home.script} {t.home.serif}
{t.home.band_tagline}
);
}
function HousePage({ lang }) {
const t = window.SONQO_I18N[lang].house;
const tc = window.SONQO_I18N[lang];
return (
{t.origin_eyebrow}
{t.origin_title_a}
{t.origin_title_script}
{t.origin_body_1}
{t.origin_body_2}
{t.origin_body_3}
{t.accent_tagline}
{t.principles_eyebrow}
{t.principles_title}
{[
[t.p1_t, t.p1_d], [t.p2_t, t.p2_d], [t.p3_t, t.p3_d], [t.p4_t, t.p4_d]
].map(([title, body], i) => (
{["I","II","III","IV"][i]}
{title}
{body}
))}
{PHOTOS.houseGallery.map((n, i) => (
))}
);
}
function ManifestoPage({ lang }) {
const t = window.SONQO_I18N[lang];
const m = window.SONQO_MANIFESTO[lang];
return (
✦
{m.body.map((b, i) => {
if (b.kind === "lead") return
{b.text}
;
if (b.kind === "verse") return (
{b.lines.map((l, j) => {l} )}
);
if (b.kind === "closing") return (
{b.lines.map((l, j) => {l} )}
);
return
{b.text}
;
})}
{lang === "pt" && "Caminhe conosco"}
{lang === "es" && "Camina con nosotras"}
{lang === "en" && "Walk with us"}
{t.nav.join}
);
}
function TherapistsPage({ lang }) {
const t = window.SONQO_I18N[lang].therapists;
const tc = window.SONQO_I18N[lang];
const list = window.SONQO_THERAPISTS;
const [filter, setFilter] = useState("all");
// only show category chips that actually have someone
const cats = CATEGORIES.filter(c => c.key === "all" ||
list.some(th => (TAGS_BY_HANDLE[th.handle] || []).includes(c.key)));
const shown = list.filter(th => filter === "all" ||
(TAGS_BY_HANDLE[th.handle] || []).includes(filter));
const countLabel = {
pt: shown.length + (shown.length === 1 ? " terapeuta" : " terapeutas"),
es: shown.length + (shown.length === 1 ? " terapeuta" : " terapeutas"),
en: shown.length + (shown.length === 1 ? " practitioner" : " practitioners"),
}[lang];
return (
{cats.map(c => (
setFilter(c.key)}>
{c.label[lang]}
))}
{countLabel}
{shown.map((th, i) => {
const phoneDigits = th.phone.replace(/\D/g, "");
const igHandle = th.handle.replace("@", "");
const initials = th.name.split(" ").map(n => n[0]).slice(0,2).join("");
const photo = th.photo || FACE(th.handle);
return (
{photo
?
:
{initials}
}
{th.name}
{th.services[lang].map((s, j) => {s} )}
);
})}
);
}
function EventsPage({ lang }) {
const t = window.SONQO_I18N[lang].events;
const tc = window.SONQO_I18N[lang];
const events = window.SONQO_EVENTS;
const agenda = window.SONQO_AGENDA || [];
const ig = (window.SONQO_IG || []).slice(0, 8);
const igLabel = { pt: "Últimas do Instagram", es: "Lo último de Instagram", en: "Latest from Instagram" }[lang];
const agendaTitle = { pt: "Agenda 2026", es: "Agenda 2026", en: "2026 calendar" }[lang];
const agendaEyebrow = { pt: "Calendário do ano", es: "Calendario del año", en: "Year calendar" }[lang];
const agendaLede = {
pt: "Os encontros fixos da casa ao longo de 2026. As datas mensais exatas são confirmadas a cada mês no Instagram.",
es: "Los encuentros fijos de la casa a lo largo de 2026. Las fechas mensuales exactas se confirman cada mes en Instagram.",
en: "The house's recurring gatherings through 2026. Exact monthly dates are confirmed each month on Instagram.",
}[lang];
return (
{t.cal_eyebrow}
{t.cal_title}
{t.ig_handle_label}
{agendaEyebrow}
{agendaTitle}
{agendaLede}
{agenda.map((group, gi) => (
{group.cat[lang]}
{group.items.length}
{group.items.map((it, ii) => (
{it.w[lang]}
{it.t}
{it.note && {it.note[lang]} }
))}
))}
{ig.length > 0 && (
)}
{tc.common.instagram}
@sonqowasi_
{lang === "pt" && "Os eventos são atualizados primeiro pelo Instagram. Siga para receber lembretes e abrir as inscrições."}
{lang === "es" && "Los eventos se actualizan primero en Instagram. Síguenos para recibir recordatorios y abrir las inscripciones."}
{lang === "en" && "Events are updated first on Instagram. Follow to get reminders and open bookings."}
{t.ig_cta}
);
}
function WeddingsPage({ lang }) {
const t = window.SONQO_I18N[lang].weddings;
const tc = window.SONQO_I18N[lang];
return (
{t.what_eyebrow}
{t.what_title}
{[[t.i1_t, t.i1_d],[t.i2_t, t.i2_d],[t.i3_t, t.i3_d],[t.i4_t, t.i4_d]].map(([title, desc], i) => (
{["✦","◯","♪","❀"][i]}
{title}
{desc}
))}
);
}
function JoinPage({ lang }) {
const t = window.SONQO_I18N[lang].join;
return (
i
{t.track1_label}
{t.track1_t}
{t.track1_d}
{t.track1_b1}
{t.track1_b2}
{t.track1_b3}
{t.track1_b4}
{t.track1_cta}
ii
{t.track2_label}
{t.track2_t}
{t.track2_d}
{t.track2_b1}
{t.track2_b2}
{t.track2_b3}
{t.track2_b4}
{t.track2_cta}
);
}
function JournalPage({ lang }) {
const t = window.SONQO_I18N[lang].journal;
const posts = window.SONQO_POSTS;
return (
);
}
function ContactPage({ lang }) {
const t = window.SONQO_I18N[lang].contact;
const [sent, setSent] = useState(false);
return (
{t.direct_eyebrow}
{t.direct_title}
{t.address_label}
{t.address_value}
{t.hours_label}
{t.hours_value}
{t.form_eyebrow}
{t.form_title}
{sent ? (
{t.sent}
) : (
)}
);
}
function DirectionsPage({ lang }) {
const t = window.SONQO_I18N[lang].directions;
const tc = window.SONQO_I18N[lang].contact;
const modes = [[t.m1_t, t.m1_d], [t.m2_t, t.m2_d], [t.m3_t, t.m3_d]];
return (
{t.ways_eyebrow}
{t.ways_title}
{modes.map(([title, desc], i) => (
{["⌖", "◷", "❦"][i]}
{title}
{desc}
))}
);
}
function Footer({ lang, go }) {
const t = window.SONQO_I18N[lang];
return (
Sonq'o Wasi
{t.home.footer_tagline}
© 2026 Sonq'o Wasi · sonqowasi.com.br · {t.footer.rights}
{t.footer.made_with}
);
}
// ── Root App ──────────────────────────────────────────────────────
function App() {
const [route, go] = useHashRoute();
const [lang, setLang] = useLang();
const PageComp = useMemo(() => {
switch (route) {
case "house": return HousePage;
case "manifesto": return ManifestoPage;
case "therapists": return TherapistsPage;
case "events": return EventsPage;
case "weddings": return WeddingsPage;
case "join": return JoinPage;
case "journal": return JournalPage;
case "contact": return ContactPage;
case "directions": return DirectionsPage;
case "home":
default: return HomePage;
}
}, [route]);
return (
);
}
ReactDOM.createRoot(document.getElementById("root")).render( );