// sections.jsx
// stellare bonin — all section components.
// Exports to window so app.jsx can pick them up.

const { useState, useEffect, useRef, useMemo } = React;

// ─── Hooks ──────────────────────────────────────────────────────────────────

function useScrollY() {
  const [y, setY] = useState(typeof window !== "undefined" ? window.scrollY : 0);
  useEffect(() => {
    let raf = 0;
    const onScroll = () => {
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => setY(window.scrollY));
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => {
      window.removeEventListener("scroll", onScroll);
      cancelAnimationFrame(raf);
    };
  }, []);
  return y;
}

function useParallax(ref, speed = 0.2) {
  const y = useScrollY();
  const [offset, setOffset] = useState(0);
  useEffect(() => {
    if (!ref.current) return;
    const rect = ref.current.getBoundingClientRect();
    const vh = window.innerHeight;
    // distance from element center to viewport center, normalised
    const center = rect.top + rect.height / 2 - vh / 2;
    setOffset(-center * speed);
  }, [y, speed, ref]);
  return offset;
}

// Phase 3.H: スクロールアニメ修復。threshold 0.18 → 0.1、rootMargin -40px で発火を早める。
function useReveal(threshold = 0.1) {
  const ref = useRef(null);
  const [shown, setShown] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((e) => {
          if (e.isIntersecting) {
            setShown(true);
            io.disconnect();
          }
        });
      },
      { threshold, rootMargin: '0px 0px -40px 0px' }
    );
    io.observe(ref.current);
    return () => io.disconnect();
  }, [threshold]);
  return [ref, shown];
}

// ─── Image helpers ──────────────────────────────────────────────────────────

const IMG = {
  // Stories
  bonin: "assets/stories/bonin-blue.webp",
  // Phase 3.E: ホエール画像（尾びれだけ）が不評のため Unsplash クジラ全身画像に差し戻し
  whale: "https://images.unsplash.com/photo-1568430462989-44163eb1752f?w=1200&h=1500&fit=fill&fill=blur&q=80&auto=format",
  stars: "assets/stories/rooftop-deck.webp",        // Stories No.03 用（重複解消）
  // Bleed
  coast: "assets/manifesto/ferry-funnel.webp",
  // Suites
  villa1: "assets/suites/ocean-villa.webp",
  villa2: "assets/suites/sky-suite.webp",
  villa3: "assets/suites/cetacean-pavilion.webp",
  villa4: "assets/suites/crest-residence.webp",
  // Dining
  dining1: "assets/dining/so-counter.webp",
  dining2: "assets/dining/lapa-terrace.webp",
  dining3: "assets/dining/nocturne.webp",
  // Activities
  snorkel: "assets/activities/snorkel.webp",
  // Phase 3.E: ドルフィン画像（イルカが欠けて見える）が不評のため Unsplash イルカ全体画像に差し戻し
  dolphin: "https://images.unsplash.com/photo-1607153333879-c174d265f1d2?w=1600&q=80&auto=format&fit=crop",
  stars_deck: "assets/activities/stargazing-deck.webp",  // Activities 04 用（Stories とは別画像）
  kayak: "assets/activities/kayak.webp",
  forest: "assets/activities/forest.webp",
  spa: "assets/activities/spa.webp"
};

// ─── Nav ─────────────────────────────────────────────────────────────────────

function Nav() {
  const y = useScrollY();
  // M3: Hero の高さ（window.innerHeight）を超えたらソリッドへ
  const solid = y > window.innerHeight;

  // Phase 5.1 (C5): Easter Egg — ロゴ 5 連打で制作プロセスモーダルを開く
  const [eggCount, setEggCount] = useState(0);
  const [eggOpen, setEggOpen] = useState(false);
  const eggTimerRef = useRef(null);

  // Phase 3.X: モバイルハンバーガーメニュー
  const [menuOpen, setMenuOpen] = useState(false);

  const handleBrandClick = () => {
    const next = eggCount + 1;
    if (next >= 5) {
      setEggOpen(true);
      setEggCount(0);
      if (eggTimerRef.current) clearTimeout(eggTimerRef.current);
      return;
    }
    setEggCount(next);
    // 2 秒以内に次のクリックが無ければカウンタをリセット
    if (eggTimerRef.current) clearTimeout(eggTimerRef.current);
    eggTimerRef.current = setTimeout(() => setEggCount(0), 2000);
  };

  // メニュー開閉
  const toggleMenu = () => setMenuOpen((v) => !v);
  const closeMenu = () => setMenuOpen(false);

  // body スクロールロック（メニュー開時）
  useEffect(() => {
    if (menuOpen) {
      const prev = document.body.style.overflow;
      document.body.style.overflow = "hidden";
      return () => { document.body.style.overflow = prev; };
    }
  }, [menuOpen]);

  // ESC キーで閉じる
  useEffect(() => {
    const onEsc = (e) => { if (e.key === "Escape") closeMenu(); };
    window.addEventListener("keydown", onEsc);
    return () => window.removeEventListener("keydown", onEsc);
  }, []);

  return (
    <>
      <nav className={"nav " + (solid ? "nav--solid" : "") + (menuOpen ? " nav--menu-open" : "")}>
        <div className="nav__brand" onClick={handleBrandClick}>
          <span>stellare</span>
          <b>bonin · 父島</b>
        </div>
        <div className="nav__links">
          <a href="#prologue">物語</a>
          <a href="#access">アクセス</a>
          <a href="#suites">客室</a>
          <a href="#dining">食</a>
          <a href="#activities">体験</a>
          <a href="#faq">よくある質問</a>
        </div>
        <a className="nav__cta" href="#reserve">Reserve</a>
        {/* Phase 3.X: ハンバーガーボタン（モバイル時のみ表示） */}
        <button
          type="button"
          className={"nav__hamburger" + (menuOpen ? " is-open" : "")}
          onClick={toggleMenu}
          aria-label={menuOpen ? "メニューを閉じる" : "メニューを開く"}
          aria-expanded={menuOpen}
          aria-controls="mobile-menu">

          <span></span>
          <span></span>
          <span></span>
        </button>
      </nav>

      {/* Phase 3.X: モバイル全画面メニュー */}
      <div
        id="mobile-menu"
        className={"mobile-menu" + (menuOpen ? " is-open" : "")}
        aria-hidden={!menuOpen}
        onClick={(e) => { if (e.target.classList.contains("mobile-menu")) closeMenu(); }}>

        <nav className="mobile-menu__nav">
          <a href="#prologue" onClick={closeMenu}>物語</a>
          <a href="#access" onClick={closeMenu}>アクセス</a>
          <a href="#suites" onClick={closeMenu}>客室</a>
          <a href="#dining" onClick={closeMenu}>食</a>
          <a href="#activities" onClick={closeMenu}>体験</a>
          <a href="#faq" onClick={closeMenu}>よくある質問</a>
          <a href="#reserve" onClick={closeMenu} className="mobile-menu__cta">Reserve</a>
        </nav>
        <div className="mobile-menu__footer">
          <em>— at the quiet edge of the Pacific —</em>
        </div>
      </div>

      {eggOpen && <EasterEggModal onClose={() => setEggOpen(false)} />}
    </>);

}

// ─── Phase 5.1 (C5): Easter Egg Modal ────────────────────────────────────────

function EasterEggModal({ onClose }) {
  useEffect(() => {
    const onEsc = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onEsc);
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => {
      window.removeEventListener("keydown", onEsc);
      document.body.style.overflow = prevOverflow;
    };
  }, [onClose]);

  return (
    <div
      className="egg-overlay"
      onClick={(e) => { if (e.target.classList.contains("egg-overlay")) onClose(); }}
      role="dialog"
      aria-modal="true"
      aria-labelledby="egg-title">

      <div className="egg-modal">
        <button
          type="button"
          className="egg-modal__close"
          onClick={onClose}
          aria-label="閉じる">×</button>
        <div className="egg-modal__eyebrow">— Behind the curtain —</div>
        <h3 className="egg-modal__title" id="egg-title">
          stellare bonin について<br />
          <em>About this portfolio</em>
        </h3>
        <div className="egg-modal__body">
          <p>
            このサイトは、Luna（インフラエンジニア・AI実験者）と Claude
            （Anthropic のAI）が協働で作ったポートフォリオ作品です。
            <em>stellare bonin</em> は架空のリゾートですが、
            設計の問いは実在します—
          </p>
          <p style={{ marginTop: 18 }}>
            サイトに「必然性」を感じさせるには？<br />
            嘘をつかないコピーをどう書くか？<br />
            写真に語らせるには？
          </p>
          <p style={{ marginTop: 18, opacity: 0.7 }}>
            生成AIと人間の協働で、商業レベルのラグジュアリーホテルLPが
            どこまで成立するかの実験記録です。
          </p>
        </div>
        <div className="egg-modal__footer">
          <span>— Luna, 2026</span>
          <span><em>Designed with Claude</em></span>
        </div>
      </div>
    </div>);

}

// ─── Hero ────────────────────────────────────────────────────────────────────

const HERO_SLIDES = [
  { src: "assets/hero/cove-overlook.webp",  caption: ["The Pacific Horizon · 27°N · May 2026", "South Cape, 14:08 JST"] },
  { src: "assets/hero/bay-overlook.webp",   caption: ["The Bonin Blue Bay · 27°N · June 2026", "Sakaiura, 09:18 JST"] },
  { src: "assets/hero/coral-staghorn.webp", caption: ["The Staghorn Garden · 27°N · April 2026", "−4 m, 11:02 JST"] },
  { src: "assets/hero/milkyway.webp",       caption: ["The Milky Way · 27°N · December 2026", "Chichijima, 23:47 JST"] },
];

function Hero() {
  const mediaRef = useRef(null);
  const offset = useParallax(mediaRef, 0.18);
  const [active, setActive] = useState(0);
  const [pause, setPause] = useState(false);

  useEffect(() => {
    if (pause) return;
    const id = setInterval(() => {
      setActive((i) => (i + 1) % HERO_SLIDES.length);
    }, 6500);
    return () => clearInterval(id);
  }, [pause]);

  const current = HERO_SLIDES[active];
  const pad = (n) => String(n).padStart(2, "0");

  return (
    <header className="hero" id="top">
      <div className="hero__media" ref={mediaRef} style={{ transform: `translate3d(0, ${offset}px, 0) scale(1.08)` }}>
        {HERO_SLIDES.map((s, i) =>
          /* Phase 3.E: 枝珊瑚（スライド3）は小笠原素材として静止画で残す。Phase 3.A の動画化は取りやめ */
          /* Phase 3.N: 1 枚目のみ eager+fetchpriority=high、2-4 枚目は lazy で document_idle 詰まり解消 */
          <img
            key={s.src}
            src={s.src}
            alt={s.caption[0]}
            className={"hero__slide " + (i === active ? "is-active" : "")}
            loading={i === 0 ? "eager" : "lazy"}
            decoding="async"
            fetchpriority={i === 0 ? "high" : "auto"}
          />
        )}
      </div>
      <div className="hero__veil" />
      <div className="hero__grain" aria-hidden="true" />
      <div className="hero__frame" aria-hidden="true" />

      <div className="hero__inner">
        <div className="hero__top">
          <div className="hero__eye">
            <span className="hero__mark-rule" />
            <span>est. mmxxvi</span><i>·</i><span>chichijima</span><i>·</i><span>ogasawara</span>
            <span className="hero__mark-rule" />
          </div>
        </div>

        <div className="hero__center">
          <div className="hero__overline">— at the quiet edge of the Pacific —</div>
          <h1 className="hero__title">
            静けさが、<br />
            <span className="hero__title-em">ひとつの贅沢になる。</span>
          </h1>
          <p className="hero__sub">
            <em>Silence, the rarest luxury.</em>
          </p>
        </div>

        <div className="hero__foot">
          <div className="hero__foot-l">
            <span className="hero__foot-lbl">— Coordinates —</span>
            <span className="hero__foot-val">27.094° N · 142.197° E</span>
            <small>東京から、24時間の船旅の果てに。</small>
          </div>

          <div className="hero__foot-c">
            <div className="hero__counter" aria-live="polite">
              <span className="hero__counter-now">{pad(active + 1)}</span>
              <span className="hero__counter-sep">⁄</span>
              <span className="hero__counter-tot">{pad(HERO_SLIDES.length)}</span>
            </div>
            <div className="hero__bar" role="tablist" aria-label="Hero slideshow">
              {HERO_SLIDES.map((s, i) => {
                const state = i < active ? "is-past" : i === active ? "is-now" : "";
                return (
                  <button
                    key={s.src}
                    type="button"
                    role="tab"
                    aria-selected={i === active}
                    aria-label={s.caption[0]}
                    className={"hero__bar-seg " + state}
                    onClick={() => { setActive(i); setPause(true); }}
                  >
                    <span
                      key={"fill-" + active + "-" + i + "-" + (pause ? "p" : "r")}
                      className={"hero__bar-fill " + state + (pause ? " is-paused" : "")}
                    />
                  </button>
                );
              })}
            </div>
            <div className="hero__scroll">SCROLL</div>
          </div>

          <div className="hero__foot-r">
            <span className="hero__foot-lbl">— Now Viewing —</span>
            <span className="hero__foot-val">{current.caption[0]}</span>
            <small>{current.caption[1]}</small>
          </div>
        </div>
      </div>
    </header>);

}

// ─── Prologue ────────────────────────────────────────────────────────────────

function Prologue() {
  const [ref, shown] = useReveal();
  return (
    <section className="section prologue" id="prologue" ref={ref}>
      <div className="wrap-sm">
        <div className={"prologue__no reveal " + (shown ? "in" : "")}>— Prologue · 序 —</div>
        <p className={"prologue__lede reveal " + (shown ? "in" : "")} style={{ "--rd": "120ms" }}>
          飛行機の届かない場所に、<br />
          いま、ひとつの島がある。
        </p>
        <div className={"divider reveal " + (shown ? "in" : "")} style={{ marginTop: 56, marginBottom: 56, "--rd": "240ms" }} />
        <p className={"prologue__body reveal " + (shown ? "in" : "")} style={{ "--rd": "320ms" }}>
          東京・竹芝から、おがさわら丸でおよそ24時間。海は次第に深く、群青から、誰も名づけえぬ青へと変わってゆく。1830年、欧米から渡った最初の移住者たちが、この群島を<em style={{ fontFamily: "var(--serif-en)" }}>Bonin Islands</em>と呼んだ——「無人」を意味する、静謐な響きで。<br /><br />
          stellare bonin は、その島のもっとも遠いひと隅で、わずか十七室。海と、空と、夜のために設えた、星のかたちをした宿。
        </p>
      </div>
    </section>);

}

// ─── Story blocks ────────────────────────────────────────────────────────────

function Story({ no, en, ja, body, meta, img, caption, reverse }) {
  const [ref, shown] = useReveal();
  const mediaRef = useRef(null);
  const offset = useParallax(mediaRef, 0.08);
  // Phase 3.G: Phase 3.F の <video> 背景は撤回。静止画 <img> のみに戻した。
  // 動画ファイル（assets/hero/coral-staghorn.{webm,mp4}）は将来流用のため残置。
  // Phase 3.Z: reveal-img のクリップパス遷移を Stories 画像に対しては省略。
  //   - loading="eager" + fetchpriority="high" で先読み
  //   - className から reveal-img を外し clip-path 待機を止める（CSS 側でも保険を入れる）
  return (
    <div className={"story " + (reverse ? "story--rev" : "")} ref={ref}>
      <div className="story__media" ref={mediaRef}>
        {/* Phase 3.Z: スクロール到達と同時に画像を出すため eager + fetchpriority=high。
            reveal-img クラスは付けず、CSS 側で story__media img を opacity:1 / clip-path:inset(0) 固定 */}
        <img
          src={img}
          alt=""
          loading="eager"
          decoding="async"
          fetchpriority="high"
          style={{ transform: `translate3d(0, ${offset}px, 0) scale(1.12)` }}
          className="story__img" />

        <div className="story__cap">
          <span>{caption?.[0]}</span><span>{caption?.[1]}</span>
        </div>
      </div>
      <div className="story__text">
        <div className={"story__num reveal " + (shown ? "in" : "")}>— {no} —</div>
        <h2 className={"story__title reveal reveal-trigger " + (shown ? "in" : "")} style={{ "--rd": "120ms" }}>
          {ja}<em>{en}</em>
        </h2>
        <p className={"story__body reveal " + (shown ? "in" : "")} style={{ "--rd": "240ms" }}>
          {body}
        </p>
        {meta &&
        <div className={"story__meta reveal " + (shown ? "in" : "")} style={{ "--rd": "360ms" }}>
            {meta.map((m, i) =>
          <div key={i}>
                {m.label}<b>{m.value}</b>
              </div>
          )}
          </div>
        }
      </div>
    </div>);

}

function Stories() {
  return (
    <section className="section">
      <div className="wrap" style={{ display: "flex", flexDirection: "column", gap: "clamp(80px, 12vh, 160px)" }}>
        <Story
          no="No. 01"
          en="The Bonin Blue"
          ja="ボニンブルー、と呼ばれる青。"
          body="外洋に開かれ、川を持たないこの島の海は、透明度の高さでも、その色でも、世界に類を見ない。光は60メートル下まで届き、藍とも瑠璃とも違う独特の青——人々はそれをただ、ボニンブルーと呼ぶ。テラスから、桟橋から、ヴィラのバスタブから。一日のどの時間に出会っても、二度と同じ青はない。"
          meta={[
          { label: "Visibility", value: "60m" },
          { label: "Endemic spp.", value: "195種" }]
          }
          caption={["Minamizaki Overlook", "13:24 JST"]}
          img={IMG.bonin} />

        <Story
          no="No. 02"
          en="A Conversation with Cetaceans"
          ja="クジラと、出会う冬。"
          body="毎年12月から5月、ザトウクジラはこの海に子を産みに帰る。船の上で、波の下で、ときに客室のテラスから、彼らの息づかいに耳をすませる。イルカは年間を通して群れをなし、舳先の波を遊ぶようについてくる。生命との距離が、ここではただ近い。"
          meta={[
          { label: "Whale season", value: "12–5月" },
          { label: "Dolphin", value: "通年" }]
          }
          caption={["Humpback · 父島", "March 2026"]}
          img={IMG.whale}
          reverse />
        
        <Story
          no="No. 03"
          en="A Sky Untouched by Light"
          ja="星のためだけの、夜。"
          body="街の灯から千キロ。雲のない夜には、肉眼で天の川がはっきりと見える。屋上のステラ・デッキに、ブランケットと白ワインだけ持って横たわる——それが、ここでの夜の作法。流星群の季節には、専属のスタージャイドが、星の物語をひとつだけ、そっと囁いてくれる。"
          meta={[
          { label: "Bortle scale", value: "Class 2" },
          { label: "Best", value: "新月前後" }]
          }
          caption={["Stella Deck · Rooftop", "01:24 JST"]}
          img={IMG.stars} />
        
      </div>
    </section>);

}

// ─── Bleed quote ─────────────────────────────────────────────────────────────

function Bleed() {
  const mediaRef = useRef(null);
  const offset = useParallax(mediaRef, 0.25);
  return (
    <section className="bleed">
      <div className="bleed__media" ref={mediaRef} style={{ transform: `translate3d(0, ${offset}px, 0) scale(1.1)` }}>
        {/* Phase 3.N: lazy へ戻す */}
        <img src={IMG.coast} alt="" loading="lazy" decoding="async" />
      </div>
      <div className="bleed__veil" />
      <div className="bleed__inner">
        <p className="bleed__quote">
          遠さは、贅沢である。<br />
          <span style={{ opacity: 0.78 }}>たどり着くことすら、<br />もてなしのうちに。</span>
        </p>
        <div className="bleed__sub">— stellare bonin manifesto, 2026</div>
      </div>
    </section>);

}

// ─── Phase 4: BleedSecondary (text-only quote) ───────────────────────────────

function BleedSecondary() {
  const [ref, shown] = useReveal();
  return (
    <section className="bleed bleed--secondary" ref={ref}>
      <div className="bleed__inner">
        <p className={"bleed__quote bleed__quote--text " + (shown ? "in" : "")}>
          もっとも遠い距離は、<br />
          <span style={{ opacity: 0.78 }}>もっとも深い沈黙をつくる。</span>
        </p>
        <div className="bleed__sub">
          <em>The longest distances make the deepest silences.</em>
        </div>
      </div>
    </section>);

}

// ─── Suites ──────────────────────────────────────────────────────────────────

const SUITES = [
{
  no: "01",
  name: "ボニン・オーシャンヴィラ",
  en: "Bonin Ocean Villa",
  body: "海面から3メートル、桟橋のように突き出した独立棟。設計：黒川義信、2024年竣工。180度の眺望を切り取る木枠の窓、伝統的な土佐漆喰の壁、無垢杉の床、テラスのインフィニティ・バス。波音以外の音を、設計の段階から取り除いた一棟。",
  specs: [
  { l: "面積", v: "112 m²" },
  { l: "定員", v: "2 名" },
  { l: "眺望", v: "海・水平線" }],

  price: { jpy: "¥168,000〜", per: "/ 1泊・2名・税サ別" },
  img: IMG.villa1,
  verse: "Where the wind has no name."
},
{
  no: "02",
  name: "ステラ・スカイスイート",
  en: "Stella Sky Suite",
  body: "屋上付きのメゾネット。設計：北原透・天文物理学者の塚原修一郎を意匠監修に迎えた、二年がかりの設計。寝室の天井は電動で開き、ベッドの真上に、銀河そのものを招き入れる。専属のスタージャイドによる解説、望遠鏡、温かなブランケット——夜のためだけの全てが、ここに整っている。",
  specs: [
  { l: "面積", v: "146 m²" },
  { l: "定員", v: "2–3 名" },
  { l: "特徴", v: "屋上 / 開閉式天井" }],

  price: { jpy: "¥228,000〜", per: "/ 1泊・2名・税サ別" },
  img: IMG.villa2,
  verse: "Roof to roof with the galaxy."
},
{
  no: "03",
  name: "クジラの間",
  en: "The Cetacean Pavilion",
  body: "ザトウクジラの来訪する湾に面した、地階のヴィラ。設計：石原拓海、2025年竣工。バスルームから直接、半屋外のプールへ伸びる水面が、ボニンブルーの湾水と視覚的に連続する。湾内をクジラが横切る稀有な日には、水面に映る彼らの影とともに湯に浸かる、という旅人だけが知る贅沢を。仕上げは土佐漆喰の白壁にダークスレートの床。",
  specs: [
  { l: "面積", v: "188 m²" },
  { l: "定員", v: "2–4 名" },
  { l: "特徴", v: "半屋外プール" }],

  price: { jpy: "¥298,000〜", per: "/ 1泊・2名・税サ別" },
  img: IMG.villa3,
  verse: "At the level of the sea."
},
{
  no: "04",
  name: "クレスト・レジデンス",
  en: "The Crest Residence",
  body: "南崎を望む丘の上、独立した一邸まるごと。設計：黒川義信、stellare bonin 最初の構想図にあった、たった一邸のための邸宅。書斎、暖炉、焼杉の外壁、専属シェフ用キッチン、四方を囲むテラス。家族や、ごく親しい人たちだけの数日のために設えた、もう一つの島の家。",
  specs: [
  { l: "面積", v: "342 m²" },
  { l: "定員", v: "最大 6 名" },
  { l: "特徴", v: "一棟貸し" }],

  price: { jpy: "¥680,000〜", per: "/ 1泊・4名・税サ別" },
  img: IMG.villa4,
  verse: "Above the wind, before the dawn."
}];


// Phase 3.B: SuiteRow は v1.1 baseline へのロールバック容易性のため残置（現在は未使用）。
// 新規レイアウトは下記 SuiteFullscreen が担う。
function SuiteRow({ s, i }) {
  const [ref, shown] = useReveal();
  return (
    <article className={"suite " + (i % 2 ? "suite--rev" : "")} ref={ref}>
      <div className="suite__media">
        <img src={s.img} alt={s.name} loading="lazy" className={"reveal-img " + (shown ? "in" : "")} />
      </div>
      <div className="suite__text">
        <div className={"suite__num reveal " + (shown ? "in" : "")}>{s.no}</div>
        <h3 className={"suite__title reveal " + (shown ? "in" : "")} style={{ "--rd": "120ms" }}>
          {s.name}<em>{s.en}</em>
        </h3>
        <p className={"suite__body reveal " + (shown ? "in" : "")} style={{ "--rd": "240ms" }}>
          {s.body}
        </p>
        <div className={"suite__specs reveal " + (shown ? "in" : "")} style={{ "--rd": "320ms" }}>
          {s.specs.map((sp, k) =>
          <div key={k}>{sp.l}<b>{sp.v}</b></div>
          )}
        </div>
        <div className={"suite__price reveal " + (shown ? "in" : "")} style={{ "--rd": "400ms" }}>
          <b>{s.price.jpy}</b>
          <span>{s.price.per}</span>
        </div>
        <div style={{ marginTop: 32 }}>
          <a className="btn-line" href="#reserve">Reserve this villa</a>
        </div>
      </div>
    </article>);

}

// Phase 3.B: Aman 型「1部屋1フルスクリーン」レイアウト
function SuiteFullscreen({ s, i }) {
  const [ref, shown] = useReveal();
  const mediaRef = useRef(null);
  const offset = useParallax(mediaRef, 0.08);
  const reverse = i % 2 === 1;

  return (
    <section
      className={"suite-fs " + (reverse ? "suite-fs--rev" : "")}
      id={"suite-" + s.no}
      ref={ref}>

      <div className="suite-fs__media" ref={mediaRef}>
        {/* Phase 3.N: lazy へ戻す */}
        <img
          src={s.img}
          alt={s.name}
          loading="lazy"
          decoding="async"
          style={{ transform: `translate3d(0, ${offset}px, 0) scale(1.06)` }} />

        <div className="suite-fs__cap">
          <span>{"No. " + s.no}</span>
          <span>{s.en}</span>
        </div>
      </div>
      <div className="suite-fs__text">
        <div className={"suite-fs__num reveal " + (shown ? "in" : "")}>{s.no}</div>
        <h3 className={"suite-fs__title reveal " + (shown ? "in" : "")} style={{ "--rd": "120ms" }}>
          {s.name}<em>{s.en}</em>
        </h3>
        <p className={"suite-fs__body reveal " + (shown ? "in" : "")} style={{ "--rd": "240ms" }}>
          {s.body}
        </p>
        <p className={"suite-fs__verse reveal " + (shown ? "in" : "")} style={{ "--rd": "320ms" }}>
          <em>{s.verse}</em>
        </p>
        <div className={"suite-fs__specs reveal " + (shown ? "in" : "")} style={{ "--rd": "400ms" }}>
          {s.specs.map((sp, k) =>
          <div key={k}>{sp.l}<b>{sp.v}</b></div>
          )}
        </div>
        <div className={"suite-fs__price reveal " + (shown ? "in" : "")} style={{ "--rd": "480ms" }}>
          <b>{s.price.jpy}</b>
          <span>{s.price.per}</span>
        </div>
        <div style={{ marginTop: 32 }} className={"reveal " + (shown ? "in" : "")}>
          <a className="btn-line" href="#reserve">Reserve this villa</a>
        </div>
      </div>
    </section>);

}

function Suites() {
  return (
    <div className="suites-fs-wrap" id="suites">
      <section className="suites-fs__intro">
        <div className="wrap">
          <header className="suites__head">
            <div>
              <div className="eyebrow">— Suites · 客室 —</div>
              <h2 className="h-section reveal-trigger" style={{ marginTop: 14 }}>四つの、独立した時間。</h2>
            </div>
            <div />
            <div className="h-en" style={{ textAlign: "right" }}>
              Four villas.<br />
              <span style={{ fontStyle: "normal", fontSize: "0.5em", letterSpacing: "0.32em", textTransform: "uppercase" }}>17 suites · all oceanfront</span>
            </div>
          </header>
        </div>
      </section>
      {SUITES.map((s, i) => <SuiteFullscreen key={s.no} s={s} i={i} />)}
    </div>);

}

// ─── Dining ──────────────────────────────────────────────────────────────────

const DINING = [
{
  type: "Signature · 創作和食",
  name: "蒼 — Sō",
  en: "the depth",
  desc: "島の漁師がその朝に揚げた魚、亜熱帯の果実、本土から運ばれた発酵食。料理長・志村英人——京都『瓢亭』にて15年、東京『虎屋』にて5年。2024年より stellare bonin へ。八品の日替わりおまかせ。海を見渡すカウンター八席のみ。",
  meta: ["18:00 / 20:30", "全 8 品 ¥28,000"],
  img: IMG.dining1
},
{
  type: "All Day · 軽食",
  name: "テラス・ラパ",
  en: "lapa terrace",
  desc: "終日開いた、海に面したテラス。眼下には、刻一刻と色を変える bonin blue の湾。シェフ・宮原ナオ——リスボン『Belcanto』を経て、2023年より島に。朝はパパイヤと島のパン、昼はカジキのセビーチェ、夕暮れにはコーラルラム・トニック。空腹も、空腹でない時間も。",
  meta: ["07:00–23:00", "アラカルト"],
  img: IMG.dining2
},
{
  type: "Cellar Bar · 静謐",
  name: "ノクターン",
  en: "nocturne",
  desc: "地下、十二席。クジラの骨をモチーフにした天井の下で、ペアリングの一杯。バーマスター・遠藤律——東京『L'Effervescence』ソムリエを7年、その後アイスランドの蒸留所で2年。世界中の島々から取り寄せた酒と、レコードと、ごく低い会話のためのバー。",
  meta: ["21:00–01:00", "予約制 / 同伴一名まで"],
  img: IMG.dining3
}];


// Phase 3.H: Dining を Aman 型から撤回。横並び3カードへ戻す。
// Phase 3.F の DiningFullscreen はロールバック容易性のためコメントアウトで残置。
/*
function DiningFullscreen({ d, i }) {
  const [ref, shown] = useReveal();
  const mediaRef = React.useRef(null);
  const offset = useParallax(mediaRef, 0.08);
  const reverse = i % 2 === 1;
  const no = String(i + 1).padStart(2, "0");

  return (
    <section
      className={"dining-fs " + (reverse ? "dining-fs--rev" : "")}
      id={"dining-" + no}
      ref={ref}>

      <div className="dining-fs__media" ref={mediaRef}>
        <img
          src={d.img}
          alt={d.name}
          loading="lazy"
          style={{ transform: `translate3d(0, ${offset}px, 0) scale(1.06)` }} />

        <div className="dining-fs__cap">
          <span>{d.type}</span>
          <span>{d.meta?.[0]}</span>
        </div>
      </div>
      <div className="dining-fs__text">
        <div className={"dining-fs__num reveal " + (shown ? "in" : "")}>{no}</div>
        <h3 className={"dining-fs__title reveal " + (shown ? "in" : "")} style={{ "--rd": "120ms" }}>
          {d.name}<em>{d.en || d.type}</em>
        </h3>
        <p className={"dining-fs__body reveal " + (shown ? "in" : "")} style={{ "--rd": "240ms" }}>
          {d.desc}
        </p>
        <div className={"dining-fs__type reveal " + (shown ? "in" : "")} style={{ "--rd": "360ms" }}>
          <span>{d.type}</span>
          <span>{d.meta?.[0]}</span>
          <span>{d.meta?.[1]}</span>
        </div>
      </div>
    </section>);

}
*/

function Dining() {
  const [ref, shown] = useReveal();
  return (
    <section className="section dining-grid" id="dining" ref={ref}>
      <div className="wrap">
        <header className="suites__head">
          <div>
            <div className="eyebrow">— Dining · 食 —</div>
            <h2 className="h-section reveal-trigger" style={{ marginTop: 14 }}>島の、三つの食卓。</h2>
          </div>
          <div />
          <div className="h-en" style={{ textAlign: "right" }}>
            Three tables. Three rhythms.<br />
            <span style={{ fontStyle: "normal", fontSize: "0.5em", letterSpacing: "0.32em", textTransform: "uppercase" }}>morning · noon · night</span>
          </div>
        </header>
        <div className={"dining-grid__deck reveal " + (shown ? "in" : "")}>
          {DINING.map((d, i) => (
            <article key={i} className="dine-card-v2">
              <div className="dine-card-v2__media">
                {/* Phase 3.N: lazy へ戻す */}
                <img src={d.img} alt={d.name} loading="lazy" decoding="async" />
              </div>
              <div className="dine-card-v2__body">
                <div className="dine-card-v2__type">{d.type}</div>
                <h3 className="dine-card-v2__name">{d.name}</h3>
                <p className="dine-card-v2__desc">{d.desc}</p>
                <div className="dine-card-v2__meta">
                  {(d.meta || []).map((m, k) => <span key={k}>{m}</span>)}
                </div>
              </div>
            </article>
          ))}
        </div>
      </div>
    </section>);

}

// ─── Activities ──────────────────────────────────────────────────────────────

// Phase 3.C: COMO型「横スクロールカードデッキ」用データ配列
const ACTIVITIES = [
  { no: "01", title: "ホエール・ウォッチング", en: "Whale Encounters · with marine biologist", season: "Dec — May · 冬季", img: IMG.whale },
  { no: "02", title: "ドルフィン・スイム", en: "Swim with wild spinner dolphins", season: "All year · 通年", img: IMG.dolphin },
  { no: "03", title: "シュノーケル", en: "Into the bonin blue · daily, 09:00", img: IMG.snorkel },
  { no: "04", title: "星空案内", en: "Private stargazing · rooftop", img: IMG.stars_deck },
  { no: "05", title: "固有種の森", en: "Endemic forest walk · w/ guide", img: IMG.forest },
  { no: "06", title: "シーカヤック・無人島渡り", en: "Sea-kayak to a deserted islet · half day", season: "Apr — Nov · 春〜秋", img: IMG.kayak },
  { no: "07", title: "タラソ・スパ", en: "Thalassotherapy · with local algae", img: IMG.spa },
];

// Phase 3.H: Activities を Aman 型から撤回。コンパクトカードグリッドへ戻す。
// Phase 3.F の ActivityFullscreen はロールバック容易性のためコメントアウトで残置。
/*
function ActivityFullscreen({ a, i }) {
  const [ref, shown] = useReveal();
  const mediaRef = React.useRef(null);
  const offset = useParallax(mediaRef, 0.08);
  const reverse = i % 2 === 1;

  return (
    <section
      className={"activity-fs " + (reverse ? "activity-fs--rev" : "")}
      id={"activity-" + a.no}
      ref={ref}>

      <div className="activity-fs__media" ref={mediaRef}>
        <img
          src={a.img}
          alt={a.title}
          loading="lazy"
          style={{ transform: `translate3d(0, ${offset}px, 0) scale(1.06)` }} />

        {a.season && <div className="activity-fs__season">{a.season}</div>}
        <div className="activity-fs__cap">
          <span>{"No. " + a.no}</span>
          <span>{a.en}</span>
        </div>
      </div>
      <div className="activity-fs__text">
        <div className={"activity-fs__num reveal " + (shown ? "in" : "")}>{a.no}</div>
        <h3 className={"activity-fs__title reveal " + (shown ? "in" : "")} style={{ "--rd": "120ms" }}>
          {a.title}<em>{a.en}</em>
        </h3>
        {a.season &&
        <div className={"activity-fs__period reveal " + (shown ? "in" : "")} style={{ "--rd": "240ms" }}>
            {a.season}
          </div>
        }
      </div>
    </section>);

}
*/

// Phase 3.U: Activities を Four Seasons Maui 風「中央フォーカス型カルーセル」に置換。
// Phase 3.H のグリッド実装は下記コメントアウトでロールバック容易性のため残置。
/*
function Activities() {
  const [ref, shown] = useReveal();
  return (
    <section className="section activities-grid" id="activities" ref={ref}>
      <div className="wrap">
        <header className="suites__head">
          <div>
            <div className="eyebrow">— Encounters · 体験 —</div>
            <h2 className="h-section reveal-trigger" style={{ marginTop: 14 }}>島の時間と、共に。</h2>
          </div>
          <div />
          <div className="h-en" style={{ textAlign: "right" }}>
            Curated by the island itself.<br />
            <span style={{ fontStyle: "normal", fontSize: "0.5em", letterSpacing: "0.32em", textTransform: "uppercase" }}>seven encounters · all year</span>
          </div>
        </header>
        <div className={"activities-grid__deck reveal " + (shown ? "in" : "")}>
          {ACTIVITIES.map((a, idx) => (
            <article key={a.no} className="act-card-v2">
              <div className="act-card-v2__media">
                <img src={a.img} alt={a.title} loading="lazy" decoding="async" />
                {a.season && <div className="act-card-v2__season">{a.season}</div>}
              </div>
              <div className="act-card-v2__body">
                <div className="act-card-v2__no">— {a.no} —</div>
                <h3 className="act-card-v2__title">{a.title}</h3>
                <div className="act-card-v2__en">{a.en}</div>
              </div>
            </article>
          ))}
        </div>
      </div>
    </section>);
}
*/

function Activities() {
  const [active, setActive] = useState(0);
  const [ref, shown] = useReveal();
  const sectionRef = useRef(null);
  const total = ACTIVITIES.length;
  const prev = () => setActive((i) => (i - 1 + total) % total);
  const next = () => setActive((i) => (i + 1) % total);

  // Phase 3.U: 配下要素 ref も親 ref と同じノードを指すよう統合
  const setRefs = (node) => {
    sectionRef.current = node;
    if (ref) ref.current = node;
  };

  // Phase 3.V: 循環表示用に前後へ複製カードを追加。
  // [末尾複製, ...ACTIVITIES, 先頭複製] = total + 2 枚を並べることで、
  // active=0 でも左に末尾カード、active=total-1 でも右に先頭カードが見える。
  const renderList = [ACTIVITIES[total - 1], ...ACTIVITIES, ACTIVITIES[0]];

  // カルーセルの translateX 計算
  // - 通常カード幅: 320px / アクティブカード幅: 440px / gap: 24px
  // - active カードの DOM index = active + 1（先頭に末尾複製がある分）
  // - DOM index j までの左端位置 = j * (NORMAL_W + GAP)
  //   ※ j より前のカードはすべて NORMAL_W で計算（active カード以外は全て normal サイズ）
  // - active カードの中心 = j * (NORMAL_W + GAP) + ACTIVE_W / 2
  // - viewport 中央 (50%) から offsetX を引いた位置を track (left:0) の transform にする
  const NORMAL_W = 320;
  const ACTIVE_W = 440;
  const GAP = 24;
  const j = active + 1;
  // active カードの中心が track 左端から何px の位置にあるか
  // j 枚分は NORMAL_W (active以前) + GAP、active カード自身の中心は ACTIVE_W/2
  const offsetX = j * (NORMAL_W + GAP) + ACTIVE_W / 2;
  // Phase 3.W: track の left:50% で起点をviewport中央に。translate(-offsetX) で active カードを中央に揃える
  const trackStyle = { transform: `translate(-${offsetX}px, -50%)` };

  // キーボード操作（セクションが viewport 内にあるときのみ）
  useEffect(() => {
    const onKey = (e) => {
      if (!sectionRef.current) return;
      const rect = sectionRef.current.getBoundingClientRect();
      const inView = rect.top < window.innerHeight * 0.7 && rect.bottom > window.innerHeight * 0.3;
      if (!inView) return;
      if (e.key === "ArrowLeft") { e.preventDefault(); prev(); }
      if (e.key === "ArrowRight") { e.preventDefault(); next(); }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [total]);

  return (
    <section className="section activities-carousel" id="activities" ref={setRefs}>
      <div className="wrap">
        <header className="suites__head">
          <div>
            <div className="eyebrow">— Encounters · 体験 —</div>
            <h2 className="h-section reveal-trigger" style={{ marginTop: 14 }}>島の時間と、共に。</h2>
          </div>
          <div />
          <div className="h-en" style={{ textAlign: "right" }}>
            Curated by the island itself.<br />
            <span style={{ fontStyle: "normal", fontSize: "0.5em", letterSpacing: "0.32em", textTransform: "uppercase" }}>seven encounters · all year</span>
          </div>
        </header>
      </div>

      <div className={"activities-carousel__viewport reveal " + (shown ? "in" : "")}>
        <div className="activities-carousel__track" style={trackStyle}>
          {renderList.map((a, domIdx) => {
            // 端点は複製カード（先頭 = 末尾の複製、末尾 = 先頭の複製）
            const isHeadClone = domIdx === 0;
            const isTailClone = domIdx === total + 1;
            const realIdx = isHeadClone ? total - 1 : isTailClone ? 0 : domIdx - 1;
            const isActive = realIdx === active && !isHeadClone && !isTailClone;
            return (
              <article
                key={`${a.no}-${domIdx}`}
                className={"act-carousel-card " + (isActive ? "is-active" : "")}
                onClick={() => setActive(realIdx)}
                aria-current={isActive ? "true" : "false"}
                aria-hidden={isHeadClone || isTailClone ? "true" : undefined}>

                <div className="act-carousel-card__media">
                  <img src={a.img} alt={a.title} loading="lazy" decoding="async" />
                </div>
                {a.season && <div className="act-carousel-card__season">{a.season}</div>}
                <div className="act-carousel-card__body">
                  <div className="act-carousel-card__no">— {a.no} —</div>
                  <h3 className="act-carousel-card__title">{a.title}</h3>
                  <div className="act-carousel-card__en">{a.en}</div>
                </div>
              </article>);
          })}
        </div>
      </div>

      <div className="wrap">
        <div className="activities-carousel__controls">
          <button type="button" onClick={prev} className="activities-carousel__arrow" aria-label="前の体験へ">←</button>
          <div className="activities-carousel__pagination">
            <b>{String(active + 1).padStart(2, "0")}</b>
            <span> / {String(total).padStart(2, "0")}</span>
          </div>
          <button type="button" onClick={next} className="activities-carousel__arrow" aria-label="次の体験へ">→</button>
        </div>
      </div>
    </section>);

}

// ─── Access ──────────────────────────────────────────────────────────────────

const TL = [
{ t: "Day 1 · 11:00", h: "東京・竹芝桟橋", en: "Departure", n: "おがさわら丸、出航。お見送りの紙テープが、ゆっくりと潮風に流される。" },
{ t: "Day 1 · 14:00", h: "外洋へ", en: "Open Sea", n: "本土が水平線の向こうに沈み、海は次第に深い藍へと変わってゆく。" },
{ t: "Day 1 · 19:30", h: "船上にて、夕食", en: "Dinner Aboard", n: "stellare bonin の選んだワインと、洋上限定の前菜が、専用ラウンジで待っている。" },
{ t: "Day 1 · 22:00", h: "デッキで、星見", en: "Stargazing", n: "光のない海の上、満天の星。あなたの旅は、この夜、すでに始まっている。" },
{ t: "Day 2 · 11:00", h: "父島・二見港、着岸", en: "Arrival, Chichijima", n: "専属のバトラーが、白い帆布の旗を掲げて、桟橋でお待ちしております。" },
{ t: "Day 2 · 11:40", h: "stellare bonin 到着", en: "Welcome", n: "ウェルカム・パイナップル・モヒート、そして、海に向かって開かれたあなたの部屋へ。" }];


function Access() {
  const [ref, shown] = useReveal();
  return (
    <section className="section access" id="access" ref={ref}>
      <div className="wrap">
        <header className="access__head">
          <div>
            <div className="eyebrow">— Access · 旅程 —</div>
            <h2 className="h-section reveal-trigger" style={{ marginTop: 14 }}>
              海路、二十四時間。<br />
              <span style={{ fontFamily: "var(--serif-en)", fontStyle: "italic", fontWeight: 300, fontSize: "0.5em", letterSpacing: "0.18em", color: "var(--muted)", display: "inline-block", marginTop: 14 }}>The journey is the welcome.</span>
            </h2>
          </div>
          <div className="access__lead">
            小笠原諸島には空港がない。だから、おがさわら丸が、唯一の道。けれど、ここでは、その24時間こそが旅の最初の章であると、わたしたちは信じている。
          </div>
        </header>
        <div className="timeline">
          {TL.map((tl, i) => {
            const side = i % 2 === 0 ? "tl--left" : "tl--right";
            return (
              <div className={"tl " + side + " reveal " + (shown ? "in" : "")} key={i} style={{ "--rd": `${i * 90}ms` }}>
                <div className="tl__col">
                  <div className="tl__time">{tl.t}</div>
                  <div className="tl__body">{tl.h}<em>{tl.en}</em></div>
                  <div className="tl__note">{tl.n}</div>
                </div>
                <div className="tl__dot" style={{ gridColumn: 2 }} />
                <div className="tl__pad-r" />
              </div>);

          })}
        </div>
      </div>
    </section>);

}

// ─── FAQ ─────────────────────────────────────────────────────────────────────

const FAQS = [
{ q: "客室はすべてオーシャンフロントですか？", a: "はい。全17室、すべてのお部屋から太平洋を望んでいただけます。同じ bonin blue でも、お部屋の階層と向きによって、ご覧いただける島影や夕陽の見え方が異なります。お好みをご相談ください。" },
{ q: "宿までのアクセスについて教えてください。", a: "東京・竹芝桟橋から「おがさわら丸」にて約24時間。原則として6日に1便の運航となります。客船の到着に合わせ、二見港にて専属バトラーがお出迎えにあがります。" },
{ q: "クジラやイルカは、必ず見られますか？", a: "ザトウクジラのシーズンは12月から5月、ハシナガイルカは通年ご覧いただけることが多いですが、自然のいとなみゆえに必ずとは申し上げられません。出会えなかった場合のための、星空ツアーもご用意しております。" },
{ q: "子ども連れでも利用できますか？", a: "10歳以上のお子さまよりお受けしております。「クレスト・レジデンス」のみ、お子さまの年齢制限はございません。詳細はお問い合わせください。" },
{ q: "食物アレルギーに対応していますか？", a: "ご予約時にお知らせいただければ、すべての食事をアレルギーに配慮した内容に組み替えいたします。離島という性質上、特殊な食材のご用意には2週間前までのご連絡を頂戴しております。" },
{ q: "キャンセルポリシーは？", a: "船便の特性上、ご出発の30日前から取消料を申し受けます。ただし、悪天候による「おがさわら丸」欠航の場合は、全額返金させていただきます。" }];


function FAQ() {
  const [open, setOpen] = useState(0);
  const [ref, shown] = useReveal();
  return (
    <section className="section faq" id="faq" ref={ref}>
      <div className="wrap-sm">
        <div style={{ textAlign: "center" }}>
          <div className="eyebrow">— FAQ · よくあるご質問 —</div>
          <h2 className="h-section reveal-trigger" style={{ marginTop: 14 }}>ご案内</h2>
        </div>
        <div className="faq__list">
          {FAQS.map((f, i) =>
          <div
            key={i}
            className={"q " + (open === i ? "open" : "") + " reveal " + (shown ? "in" : "")}
            style={{ "--rd": `${i * 60}ms` }}
            onClick={() => setOpen(open === i ? -1 : i)}>
            
              <div className="q__row">
                <div className="q__no">Q.{String(i + 1).padStart(2, "0")}</div>
                <h4 className="q__q">{f.q}</h4>
                <div className="q__toggle">+</div>
              </div>
              <div className="q__a"><div className="q__a-inner">{f.a}</div></div>
            </div>
          )}
        </div>
      </div>
    </section>);

}

// ─── Reserve / Footer ────────────────────────────────────────────────────────

function Reserve() {
  const [ref, shown] = useReveal();
  return (
    <section className="reserve" id="reserve" ref={ref}>
      <div className={"reveal " + (shown ? "in" : "")}>
        <div className="eyebrow" style={{ color: "rgba(236,229,216,0.5)", marginBottom: 24 }}>— Reservations —</div>
        <h2 className="h-display" style={{ marginBottom: 18 }}>
          いつか、ではなく。<br />
          <span style={{ opacity: 0.7 }}>今度の、新月の夜に。</span>
        </h2>
        <p className="h-en">
          A fictional luxury retreat. <em>Designed as a portfolio experiment in 2026.</em>
        </p>
        <a href="#" className="reserve__cta">Begin your retreat</a>
        <div className="reserve__future">
          Phase II — Twin Wing & Submerged Library · Coming Winter 2027
        </div>
      </div>
    </section>);

}

// ─── M4: Floating Reserve CTA ────────────────────────────────────────────────

function FloatingReserveCTA() {
  const y = useScrollY();
  // Hero 表示中（scroll < window.innerHeight）は非表示、Stories 以降で表示
  const visible = y > window.innerHeight * 0.9;
  return (
    <a
      href="#reserve"
      className={"floating-cta " + (visible ? "visible" : "")}
      aria-label="Begin your retreat — Reserve">
      Begin your retreat →
    </a>);

}

function Footer() {
  return (
    <footer className="foot">
      <div className="foot__brand">
        <b><em>stellare bonin</em></b>
        <p>東京都小笠原村父島字南崎<br />〒100-2101</p>
      </div>
      <div>
        <h5>Contact</h5>
        <p>reserve@stellare-bonin.jp</p>
      </div>
      <div>
        <h5>Hotel</h5>
        <ul>
          <li>客室</li>
          <li>レストラン</li>
          <li>アクティビティ</li>
          <li>アクセス</li>
        </ul>
      </div>
      <div>
        <h5>Legal</h5>
        <ul>
          <li>プライバシーポリシー</li>
          <li>キャンセル規定</li>
          <li>採用情報</li>
        </ul>
      </div>
      <div className="foot__copy">
        <span>© 2026 stellare bonin · all rights reserved</span>
        <span>27.094° N · 142.197° E · Bonin Islands</span>
        <span style={{ marginTop: 8, opacity: 0.6, fontStyle: "italic" }}>
          A fictional portfolio · Designed by Luna with Claude
        </span>
      </div>
    </footer>);

}

// ─── Export to window ────────────────────────────────────────────────────────

Object.assign(window, {
  useScrollY, useParallax, useReveal,
  Nav, EasterEggModal, Hero, Prologue, Stories, Bleed, BleedSecondary, Suites, Dining, Activities, Access, FAQ, Reserve, FloatingReserveCTA, Footer,
  IMG
});