// components/app.jsx
// Top-level app + Tweaks integration. Wraps everything, owns modal state.

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "iosLaunched": true,
  "androidLaunched": false,
  "viewport": "desktop",
  "countdownDate": "2026-06-11T11:00:00-04:00",
  "showAnimations": true,
  "shapeOpacity": 22,
  "showPlaceholders": false,
  "heroPhoneContent": "smart_lines",
  "heroLayout": "centered",
  "familiarVariant": "sandwich"
}/*EDITMODE-END*/;

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [modalIntent, setModalIntent] = React.useState(null);
  const [openFAQItem, setOpenFAQItem] = React.useState(null);

  // Apply per-platform launch flags to live COPY so any consumer sees current state.
  React.useEffect(() => {
    window.COPY.iosLaunched = t.iosLaunched;
    window.COPY.androidLaunched = t.androidLaunched;
  }, [t.iosLaunched, t.androidLaunched]);

  // Handle initial hash (e.g. someone lands on #faq-whats-next or #faq-how-it-works directly).
  React.useEffect(() => {
    const hash = window.location.hash.replace("#", "");
    if (hash === "faq-whats-next" || hash === "faq-how-it-works") {
      setTimeout(() => setOpenFAQItem(hash), 200);
    }
  }, []);

  // v=154: pre-reveal data-reveal elements inside the navigation target.
  // When a user clicks a nav item that scrolls to a section containing
  // reveal animations (e.g. #what-you-get whose H2 has data-reveal), the
  // animations would fire WHILE the browser is settling the scroll, which
  // on WebKit (iOS Chrome/Safari) can cause anchor scroll-chase (visible
  // as "bouncing"). Pre-applying .is-revealed on hash change means the
  // animations have already completed by the time the user arrives,
  // giving the browser a static target.
  React.useEffect(() => {
    const prereveal = () => {
      const hash = window.location.hash.replace("#", "");
      if (!hash) return;
      const target = document.getElementById(hash);
      if (!target) return;
      if (target.hasAttribute("data-reveal")) target.classList.add("is-revealed");
      target.querySelectorAll("[data-reveal]").forEach((el) =>
        el.classList.add("is-revealed")
      );
    };
    prereveal();
    window.addEventListener("hashchange", prereveal);
    return () => window.removeEventListener("hashchange", prereveal);
  }, []);

  // Header "How it works" link routes to FAQ and auto-opens the how-it-works entry.
  const onHowItWorks = () => {
    setOpenFAQItem("faq-how-it-works");
  };

  const onIntent = (source, platform) => {
    setModalIntent({ source, platform: platform || "android" });
  };

  const onWhatsNext = () => {
    setOpenFAQItem("faq-whats-next");
  };

  // Viewport simulation. Wrap content in a width-constrained frame.
  const viewportWidth =
    t.viewport === "mobile-375" ? 375 :
    t.viewport === "mobile-390" ? 390 :
    null;

  // Apply placeholder-marker class on body when toggle is on so descendant components
  // can opt into outline + label rendering via CSS.
  React.useEffect(() => {
    document.body.classList.toggle("show-placeholders", !!t.showPlaceholders);
  }, [t.showPlaceholders]);

  // ── Header pin + bg-aware coloring ──────────────────────────────────
  // Two body classes drive the floating header:
  //   .header-pinned       → wordmark fades in once the hero brand mark
  //                          has scrolled above the viewport
  //   .header-on-cream     → wordmark + hamburger swap to near-black so
  //                          they stay legible over cream sections
  // Pure scroll-position math (no IntersectionObserver) so the threshold
  // is deterministic and the transition is driven by CSS opacity only —
  // toggling the class is a single boolean flip, the rest is smooth.
  React.useEffect(() => {
    const PIN_BUFFER = 24; // pin slightly before the mark fully leaves
    const HEADER_PROBE_Y = 32; // sample Y for "which section is at top"

    const onScroll = () => {
      const mark = document.querySelector(".hero-mark");
      if (mark) {
        const r = mark.getBoundingClientRect();
        document.body.classList.toggle("header-pinned", r.bottom < PIN_BUFFER);
      }
      // Probe the element currently sitting under the header. Walk up
      // from elementFromPoint until we hit a section to read its bg.
      const el = document.elementFromPoint(window.innerWidth / 2, HEADER_PROBE_Y);
      let node = el;
      let onCream = false;
      while (node && node !== document.body) {
        if (node.classList?.contains("section-cream")) { onCream = true; break; }
        if (node.classList?.contains("section-dark")) { onCream = false; break; }
        node = node.parentElement;
      }
      document.body.classList.toggle("header-on-cream", onCream);
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
    };
  }, []);

  // Global scroll-triggered reveal observer. Any element with [data-reveal]
  // anywhere on the page fades up + into place when it enters the viewport.
  // Optional per-element stagger via [data-reveal-delay="120"] (milliseconds)
  // applied to transition-delay. Respects prefers-reduced-motion (everything
  // appears instantly, no transitions). Re-runs after each render so newly
  // mounted elements (modals, expanded FAQ items) get picked up too.
  React.useEffect(() => {
    if (typeof window === "undefined") return;
    const reduce =
      window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (reduce) {
      document.querySelectorAll("[data-reveal]").forEach((el) =>
        el.classList.add("is-revealed")
      );
      return;
    }
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            entry.target.classList.add("is-revealed");
            io.unobserve(entry.target);
          }
        });
      },
      { threshold: 0.25, rootMargin: "0px 0px -16% 0px" }
    );
    const observe = () => {
      document
        .querySelectorAll("[data-reveal]:not(.is-revealed)")
        .forEach((el) => io.observe(el));
    };
    observe();
    // Re-observe on a slight delay so freshly mounted React subtrees join in.
    const t1 = setTimeout(observe, 600);
    const t2 = setTimeout(observe, 1800);
    return () => {
      clearTimeout(t1);
      clearTimeout(t2);
      io.disconnect();
    };
  }, []);

  const content = (
    <>
      <SiteHeader onIntent={onIntent} onHowItWorks={onHowItWorks} />
      <main id="main">
        <HeroSection
          media={window.MEDIA.hero}
          liveCarousel={window.MEDIA.liveCarousel}
          onIntent={onIntent}
          animationsOn={t.showAnimations}
          shapeOpacity={t.shapeOpacity / 100}
          phoneContent={t.heroPhoneContent}
          heroLayout={t.heroLayout}
        />
        <FamiliarSection variant={t.familiarVariant} />
        {/* v=143: cream zone reverted to wrap all three cream sections
            (ish cards + WYG + FAQ). The brief v=142 experiment that
            closed the device frame right after the cards has been
            rolled back per user direction — the cream "breath" is the
            whole chapter, not just the cards. */}
        <div className="device-frame">
          <div className="device-frame-inner">
            <IshCardsGrid cards={window.MEDIA.cards} />
            <WhatYouGetSection onWhatsNext={onWhatsNext} />
            <FAQSection
              activeOpenItem={openFAQItem}
              onItemHandled={() => setOpenFAQItem(null)}
              onIntent={onIntent}
            />
          </div>
        </div>
        <FinalCTASection
          onIntent={onIntent}
          countdownDate={t.countdownDate}
        />
      </main>
      <SiteFooter />

      {modalIntent && (
        <DownloadIntentModal
          source={modalIntent.source}
          platform={modalIntent.platform}
          onClose={() => setModalIntent(null)}
        />
      )}
      <CookieBanner />
    </>
  );

  return (
    <>
      {viewportWidth ? (
        <div className="viewport-sim">
          <div className="viewport-frame" style={{ width: viewportWidth + "px" }}>
            <div className="viewport-content">{content}</div>
          </div>
        </div>
      ) : content}

      <TweaksPanel title="Tweaks">
        <TweakSection label="Launch state" />
        <TweakToggle
          label="iOS launched"
          value={t.iosLaunched}
          onChange={(v) => setTweak("iosLaunched", v)}
        />
        <TweakToggle
          label="Android launched"
          value={t.androidLaunched}
          onChange={(v) => setTweak("androidLaunched", v)}
        />
        <TweakText
          label="Countdown target"
          value={t.countdownDate}
          onChange={(v) => setTweak("countdownDate", v)}
        />

        <TweakSection label="Viewport" />
        <TweakRadio
          label="Width"
          value={t.viewport}
          options={[
            { value: "desktop", label: "Desktop" },
            { value: "mobile-390", label: "390px" },
            { value: "mobile-375", label: "375px" },
          ]}
          onChange={(v) => setTweak("viewport", v)}
        />

        <TweakSection label="Hero phone content" />
        <TweakRadio
          label="Show in phone"
          value={t.heroPhoneContent}
          options={[
            { value: "live_carousel", label: "Live carousel" },
            { value: "smart_lines", label: "Smart Lines" },
          ]}
          onChange={(v) => setTweak("heroPhoneContent", v)}
        />

        <TweakSection label="Hero layout" />
        <TweakRadio
          label="Alignment"
          value={t.heroLayout}
          options={[
            { value: "left", label: "Left" },
            { value: "centered", label: "Center" },
            { value: "right", label: "Right" },
          ]}
          onChange={(v) => setTweak("heroLayout", v)}
        />

        <TweakSection label="Familiar section" />
        <TweakRadio
          label="Layout"
          value={t.familiarVariant}
          options={[
            { value: "crescendo", label: "Crescendo" },
            { value: "sandwich", label: "Sandwich" },
            { value: "collage", label: "Collage" },
            { value: "rebel", label: "Middle as rebel" },
          ]}
          onChange={(v) => setTweak("familiarVariant", v)}
        />

        <TweakSection label="Motion" />
        <TweakToggle
          label="Show animations"
          value={t.showAnimations}
          onChange={(v) => setTweak("showAnimations", v)}
        />
        <TweakSlider
          label="Hero shape opacity"
          value={t.shapeOpacity}
          min={0}
          max={40}
          unit="%"
          onChange={(v) => setTweak("shapeOpacity", v)}
        />

        <TweakSection label="Review" />
        <TweakToggle
          label="Show placeholder markers"
          value={t.showPlaceholders}
          onChange={(v) => setTweak("showPlaceholders", v)}
        />
      </TweaksPanel>
    </>
  );
}

window.App = App;
