/* ============ DevTools easter egg ============ */ console.log( "%cHASHBIT%c 何が気になるんですか?お問い合わせから気になっていることを教えてくださいね👀", "background:#0c0c0b;color:#7ae800;padding:4px 7px;border-radius:4px;font-weight:700;letter-spacing:.06em", "color:#7ae800;font-size:14px;font-weight:700" ); /* ============ Sound ============ */ let _ac = null; function _ctx() { if (!_ac) _ac = new (window.AudioContext || window.webkitAudioContext)(); if (_ac.state === "suspended") _ac.resume(); return _ac; } function playClick(freq = 880, dur = 0.035, vol = 0.12) { try { const ac = _ctx(); const o = ac.createOscillator(); const g = ac.createGain(); o.connect(g); g.connect(ac.destination); o.type = "sine"; o.frequency.setValueAtTime(freq, ac.currentTime); o.frequency.exponentialRampToValueAtTime(freq * 0.6, ac.currentTime + dur); g.gain.setValueAtTime(vol, ac.currentTime); g.gain.exponentialRampToValueAtTime(0.001, ac.currentTime + dur); o.start(ac.currentTime); o.stop(ac.currentTime + dur + 0.01); } catch (_) {} } document.addEventListener("click", (e) => { const el = e.target.closest("button, a[href], a[onclick], .work-card, .filter-chip, .menu-btn"); if (!el) return; if (el.matches(".work-card")) playClick(660, 0.05, 0.10); else if (el.matches(".filter-chip")) playClick(1100, 0.025, 0.09); else if (el.matches(".wd-back")) playClick(440, 0.06, 0.10); else if (el.matches(".btn.accent")) playClick(920, 0.04, 0.14); else playClick(800, 0.035, 0.11); }, true); /* ============ Nav + App shell ============ */ const HAS_NEWS = ((window.HASHBIT_DATA && window.HASHBIT_DATA.news) || []).some(n => n.published !== false); const NAV = [ ["home","Home","HOME"], ["works","Works","WORKS"], ...(HAS_NEWS ? [["news","News","NEWS"]] : []), ["member","Member","MEMBER"],["company","Company","COMPANY"] ]; function Nav({ active, go }) { const [solid, setSolid] = useState(false); const [open, setOpen] = useState(false); useEffect(() => { const onS = () => setSolid(window.scrollY > 60); onS(); window.addEventListener("scroll", onS, { passive: true }); return () => window.removeEventListener("scroll", onS); }, []); const nav = (id) => { setOpen(false); go(id); }; return (
{[...NAV, ["contact","Contact","CONTACT"]].map(n => ( nav(n[0])}>{n[1]}{n[2]} ))}
); } function getWorkFromUrl(works) { if (window.location.pathname.replace(/\/$/, "") !== "/works") return null; const params = new URLSearchParams(window.location.search); const id = params.get("id") || params.get("work"); if (!id) return null; if (/^\d+$/.test(id)) return works[Number(id) - 1] || null; return works.find(w => w.id === id) || null; } function workUrl(work) { return `/works?id=${encodeURIComponent(work.id)}`; } function App() { const [active, setActive] = useState("home"); const worksData = (window.HASHBIT_DATA && window.HASHBIT_DATA.works) || []; const [openWork, setOpenWork] = useState(() => getWorkFromUrl(worksData)); const [worksSel, setWorksSel] = useState("All"); const go = (id) => { if (window.location.pathname.replace(/\/$/, "") === "/works") { window.history.pushState({ work: null }, "", id === "home" ? "/" : `/#${id}`); setOpenWork(null); setTimeout(() => go(id), 30); return; } const el = document.getElementById(id); if (el) window.scrollTo({ top: id === "home" ? 0 : el.offsetTop - 40, behavior: "smooth" }); }; useEffect(() => { const ids = ["home", "works", ...(HAS_NEWS ? ["news"] : []), "member","company","contact"]; const io = new IntersectionObserver((ents) => { ents.forEach(e => { if (e.isIntersecting) setActive(e.target.id); }); }, { rootMargin: "-45% 0px -50% 0px" }); ids.forEach(id => { const el = document.getElementById(id); if (el) io.observe(el); }); return () => io.disconnect(); }, []); useEffect(() => { const syncRoute = () => { const w = getWorkFromUrl(worksData); setOpenWork(w); if (w) setTimeout(() => window.scrollTo({ top: 0, behavior: "auto" }), 0); else if (window.location.hash) setTimeout(() => go(window.location.hash.slice(1)), 0); }; window.addEventListener("popstate", syncRoute); syncRoute(); return () => window.removeEventListener("popstate", syncRoute); }, []); const openWorkDetail = (work) => { setOpenWork(work); window.history.pushState({ work: work.id }, "", workUrl(work)); window.scrollTo({ top: 0, behavior: "auto" }); }; const closeWorkDetail = () => { if (window.history.state && window.history.state.work && window.location.pathname.replace(/\/$/, "") === "/works") { window.history.back(); return; } window.history.replaceState({ work: null }, "", "/#works"); setOpenWork(null); setTimeout(() => go("works"), 30); }; const focusWork = (id) => { setWorksSel("All"); requestAnimationFrame(() => { requestAnimationFrame(() => { const el = document.querySelector(`[data-work-id="${id}"]`); if (!el) return; el.scrollIntoView({ behavior: "smooth", block: "center" }); el.classList.add("flash"); setTimeout(() => el.classList.remove("flash"), 900); }); }); }; return (