/* ------------------------------------------------------------------ *
* Home page (i18n)
* ------------------------------------------------------------------ */
function PageHome({ navigate }) {
const { t } = useLang();
const heroTitle = t("home.hero.title"); // array of 4 strings
const productsTitle = t("home.products.title"); // array of 2
return (
{/* HERO — Editorial Collage */}
{t("home.location")}
— Floating Editions · No. 01 —
N 18°28′ · W 69°54′
The 2026 Collection
{heroTitle[0]}
{heroTitle[1]}{" "}
{heroTitle[2]}
{heroTitle[3]}
{t("home.hero.sub")}
navigate("/collection")}>
{t("home.intro.cta1")} →
03
Signature Pieces
01 Aqua Tray · Casa de Campo
02 Turquoise · Cap Cana
03 Aqua Box
04 Aqua Bar
— Designed in the Dominican Republic.
— Crafted for the Caribbean climate.
{t("common.scroll")}
{/* MARQUEE */}
{/* INTRO */}
{t("home.intro.eyebrow")}
{t("home.intro.title.a")}{t("home.intro.title.b")}
{t("home.intro.body")}
navigate("/collection")}>
{t("home.intro.cta1")} →
navigate("/story")}>
{t("home.intro.cta2")}
i. Aqua Box
ii. Aqua Tray
iii. Aqua Bar
{/* PRODUCTS */}
{productsTitle[0]} {productsTitle[1]}>}
count={t("home.products.count")}
/>
{getProducts(t).map((p, i) => (
))}
{/* PULL QUOTE */}
{t("home.quote.eyebrow")}
{t("home.quote.body")}
{t("home.quote.name")}
{t("home.quote.role")}
{/* MATERIALS */}
{t("home.materials.title.a")}{t("home.materials.title.em")} {t("home.materials.title.b")}>}
kicker={t("home.materials.kicker")}
/>
{getStats(t).map((s, i) => (
{s.num}
{s.label}
))}
{/* COVERAGE */}
{t("home.coverage.eyebrow")}
{t("home.coverage.title.a")}{t("home.coverage.title.em")} {t("home.coverage.title.b")}
{t("home.coverage.body")}
navigate("/contact")}>
{t("home.coverage.cta")} →
{/* CTA */}
{t("home.cta.eyebrow")}
{t("home.cta.title.a")}{t("home.cta.title.em1")} {t("home.cta.title.b")}{t("home.cta.title.em2")}
{t("home.cta.body")}
navigate("/contact")}>
{t("home.cta.button")} →
);
}
// Translated product builder
function getProducts(t) {
return [
{
id: "aqua-tray",
name: t("p.tray.name"),
tagline: t("p.tray.tagline"),
desc: t("p.tray.desc"),
tone: "sand",
photo: "assets/photos/tray-hero-woman.jpg",
photoAlt: "assets/photos/tray-charcuterie.jpg",
gallery: [
{ src: "assets/photos/tray-hero-woman.jpg", label: "01 · Poolside", crop: "Aqua Tray · Casa de Campo" },
{ src: "assets/photos/tray-charcuterie.jpg", label: "02 · Charcuterie", crop: "Floating service · golden hour" },
{ src: "assets/photos/tray-aerial-water.jpg", label: "03 · Aerial", crop: "Stillwater shot" },
{ src: "assets/photos/tray-turquoise-woman.jpg", label: "04 · Turquoise", crop: "Cap Cana · woman & tray" },
{ src: "assets/photos/tray-turquoise-breakfast.jpg", label: "05 · Breakfast", crop: "Morning ritual" },
{ src: "assets/photos/tray-aerial-sun.jpg", label: "06 · Weave", crop: "HDPE poly rattan · close" },
],
dim: "50 × 35 cm",
colors: [
{ id: "natural", name: t("color.natural"), hex: "#D9C29B" },
{ id: "sand", name: t("color.sand"), hex: "#EFE3D0" },
{ id: "ocean", name: t("color.ocean"), hex: "#1A2E35" },
{ id: "teal", name: t("color.teal"), hex: "#3AA4AF" },
{ id: "ivory", name: t("color.ivory"), hex: "#F5EDE0" },
],
basePrice: 13500,
features: [t("feat.tray.0"), t("feat.tray.1"), t("feat.tray.2"), t("feat.tray.3")],
ideal: [t("ideal.hotels"), t("ideal.resorts"), t("ideal.villas"), t("ideal.beachclubs")],
},
{
id: "aqua-box",
name: t("p.box.name"),
tagline: t("p.box.tagline"),
desc: t("p.box.desc"),
tone: "ocean",
photo: "assets/photos/box-towel-woman.jpg",
photoAlt: "assets/photos/box-poolside.jpg",
gallery: [
{ src: "assets/photos/box-towel-woman.jpg", label: "01 · Morning swim", crop: "Aqua Box · the ritual" },
{ src: "assets/photos/box-poolside.jpg", label: "02 · Terrace", crop: "Pool deck · stripe towel" },
{ src: "assets/photos/box-woman-open.jpg", label: "03 · Open", crop: "Loading folded linens" },
{ src: "assets/photos/box-towel-load.jpg", label: "04 · Load", crop: "Capacity & weave" },
{ src: "assets/photos/box-towel-drape.jpg", label: "05 · Drape", crop: "Lid detail · linen drape" },
{ src: "assets/photos/box-product.jpg", label: "06 · Studio", crop: "Studio finish" },
],
dim: "70 × 50 × 50 cm",
colors: [
{ id: "natural", name: t("color.natural"), hex: "#D9C29B" },
{ id: "ocean", name: t("color.ocean"), hex: "#1A2E35" },
{ id: "graphite",name: t("color.graphite"), hex: "#3D4A50" },
{ id: "ivory", name: t("color.ivory"), hex: "#F5EDE0" },
],
basePrice: 17500,
features: [t("feat.box.0"), t("feat.box.1"), t("feat.box.2"), t("feat.box.3")],
ideal: [t("ideal.poolDecks"), t("ideal.spa"), t("ideal.terraceLounges"), t("ideal.beachfronts")],
},
{
id: "aqua-bar",
name: t("p.bar.name"),
tagline: t("p.bar.tagline"),
desc: t("p.bar.desc"),
tone: "teal",
photo: "assets/photos/bar-poolside-woman.jpg",
photoAlt: "assets/photos/bar-full-trays.jpg",
gallery: [
{ src: "assets/photos/bar-poolside-woman.jpg", label: "01 · Sunset", crop: "Aqua Bar · Punta Cana" },
{ src: "assets/photos/bar-archway.jpg", label: "02 · Archway", crop: "Stone vestibule" },
{ src: "assets/photos/bar-detail.jpg", label: "03 · Drinks", crop: "Service detail" },
{ src: "assets/photos/bar-full-trays.jpg", label: "04 · Full set", crop: "Bar + matching trays" },
{ src: "assets/photos/bar-product.jpg", label: "05 · Studio", crop: "Open bar configuration" },
],
dim: "160 × 60 × 105 cm",
colors: [
{ id: "natural", name: t("color.natural"), hex: "#D9C29B" },
{ id: "ocean", name: t("color.ocean"), hex: "#1A2E35" },
{ id: "teal", name: t("color.teal"), hex: "#3AA4AF" },
{ id: "ivory", name: t("color.ivory"), hex: "#F5EDE0" },
],
basePrice: 27500,
features: [t("feat.bar.0"), t("feat.bar.1"), t("feat.bar.2"), t("feat.bar.3")],
ideal: [t("ideal.luxuryVillas"), t("ideal.resortPools"), t("ideal.rooftopBars"), t("ideal.eventSpaces")],
},
];
}
function getStats(t) {
return [
{ num: "10+", label: t("stats.0.label") },
{ num: "100%", label: t("stats.1.label") },
{ num: "0kg", label: t("stats.2.label") },
{ num: "48h", label: t("stats.3.label") },
];
}
function ProductCard({ product, num, navigate, t }) {
return (
navigate(`/product/${product.id}`)}
role="button"
tabIndex={0}
onKeyDown={(e) => { if (e.key === "Enter") navigate(`/product/${product.id}`); }}
>
{product.name}
{num}
{product.desc}
RD$ {product.basePrice.toLocaleString("en-US")}
{t("common.view")} →
);
}
// ---------- Caribbean Map ----------
function CaribbeanMap({ mini = false }) {
const { t } = useLang();
const [active, setActive] = useState(null);
const locations = [
{ id: "sd", name: t("map.sd"), x: 38, y: 62 },
{ id: "pc", name: t("map.pc"), x: 56, y: 58 },
{ id: "puj", name: t("map.lr"), x: 48, y: 64 },
{ id: "stm", name: t("map.sm"), x: 50, y: 48 },
{ id: "pop", name: t("map.pp"), x: 35, y: 40 },
{ id: "sju", name: t("map.sju"), x: 82, y: 56 },
{ id: "mtj", name: t("map.mtj"), x: 12, y: 76 },
{ id: "nas", name: t("map.nas"), x: 22, y: 18 },
{ id: "ctw", name: t("map.ctw"), x: 22, y: 50 },
];
return (
{locations.map((loc) => {
if (mini && !["sd","pc","sju","mtj","nas"].includes(loc.id)) return null;
return (
setActive(loc.id)}
onMouseLeave={() => setActive(null)}
aria-label={loc.name}
/>
{active === loc.id && (
{loc.name}
)}
);
})}
);
}
// Backward-compat exports — other pages import PRODUCTS at module-eval time,
// but we now want them to call getProducts(t). Keep PRODUCTS for any legacy
// reference but pre-fill with English so it doesn't crash if used.
const PRODUCTS_EN = (function () {
const tEN = (k) => (DICT.en[k] !== undefined ? DICT.en[k] : k);
return getProducts(tEN);
})();
Object.assign(window, { PageHome, PRODUCTS: PRODUCTS_EN, getProducts, getStats, ProductCard, CaribbeanMap });