// app.jsx — stellare bonin
// Wires sections together + Tweaks panel (palette / typography / layout).

const { useEffect: useEffectApp } = React;

const TWEAK_DEFAULTS = { palette: "bonin", typeMix: "mincho", layout: "editorial", showCaptions: true };

// Palette definitions — bonin (蒼の宿) のみに統一
// 過去の earth / ink / night は Phase 2.6 で削除（未使用・緑色混入の原因）
const PALETTES = {
  bonin: {
    label: "Bonin · 蒼の宿",
    swatch: ["#0a1a28", "#5fb3c4", "#f2e9d8", "#d96a3a"],
    vars: {
      "--bg":        "#f2ece0",
      "--bg-2":      "#e5dcc8",
      "--ink":       "#0a1a28",
      "--ink-2":     "#142a3a",
      "--muted":     "#586a78",
      "--rule":      "rgba(10, 26, 40, 0.18)",
      "--rule-soft": "rgba(10, 26, 40, 0.10)",
      "--accent":    "#1a4a6a",
      // Phase 3.A: 2色パレット絞り（ロールバック容易性のためコメントアウトで保持）
      // "--accent-2":  "#5fb3c4",
      // "--warm":      "#d96a3a",
    },
  },
};

const TYPES = {
  mincho: {
    label: "Shippori × Cormorant",
    vars: {
      "--serif-jp": "'Shippori Mincho B1', 'Zen Old Mincho', 'Yu Mincho', serif",
      "--serif-en": "'Cormorant Garamond', 'Cormorant', serif",
    },
  },
  oldjp: {
    label: "Zen Old Mincho",
    vars: {
      "--serif-jp": "'Zen Old Mincho', 'Shippori Mincho B1', serif",
      "--serif-en": "'Cormorant', 'Cormorant Garamond', serif",
    },
  },
  sans: {
    label: "Noto Sans JP",
    vars: {
      "--serif-jp": "'Noto Sans JP', 'Yu Gothic', sans-serif",
      "--serif-en": "'Cormorant', 'Cormorant Garamond', serif",
    },
  },
};

function applyTheme(tweaks) {
  const root = document.documentElement;
  const p = PALETTES[tweaks.palette] || PALETTES.bonin;
  const t = TYPES[tweaks.typeMix] || TYPES.mincho;
  Object.entries(p.vars).forEach(([k, v]) => root.style.setProperty(k, v));
  Object.entries(t.vars).forEach(([k, v]) => root.style.setProperty(k, v));
  const cls = [`layout-${tweaks.layout}`, `palette-${tweaks.palette}`];
  if (tweaks.showCaptions) cls.push("show-captions");
  document.body.className = cls.join(" ");
}

// ─── Phase 3.A: Custom Cursor C1 ─────────────────────────────────────────────
// useRef で DOM 直接操作 → リレンダ回避
// requestAnimationFrame で位置更新
// matchMedia('(hover: hover)') で hover デバイスのみ動作（モバイル無効化）

function CustomCursor() {
  const dotRef = React.useRef(null);
  const [variant, setVariant] = React.useState("default");

  React.useEffect(() => {
    // hover デバイス以外（タッチ・モバイル）では一切動作しない
    if (typeof window === "undefined") return;
    if (!window.matchMedia("(hover: hover)").matches) return;

    let x = -100, y = -100;
    let rafId = 0;

    const onMove = (e) => {
      x = e.clientX;
      y = e.clientY;
    };

    const onOver = (e) => {
      const t = e.target;
      if (!t || !t.closest) return;
      if (t.closest("a, button")) {
        setVariant("link");
      } else if (t.tagName === "IMG" && t.closest(".hero, .bleed, .suite__media")) {
        setVariant("view");
      } else if (t.tagName === "IMG" || t.tagName === "VIDEO") {
        setVariant("image");
      } else {
        setVariant("default");
      }
    };

    const tick = () => {
      if (dotRef.current) {
        dotRef.current.style.transform = `translate3d(${x}px, ${y}px, 0)`;
      }
      rafId = requestAnimationFrame(tick);
    };

    window.addEventListener("mousemove", onMove, { passive: true });
    window.addEventListener("mouseover", onOver, { passive: true });
    document.body.classList.add("has-custom-cursor");
    rafId = requestAnimationFrame(tick);

    return () => {
      cancelAnimationFrame(rafId);
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseover", onOver);
      document.body.classList.remove("has-custom-cursor");
    };
  }, []);

  return (
    <div
      ref={dotRef}
      className={"cursor cursor--" + variant}
      aria-hidden="true"
    >
      {variant === "view" && <span className="cursor__label">VIEW</span>}
    </div>
  );
}

function App() {
  useEffectApp(() => { applyTheme(TWEAK_DEFAULTS); }, []);

  // S1: Lenis smooth scroll
  useEffectApp(() => {
    if (!window.Lenis) return;
    const lenis = new window.Lenis({
      duration: 1.2,
      easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
      smoothWheel: true,
      smoothTouch: false,
    });
    let rafId = 0;
    function raf(time) {
      lenis.raf(time);
      rafId = requestAnimationFrame(raf);
    }
    rafId = requestAnimationFrame(raf);
    return () => {
      cancelAnimationFrame(rafId);
      lenis.destroy();
    };
  }, []);

  // M2: IntersectionObserver reveal-trigger
  useEffectApp(() => {
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          e.target.classList.add("revealed");
          io.unobserve(e.target);
        }
      });
    }, { threshold: 0.15, rootMargin: "0px 0px -80px 0px" });
    document.querySelectorAll(".reveal-trigger").forEach((el) => io.observe(el));
    return () => io.disconnect();
  }, []);

  // Phase 3.I: Hero メタ情報の scroll-fade
  // Hero が画面の 40% 以上スクロールされたら、COORDINATES / NOW VIEWING / EST. MMXXVI
  // を含む .hero__top / .hero__foot を fade out。nav と重なって読みにくい問題を解消。
  useEffectApp(() => {
    const onScroll = () => {
      const scrolled = window.scrollY > window.innerHeight * 0.4;
      document.body.classList.toggle("hero-meta-hidden", scrolled);
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener("scroll", onScroll);
  }, []);

  return (
    <React.Fragment>
      <Nav />
      <Hero />
      <Prologue />
      <Stories />
      <Bleed />
      <BleedSecondary />
      <Access />
      <Suites />
      <Dining />
      <Activities />
      <FAQ />
      <Reserve />
      <FloatingReserveCTA />
      <Footer />
      <CustomCursor />
    </React.Fragment>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
