// Japanese page: header, pinned horizontal hero, floating consultation button. // Same structure and behaviour as ../hero.jsx; copy from the copy deck (N-*, H-*). const NAV_ITEMS = [ ['サービス', '#solutions'], ['事例', '#work'], ['進め方', '#process'], ['選ばれる理由', '#why'], ['よくある質問', '#faq'], ]; function Header() { const [open, setOpen] = React.useState(false); const headerRef = React.useRef(null); const lastY = React.useRef(0); const menuBtnRef = React.useRef(null); useScrollFrame(() => { const el = headerRef.current; if (!el) return; const y = window.scrollY; el.classList.toggle('is-solid', y > 24); const goingDown = y > lastY.current + 4; const goingUp = y < lastY.current - 4; if (goingDown && y > 240) el.classList.add('is-hidden'); if (goingUp || y < 240) el.classList.remove('is-hidden'); lastY.current = y; }); // is-solid / is-hidden are toggled imperatively above, so is-open is too: // a React-managed className would wipe them on every menu toggle. React.useLayoutEffect(() => { const el = headerRef.current; if (!el) return; el.classList.toggle('is-open', open); if (open) el.classList.remove('is-hidden'); }, [open]); React.useEffect(() => { document.body.classList.toggle('menu-open', open); if (!open) return undefined; const onKey = (e) => { if (e.key === 'Escape') { setOpen(false); menuBtnRef.current && menuBtnRef.current.focus(); } }; const onResize = () => window.innerWidth > 1023 && setOpen(false); window.addEventListener('keydown', onKey); window.addEventListener('resize', onResize); return () => { window.removeEventListener('keydown', onKey); window.removeEventListener('resize', onResize); }; }, [open]); const close = () => setOpen(false); return (
Next Horizon 無料見積 無料見積
); } // Headline phrases. The pill (a switch whose rising-sun knob slides from off to on) is inserted after PILL_AFTER phrases. const HERO_WORDS = ['アイデアを、', '現実に。']; const PILL_AFTER = 1; function Hero() { const reduced = useReducedMotion(); const sectionRef = React.useRef(null); const trackRef = React.useRef(null); const pillRef = React.useRef(null); const wordRefs = React.useRef([]); useScrollFrame(() => { const section = sectionRef.current; const track = trackRef.current; if (!section || !track) return; if (reduced) { track.style.transform = ''; if (pillRef.current) pillRef.current.style.removeProperty('--roll'); return; } const p = pinProgress(section); const vw = window.innerWidth; const distance = Math.max(0, track.scrollWidth - vw); // Hold briefly at the start, then slide the whole line through. const t = clamp01((p - 0.04) / 0.86); track.style.transform = `translate3d(${(-distance * t).toFixed(1)}px,0,0)`; if (pillRef.current) pillRef.current.style.setProperty('--roll', clamp01((p - 0.02) / 0.26).toFixed(3)); const threshold = vw * 0.78; wordRefs.current.forEach((w) => { if (!w) return; w.classList.toggle('is-on', w.getBoundingClientRect().left < threshold); }); }, [reduced]); const parts = []; HERO_WORDS.forEach((w, i) => { parts.push( (wordRefs.current[i] = el)} className="hw">{w}); if (i === PILL_AFTER - 1) { parts.push( , ); } }); return (

{HERO_WORDS.join('')}

まず無料見積 事例

AIとカスタムソフトウェア開発。業務システム、ダッシュボード、顧客ポータル、業務の自動化、AIツールなどを、4〜8週間で本番稼働まで。

); } // Pill that follows the visitor after the hero and gets out of the way near the consultation/CTA/footer. function FloatingQuote() { const ref = React.useRef(null); useScrollFrame(() => { const el = ref.current; if (!el) return; const vh = window.innerHeight; const inView = (id) => { const node = document.getElementById(id); if (!node) return false; const r = node.getBoundingClientRect(); return r.top < vh && r.bottom > 0; }; const about = document.getElementById('about'); const pastIntro = about ? about.getBoundingClientRect().bottom < vh * 0.6 : false; const blocked = ['quote', 'contact', 'footer'].some(inView); const menuOpen = document.body.classList.contains('menu-open'); el.classList.toggle('is-visible', pastIntro && !blocked && !menuOpen); // Swap to ink while the button sits over the accent band. const sol = document.getElementById('solutions'); const r = sol ? sol.getBoundingClientRect() : null; el.classList.toggle('on-accent', !!r && r.top < vh - 40 && r.bottom > vh - 40); }); return (
まずは無料見積
); } Object.assign(window, { Header, Hero, FloatingQuote });