// Hero — 4 layouts (mega / split / stamp / ticker)
// Exports window.Hero, window.Header, window.Marquee
const { useEffect, useState, useRef } = React;
// ---------- helper: render JSX from data tokens like {red: "x"} ----------
function renderTokens(arr, accentClass = "red") {
return arr.map((tok, i) => {
if (typeof tok === "string") return {tok};
if (tok.red) return {tok.red};
if (tok.yellow) return {tok.yellow};
return null;
});
}
// ---------- Header ----------
function Header({ content, lang, setLang, scrolled }) {
const [menuOpen, setMenuOpen] = useState(false);
return (
);
}
// ---------- Marquee strip ----------
function Marquee({ words, variant = "default" }) {
const items = [];
for (let i = 0; i < 4; i++) {
words.forEach((w, j) => {
items.push(
{w}
);
});
}
return (
);
}
// ---------- Hero ----------
function Hero({ content, layout, lang, parallaxY }) {
const heroRef = useRef(null);
const photosRef = useRef(null);
// Random crossfading food-photo backdrop
useEffect(() => {
const wrap = photosRef.current;
if (!wrap) return;
const slides = Array.prototype.slice.call(wrap.children);
if (!slides.length) return;
const reduced = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
let cur = Math.floor(Math.random() * slides.length);
slides[cur].classList.add("show");
if (reduced) return;
const id = setInterval(function () {
let next = Math.floor(Math.random() * slides.length);
if (next === cur) next = (cur + 1) % slides.length;
slides[cur].classList.remove("show");
slides[next].classList.add("show");
cur = next;
}, 5200);
return function () { clearInterval(id); };
}, []);
const PHOTOS = ["assets/hero-1.jpg", "assets/hero-2.jpg", "assets/hero-3.jpg", "assets/hero-4.jpg"];
return (
{PHOTOS.map((p, i) => (
))}
{layout === "mega" && }
{layout === "split" && }
{layout === "stamp" && }
{layout === "ticker" && }
);
}
function HeroCTAs({ content }) {
return (
);
}
// MEGA — big centered type, stickers floating around with parallax
function HeroMega({ content, lang, parallaxY }) {
const words = content.hero.titleWords;
return (
{content.hero.eyebrow &&
{content.hero.eyebrow}
}
{words.map((w, i) => {
const cls = i === 0 ? "word red" : i === words.length - 1 ? "word outline" : "word";
return {w};
})}
{content.hero.sub}
);
}
// SPLIT — text left, image stack right
function HeroSplit({ content, lang, parallaxY }) {
const words = content.hero.titleWords;
return (
{content.hero.eyebrow &&
{content.hero.eyebrow}
}
{words.map((w, i) => (
{i === 1 ? {w} : i === words.length - 1 ? {w} : w}
{i < words.length - 1 && (lang === "en" ? " " : "")}
{i === 0 || i === 2 ?
: null}
))}
{content.hero.sub}
);
}
// STAMP — overlapping rotated kanji words
function HeroStamp({ content, lang, parallaxY }) {
const words = content.hero.titleWords;
return (
{content.hero.eyebrow &&
{content.hero.eyebrow}
}
{words[0]}
{words[2] || words[1]}
{words[3] || words[2]}
{content.hero.stickers[1].text}
);
}
// TICKER — three rows of giant scrolling display type
function HeroTicker({ content, lang, parallaxY }) {
const words = content.hero.titleWords;
// build a row of repeating words with separators
const buildRow = (text, sep = "★", repeat = 8) => {
const items = [];
for (let i = 0; i < repeat; i++) {
items.push({text});
items.push({sep});
}
return items;
};
return (
{content.hero.eyebrow &&
{content.hero.eyebrow}
}
{buildRow(words[0])}{buildRow(words[0])}
{buildRow(words[2] || words[1])}{buildRow(words[2] || words[1])}
{buildRow(words[3] || words[2])}{buildRow(words[3] || words[2])}
);
}
Object.assign(window, {
Header,
Hero,
Marquee,
renderTokens,
});