/* global React */
// Rayu — Lifestyle sections, sequenced like the WHOOP homepage:
//   2. Wear rayu daily       4. Choose your membership
//   3. The complete picture  5. Backed by science / worn by the bold
//                            6. Built to be worn 24/7
// Real band photography (assets/band-*.png); lifestyle imagery via <image-slot>.

const { useEffect, useRef, useState } = React;

function useReveal() {
  const ref = useRef(null);
  useEffect(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver(
      (entries) => entries.forEach((e) => {
        if (e.isIntersecting) {e.target.classList.add("in");io.unobserve(e.target);}
      }),
      { threshold: 0.12 }
    );
    io.observe(ref.current);
    return () => io.disconnect();
  }, []);
  return ref;
}
function Reveal({ as: Tag = "div", className = "", children, ...rest }) {
  const ref = useReveal();
  return <Tag ref={ref} className={`reveal ${className}`} {...rest}>{children}</Tag>;
}

const Plus = (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" {...p}><path d="M12 5v14M5 12h14" /></svg>;
const Check = (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M20 6L9 17l-5-5" /></svg>;
const Arrow = (p) => <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M5 12h14M13 6l6 6-6 6" /></svg>;

/* ── 2. Wear rayu daily, eat smarter ──────────────────────────────────── */
function DailyHealth() {
  return (
    <section id="daily" className="daily" data-screen-label="Wear daily">
      <div className="shell">
        <Reveal className="daily-head">
          <h2 className="display daily-title">Your nutrition is your health<span style={{ display: "block", fontFamily: "\"Space Grotesk\"", fontSize: "24px", fontStyle: "normal", fontWeight: 400, lineHeight: 1.4, marginTop: "20px" }}>Whether its <b style={{ fontWeight: 600 }}>loosing weight</b>, <b style={{ fontWeight: 600 }}>getting in shape</b>, or getting that <b style={{ fontWeight: 600 }}>summer bod</b>.</span></h2>
          <p className="daily-lede">
            Rayu is an AI nutrition coach that helps you understand your body and make better food choices in real life. Like a personal nutritionist in your pocket, it tracks your progress, answers questions, creates meal plans, and gives private, personalised guidance based on your goals, habits, food preferences, wearable data, sleep, and daily routine.

            <span className="daily-aster">*</span>
          </p>
        </Reveal>

        <Reveal className="daily-stage">
          <image-slot id="daily-lifestyle" class="daily-photo" shape="rounded" radius="24"
          placeholder="Drop a lifestyle photo — someone cooking, eating, living">
          </image-slot>
          <div className="daily-orb">
            <div className="orb-glow" />
            <div className="orb-core">
              <div className="orb-num">29.4</div>
              <div className="orb-lbl">rayu age</div>
              <div className="orb-sub">4.7 years younger</div>
            </div>
          </div>
          <a className="daily-join" href="Order.html?v=Asub">Order the band</a>
        </Reveal>
      </div>
    </section>);

}

/* ── 3. The complete picture ──────────────────────────────────────────── */
function CompletePicture() {
  const cards = [
  { id: "cp1", t: "Quantify how your body feels", k: "Recovery" },
  { id: "cp2", t: "Extend your prime for years to come", k: "Longevity" },
  { id: "cp3", t: "Optimize your sleep", k: "Sleep" },
  { id: "cp4", t: "Measure every meal's impact", k: "Nutrition" }];

  return (
    <section id="how" className="cp" data-screen-label="Complete picture">
      <div className="shell">
        <Reveal className="cp-head">
          <h2 className="display cp-title">Get the complete picture of your <em>plate</em>.</h2>
          <p className="cp-lede">
            With 24/7 monitoring across glucose, sleep, strain, and heart health, rayu gives you
            a complete view of your body — so every meal decision is an informed one.
          </p>
        </Reveal>
      </div>

      <Reveal className="cp-scroller">
        <div className="cp-track">
          {cards.map((c) =>
          <article key={c.id} className="cp-card">
              <image-slot id={c.id} class="cp-card-media" shape="rounded" radius="20"
            placeholder={`Drop a ${c.k.toLowerCase()} photo`}></image-slot>
              <div className="cp-card-grad" />
              <span className="cp-card-kicker">{c.k}</span>
              <h3 className="cp-card-title">{c.t}</h3>
              <button className="cp-card-plus" aria-label={c.t}><Plus /></button>
            </article>
          )}
        </div>
      </Reveal>

      <div className="shell cp-cta-wrap">
        <a className="btn cp-cta" href="Order.html?v=Asub"
        style={{ background: "var(--accent)", color: "var(--accent-fg)" }}>Order the band</a>
      </div>
    </section>);

}

/* ── 4. Choose your membership ────────────────────────────────────────── */
function Membership() {
  const feats = [
    "rayu band — included",
    "12 months of Rayu AI nutritionist",
    "Personalized meal coaching",
    "Heart, sleep & glucose insights",
  ];
  return (
    <section id="membership" className="mem" data-screen-label="Membership">
      <div className="shell">
        <Reveal><h2 className="display mem-title">One membership</h2></Reveal>
        <div className="mem-grid mem-single">
          <Reveal className="mem-card hi">
            <div className="mem-media">
              <span className="mem-name">RAYU</span>
              <img src="assets/band-navy.png" alt="rayu band" />
            </div>
            <div className="mem-body">
              <div className="mem-swatches">
                {["#0e0e10", "#2b3340", "#9a9c9f", "#b9433f"].map((s, i) => <i key={i} style={{ background: s }} />)}
                <span className="mem-swatch-more">+12</span>
              </div>
              <div className="mem-price">
                <span className="mem-price-num">$249</span>
                <span className="mem-price-per">/ 12 months</span>
              </div>
              <ul className="mem-feats">
                {feats.map((f, i) => <li key={i}><Check />{f}</li>)}
              </ul>
              <a className="mem-start" href="Checkout.html?brand=lifestyle&plan=p12">
                Order the band
              </a>
              <div className="mem-foot">Free shipping · 60-day money-back</div>
            </div>
          </Reveal>
        </div>
      </div>
    </section>);

}

/* ── 5. Backed by science, worn by the bold ───────────────────────────── */
function WornBy() {
  return (
    <section id="voices" className="wb" data-screen-label="Worn by">
      <div className="shell">
        <Reveal className="wb-head">
          <h2 className="display wb-title">Backed by science,<br />worn by the <em>bold</em>.</h2>
          <p className="wb-lede">
            Whether you're dialing in your very first habit or optimizing like a pro, rayu helps
            you eat, sleep, and feel better — every single day.
          </p>
        </Reveal>

        <div className="wb-grid">
          <Reveal className="wb-tile wb-a">
            <image-slot id="wb1" class="wb-media" shape="rounded" radius="18" placeholder="Athlete / creator photo"></image-slot>
            <div className="wb-grad" /><button className="wb-play" aria-label="Play"><Arrow /></button>
            <div className="wb-cap"><div className="wb-cap-name">Kenji Mori</div><div className="wb-cap-role">Chef &amp; runner</div></div>
          </Reveal>
          <Reveal className="wb-tile wb-b">
            <image-slot id="wb2" class="wb-media" shape="rounded" radius="18" placeholder="Athlete / creator photo"></image-slot>
            <div className="wb-grad" />
            <div className="wb-cap"><div className="wb-cap-name">Sara Lin</div><div className="wb-cap-role">Climber</div></div>
            <button className="wb-plus" aria-label="More"><Plus /></button>
          </Reveal>
          <Reveal className="wb-tile wb-quote wb-c">
            <blockquote>"rayu changed how I eat on long training days — I stopped guessing."</blockquote>
            <div className="wb-q-meta"><span>Weilynn T.</span><span className="wb-q-sub">rayu member</span></div>
            <button className="wb-plus dark" aria-label="More"><Plus /></button>
          </Reveal>
          <Reveal className="wb-tile wb-d">
            <image-slot id="wb3" class="wb-media" shape="rounded" radius="18" placeholder="Athlete / creator photo"></image-slot>
            <div className="wb-grad" /><button className="wb-play" aria-label="Play"><Arrow /></button>
            <div className="wb-cap"><div className="wb-cap-name">Marco Reyes</div><div className="wb-cap-role">Triathlete</div></div>
          </Reveal>
          <Reveal className="wb-tile wb-quote wb-e">
            <blockquote>"The first wearable that tells me what to eat, not just what I burned."</blockquote>
            <div className="wb-q-meta"><span>Amara O.</span><span className="wb-q-sub">rayu member</span></div>
            <button className="wb-plus dark" aria-label="More"><Plus /></button>
          </Reveal>
          <Reveal className="wb-tile wb-f">
            <image-slot id="wb4" class="wb-media" shape="rounded" radius="18" placeholder="Athlete / creator photo"></image-slot>
            <div className="wb-grad" /><button className="wb-play" aria-label="Play"><Arrow /></button>
            <div className="wb-cap"><div className="wb-cap-name">Devin Park</div><div className="wb-cap-role">Cyclist</div></div>
          </Reveal>
        </div>

        <div className="wb-cta-wrap">
          <a className="btn" href="Order.html?v=Asub" style={{ background: "var(--accent)", color: "var(--accent-fg)" }}>Order the band</a>
        </div>
      </div>
    </section>);

}

/* ── 6. Built to be worn 24/7 (scroll-scrubbed) ──────────────────────── */
function Worn247() {
  const feats = [
  { t: "Zero distractions", d: "A screen-free design means no pings, no notifications, no unnecessary bells & whistles." },
  { t: "Always-on sensors", d: "A continuous optical stack reads glucose proxy, heart rate, and temperature around the clock." },
  { t: "7+ day battery life", d: "Charge it once a week. Slide the charger on without ever taking the band off." },
  { t: "Completely customizable", d: "Swap bands in seconds — twelve colorways in knit, woven, and performance fabrics." }];

  const sectionRef = useRef(null);
  const [progress, setProgress] = useState(0);
  useEffect(() => {
    const onScroll = () => {
      const el = sectionRef.current;
      if (!el) return;
      const rect = el.getBoundingClientRect();
      const total = el.offsetHeight - window.innerHeight;
      const scrolled = Math.min(Math.max(-rect.top, 0), total);
      setProgress(total > 0 ? scrolled / total : 0);
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {window.removeEventListener("scroll", onScroll);window.removeEventListener("resize", onScroll);};
  }, []);
  const n = feats.length;
  const active = Math.min(n - 1, Math.floor(progress * n * 0.9999));
  // fill stops at the last item's center rather than overrunning the track
  const fillPct = Math.min(100, progress * n / n * 100 + 100 / n);
  return (
    <section ref={sectionRef} id="device" className="w247" data-screen-label="Worn 24/7">
      <div className="w247-sticky">
        <div className="shell">
          <h2 className="display w247-title">Built to be worn 24/7</h2>
          <div className="w247-grid">
            <div className="w247-list">
              <div className="w247-track"><div className="w247-fill" style={{ height: `${Math.min(100, progress * 100)}%` }} /></div>
              {feats.map((f, i) =>
              <div key={i} className={i <= active ? "w247-item on" : "w247-item"}>
                  <span className="w247-item-t">{f.t}</span>
                  <span className="w247-item-d" style={{ maxHeight: i === active ? 120 : 0, opacity: i === active ? 1 : 0 }}>{f.d}</span>
                </div>
              )}
            </div>
            <div className="w247-stage">
              <div className="w247-spot" />
              <div className="w247-phone">
                <div className="w247-phone-screen">
                  <img src="assets/home_screen.png" alt="Rayu app home screen" />
                </div>
                <div className="w247-phone-island" />
              </div>
            </div>
          </div>
        </div>
      </div>
    </section>);

}

/* ── Footer ────────────────────────────────────────────────────────────── */
function Footer() {
  return (
    <footer>
      <div className="shell">
        <div className="footer-grid">
          <div>
            <div className="footer-logo"><img src="assets/logo-white.png" alt="rayu.ai" /></div>
            <p className="footer-tag">The nutritionist that's always on. Designed in Los Angeles, built in Seoul.</p>
          </div>
          <div className="footer-col"><h5>Product</h5><ul><li><a href="#daily">The app</a></li><li><a href="#device">The band</a></li><li><a href="#membership">Membership</a></li><li><a href="#how">Science</a></li></ul></div>
          <div className="footer-col"><h5>Company</h5><ul><li><a href="#">About</a></li><li><a href="#">Journal</a></li><li><a href="#">Careers</a></li><li><a href="#">Press</a></li></ul></div>
          <div className="footer-col"><h5>Support</h5><ul><li><a href="#">Help center</a></li><li><a href="#">Privacy</a></li><li><a href="#">Terms</a></li><li><a href="#">Contact</a></li></ul></div>
        </div>
        <div className="footer-bottom">
          <span>© 2026 Rayu Health, Inc.</span>
          <span>Made with care · Not medical advice</span>
        </div>
      </div>
    </footer>);

}

Object.assign(window, {
  DailyHealth, CompletePicture, Membership, WornBy, Worn247, Footer, Reveal, RayuScrollSection, RayuScrollFeature
});

/**
 * RayuScrollFeature — scroll-scrubbed video section (the paste-in block).
 * Markup + scrub logic preserved from the standalone rayu_scroll_section.html;
 * adapted to this project's global-React setup. As the user scrolls through the
 * section the video is scrubbed frame-by-frame (forward & back).
 *
 * The video must be encoded with all keyframes for smooth seeking:
 *   ffmpeg -i in.mp4 -an -g 1 -keyint_min 1 -x264-params scenecut=0 \
 *          -crf 20 -movflags +faststart rayu_scroll.mp4
 *
 * Props:
 *   videoSrc     – path to the scrub video. Default "assets/rayu_scroll.mp4".
 *   scrollLength – section height as a multiple of viewport (smaller = faster). Default "300vh".
 *   maxWidth     – max width of the video in px. Default 1200.
 *   smoothing    – easing toward the target frame, 0–1 (higher = snappier). Default 0.22.
 */
function RayuScrollFeature({
  videoSrc = "assets/rayu_scroll.mp4",
  scrollLength = "300vh",
  maxWidth = 1200,
  smoothing = 0.22,
}) {
  const sectionRef = useRef(null);
  const stickyRef = useRef(null);
  const videoRef = useRef(null);

  useEffect(() => {
    const section = sectionRef.current;
    const sticky = stickyRef.current;
    const video = videoRef.current;
    if (!section || !video) return;

    let duration = 0, target = 0, current = 0, ready = false, raf = 0, objUrl = null;

    const progress = () => {
      const rect = section.getBoundingClientRect();
      const total = section.offsetHeight - window.innerHeight;
      const scrolled = Math.min(Math.max(-rect.top, 0), total);
      return total > 0 ? scrolled / total : 0;
    };
    const compute = () => {
      target = progress() * duration;
      // Manual pin: position:sticky is broken on this page by an ancestor's
      // overflow-x:hidden (html, body), so toggle fixed/absolute to keep the
      // stage centered for the whole section and release it at the bottom.
      if (!sticky) return;
      const rect = section.getBoundingClientRect();
      if (rect.top <= 0 && rect.bottom >= window.innerHeight) {
        if (sticky.dataset.pin !== "fixed") {
          sticky.dataset.pin = "fixed";
          sticky.style.position = "fixed";
          sticky.style.top = "0"; sticky.style.bottom = "auto";
        }
      } else if (rect.bottom < window.innerHeight) {
        if (sticky.dataset.pin !== "bottom") {
          sticky.dataset.pin = "bottom";
          sticky.style.position = "absolute";
          sticky.style.top = "auto"; sticky.style.bottom = "0";
        }
      } else if (sticky.dataset.pin !== "top") {
        sticky.dataset.pin = "top";
        sticky.style.position = "absolute";
        sticky.style.top = "0"; sticky.style.bottom = "auto";
      }
    };

    const markReady = () => {
      if (ready) return;
      duration = video.duration || 0;
      if (!duration || !isFinite(duration)) return;
      ready = true; compute(); current = target;
      try { video.currentTime = current; } catch (e) {}
    };
    const readyEvents = ["loadedmetadata", "loadeddata", "canplay"];
    readyEvents.forEach((e) => video.addEventListener(e, markReady));
    // Load the clip through a blob URL so it is fully seekable. A file-served
    // mp4 (static server without HTTP Range support) reports an empty seekable
    // range, so currentTime writes silently fail and the scrub never moves.
    // Fetching into an in-memory blob sidesteps that; falls back to direct src.
    fetch(videoSrc)
      .then((r) => r.blob())
      .then((b) => { objUrl = URL.createObjectURL(b); video.src = objUrl; video.load(); })
      .catch(() => { video.src = videoSrc; video.load(); });

    let ticking = false;
    const onScroll = () => {
      if (!ticking) { requestAnimationFrame(() => { compute(); ticking = false; }); ticking = true; }
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", compute);
    compute(); // establish the pin state immediately, before first scroll

    const loop = () => {
      if (ready) {
        current += (target - current) * smoothing;
        if (Math.abs(target - current) < 0.0015) current = target;
        if (Math.abs(video.currentTime - current) > 0.01) {
          try { video.currentTime = current; } catch (e) {}
        }
      }
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    if (video.readyState >= 1) markReady();

    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", compute);
      readyEvents.forEach((e) => video.removeEventListener(e, markReady));
      if (objUrl) URL.revokeObjectURL(objUrl);
    };
  }, [videoSrc, smoothing]);

  return (
    <section
      ref={sectionRef}
      className="rayu-scrolly"
      data-screen-label="Scroll film"
      style={{ "--rayu-scroll-length": scrollLength, "--rayu-max-width": `${maxWidth}px` }}
    >
      <div ref={stickyRef} className="rayu-sticky">
        <video ref={videoRef} className="rayu-video" muted playsInline preload="auto">
          <source src={videoSrc} type="video/mp4" />
        </video>
      </div>
    </section>);

}

/**
 * RayuScrollSection — scroll-scrubbed video (user-provided).
 * As the user scrolls through the section, the video is scrubbed frame-by-frame.
 * Adapted from the uploaded ESM component to this project's global-React setup;
 * logic preserved verbatim.
 *
 * Props:
 *   videoSrc     – path to the scrub video (encode with all keyframes: ffmpeg -g 1 -keyint_min 1)
 *   scrollLength – section height as a multiple of viewport. Smaller = faster scrub. Default "300vh".
 *   maxWidth     – max width of the video in px. Default 1200.
 *   smoothing    – easing toward the target frame, 0–1. Higher = snappier. Default 0.22.
 */
function RayuScrollSection({
  videoSrc = "assets/rayu_scroll.mp4",
  scrollLength = "300vh",
  maxWidth = 1200,
  smoothing = 0.22,
}) {
  const sectionRef = useRef(null);
  const stickyRef = useRef(null);
  const videoRef = useRef(null);

  useEffect(() => {
    const section = sectionRef.current;
    const sticky = stickyRef.current;
    const video = videoRef.current;
    if (!section || !video) return;

    let duration = 0;
    let target = 0;
    let current = 0;
    let ready = false;
    let raf = 0;
    let objUrl = null;

    const progress = () => {
      const rect = section.getBoundingClientRect();
      const total = section.offsetHeight - window.innerHeight;
      const scrolled = Math.min(Math.max(-rect.top, 0), total);
      return total > 0 ? scrolled / total : 0;
    };
    const compute = () => {
      target = progress() * duration;
      // Manual pin: position:sticky is broken on this page by an ancestor's
      // overflow-x:hidden, so toggle fixed/absolute to keep the stage centered
      // for the whole section and release exactly at its bottom.
      if (sticky) {
        const rect = section.getBoundingClientRect();
        if (rect.top <= 0 && rect.bottom >= window.innerHeight) {
          if (sticky.dataset.pin !== "fixed") {
            sticky.dataset.pin = "fixed";
            sticky.style.position = "fixed";
            sticky.style.top = "0";
            sticky.style.bottom = "auto";
          }
        } else if (rect.bottom < window.innerHeight) {
          if (sticky.dataset.pin !== "bottom") {
            sticky.dataset.pin = "bottom";
            sticky.style.position = "absolute";
            sticky.style.top = "auto";
            sticky.style.bottom = "0";
          }
        } else if (sticky.dataset.pin !== "top") {
          sticky.dataset.pin = "top";
          sticky.style.position = "absolute";
          sticky.style.top = "0";
          sticky.style.bottom = "auto";
        }
      }
    };

    const markReady = () => {
      if (ready) return;
      duration = video.duration || 0;
      if (!duration || !isFinite(duration)) return;
      ready = true;
      compute();
      current = target;
      try {
        video.currentTime = current;
      } catch (e) {}
    };

    let ticking = false;
    const onScroll = () => {
      if (!ticking) {
        requestAnimationFrame(() => {
          compute();
          ticking = false;
        });
        ticking = true;
      }
    };

    const loop = () => {
      if (ready) {
        current += (target - current) * smoothing;
        if (Math.abs(target - current) < 0.0015) current = target;
        if (Math.abs(video.currentTime - current) > 0.01) {
          try {
            video.currentTime = current;
          } catch (e) {}
        }
      }
      raf = requestAnimationFrame(loop);
    };

    ["loadedmetadata", "loadeddata", "canplay"].forEach((e) =>
      video.addEventListener(e, markReady)
    );

    // A file-served mp4 reports an empty seekable range in this embedded
    // context, so currentTime writes silently fail. Loading it into an
    // in-memory blob URL makes it fully seekable. The resolved offline blob
    // (window.__resources) is already seekable, so use it directly.
    const resolved =
      typeof window !== "undefined" && window.__resources && window.__resources.scrollClip
        ? window.__resources.scrollClip
        : videoSrc;
    if (resolved.startsWith("blob:")) {
      video.src = resolved;
      video.load();
    } else {
      fetch(resolved)
        .then((r) => r.blob())
        .then((b) => {
          objUrl = URL.createObjectURL(b);
          video.src = objUrl;
          video.load();
        })
        .catch(() => {
          video.src = resolved;
          video.load();
        });
    }

    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", compute);
    raf = requestAnimationFrame(loop);
    if (video.readyState >= 1) markReady();

    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", compute);
      ["loadedmetadata", "loadeddata", "canplay"].forEach((e) =>
        video.removeEventListener(e, markReady)
      );
      if (objUrl) URL.revokeObjectURL(objUrl);
    };
  }, [videoSrc, smoothing]);

  return (
    <section
      ref={sectionRef}
      data-screen-label="Scroll film"
      style={{ position: "relative", height: scrollLength, background: "#000" }}
    >
      <div
        ref={stickyRef}
        style={{
          position: "absolute",
          top: 0,
          left: 0,
          right: 0,
          height: "100vh",
          width: "100%",
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          overflow: "hidden",
        }}
      >
        <video
          ref={videoRef}
          muted
          playsInline
          preload="auto"
          style={{
            maxWidth: `min(92vw, ${maxWidth}px)`,
            maxHeight: "90vh",
            width: "auto",
            height: "auto",
            display: "block",
          }}
        />
      </div>
    </section>
  );
}
