/* ========================================================================= shared.jsx — site-wide shell: helpers, Header, Footer, PageApp Loaded on every page before the page script. ========================================================================= */ /* ---------- image asset paths ---------- */ const IMAGE_ASSETS = Object.freeze({ logo: "assets/images/liveons-logo.svg", heroMain: "assets/images/liveons-hero-main.jpg", heroPoster: "assets/images/liveons-hero-poster.jpg", serviceInfluence: "assets/images/liveons-service-influence.jpg", servicePromotion: "assets/images/liveons-service-promotion.jpg", serviceSystem: "assets/images/liveons-service-system.jpg", representativePortrait: "assets/images/liveons-representative-portrait.jpg", }); /* ---------- video asset paths ---------- */ const VIDEO_ASSETS = Object.freeze({ heroMain: "assets/videos/liveons-hero-main.mp4", heroMainMobile: "assets/videos/liveons-hero-main-mobile.mp4", }); /* ---------- tiny helpers ---------- */ const Ph = ({ label, className = "", dark = false, style, src, alt, position = "center" }) => { const [failed, setFailed] = React.useState(false); const showImage = !!src && !failed; return (
{showImage && ( {alt setFailed(true)} /> )}
); }; const Logo = ({ footer = false }) => ( LIVEONS ); const Arrow = () => ( ); /* ---------- social icons ---------- */ const IconYT = () => (); const IconIG = () => (); const IconTT = () => (); const NAV = [ ["事業内容", "services.html"], ["代表メッセージ", "message.html"], ["会社概要", "company.html"], ]; /* ---------- Header ---------- */ function Header({ active = "" }) { const [scrolled, setScrolled] = React.useState(false); const [onDark, setOnDark] = React.useState(false); const [menu, setMenu] = React.useState(false); React.useEffect(() => { const hero = document.getElementById("hero-sentinel"); const onScroll = () => { setScrolled(window.scrollY > 30); if (hero) setOnDark(hero.dataset.dark === "1" && window.scrollY < window.innerHeight - 120); else setOnDark(false); }; onScroll(); window.addEventListener("scroll", onScroll, { passive: true }); return () => window.removeEventListener("scroll", onScroll); }, []); React.useEffect(() => { if (!menu) return; const previous = document.body.style.overflow; document.body.style.overflow = "hidden"; return () => { document.body.style.overflow = previous; }; }, [menu]); const close = () => setMenu(false); return (
); } /* ---------- Footer ---------- */ function Footer() { const cols = [["事業内容", "services.html"], ["代表メッセージ", "message.html"], ["会社概要", "company.html"], ["お問い合わせ", "contact.html"]]; return ( ); } /* ---------- shared CTA band ---------- */ function ContactCTA() { return (

次の挑戦を、一緒に。

事業に関するご相談、取材・協業のお問い合わせはお気軽にどうぞ。担当者よりスピード感を持ってご返信いたします。

お問い合わせフォームへ info@live-ons.com
); } /* ---------- hooks ---------- */ function useReveal(dep) { React.useEffect(() => { const els = document.querySelectorAll(".reveal:not(.in)"); if (!("IntersectionObserver" in window)) { els.forEach(e => e.classList.add("in")); return; } const io = new IntersectionObserver((ents) => { ents.forEach(en => { if (en.isIntersecting) { en.target.classList.add("in"); io.unobserve(en.target); } }); }, { threshold: 0.12, rootMargin: "0px 0px -8% 0px" }); els.forEach(e => io.observe(e)); return () => io.disconnect(); }, [dep]); } /* ---------- PageApp: wraps a page in shell ---------- */ function PageApp({ active, children }) { useReveal(true); return ( <>
{children}