/* global React */
const { useEffect, useRef, useState } = React;

// ---------- Hooks ----------
function useReveal() {
  useEffect(() => {
    const els = document.querySelectorAll(".reveal, .reveal-mask, .reveal-write");
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) {
          e.target.classList.add("in");
          io.unobserve(e.target);
        }
      });
    }, { threshold: 0.15, rootMargin: "0px 0px -8% 0px" });
    els.forEach((el) => io.observe(el));
    return () => io.disconnect();
  }, []);
}

// ---------- Loader ----------
function Loader({ onDone }) {
  const [count, setCount] = useState(0);
  const [exit, setExit] = useState(false);

  useEffect(() => {
    const start = performance.now();
    const dur = 2400;
    let raf;
    const tick = (t) => {
      const p = Math.min(1, (t - start) / dur);
      const eased = 1 - Math.pow(1 - p, 2.4);
      setCount(Math.floor(eased * 100));
      if (p < 1) raf = requestAnimationFrame(tick);else
      {
        setTimeout(() => setExit(true), 250);
        setTimeout(() => onDone && onDone(), 1300);
      }
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [onDone]);

  return (
    <div className={"loader" + (exit ? " exit" : "")}>
      <div className="loader__pops">
        {[1, 2, 3, 4, 5, 6].map((n) =>
          <img key={n} className={"loader__pop loader__pop--" + n} src={"uploads/pop-" + n + ".webp"} alt="" decoding="async" />
        )}
      </div>
      <div className="loader__inner">
        <div className="loader__name">
          <span>Ines&nbsp;</span><span>Madouri</span>
        </div>
        <div className="loader__sub">Studio d'architecture d'intérieur</div>
        <div className="loader__count">{String(count).padStart(3, "0")} / 100</div>
      </div>
      <div className="loader__bar"></div>
    </div>);

}

// ---------- Nav ----------
// At the top of the page: full-width transparent bar (logo + e-mail).
// Once scrolled: a floating centred pill. Scrolling down shrinks it to a
// tiny logo-only pill; scrolling up (or hovering) brings the CTA back.
function Nav() {
  const [dark, setDark] = useState(false);
  const [pill, setPill] = useState(false);
  const [mini, setMini] = useState(false);
  useEffect(() => {
    let lastY = window.scrollY, dir = 0, raf = 0;
    const update = () => {
      raf = 0;
      const y = window.scrollY;
      if (y > lastY + 4) dir = 1; else if (y < lastY - 4) dir = -1;
      lastY = y;
      const hero = document.querySelector(".hero");
      const h = hero ? hero.offsetHeight : window.innerHeight;
      // dark ink on the light sections (intro, projects, press); light over hero / dark sections
      let onLight = y + 60 > h;
      document.querySelectorAll(".services, .banner, .contact, .method").forEach((el) => {
        const r = el.getBoundingClientRect();
        if (r.top <= 60 && r.bottom > 60) onLight = false;
      });
      setDark(onLight);
      const scrolled = y > 90;
      setPill(scrolled);
      setMini(scrolled && dir === 1);
    };
    const onScroll = () => { if (!raf) raf = requestAnimationFrame(update); };
    update();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => { window.removeEventListener("scroll", onScroll); cancelAnimationFrame(raf); };
  }, []);
  return (
    <header className={"nav" + (dark ? " nav--dark" : "") + (pill ? " is-pill" : "") + (mini ? " is-mini" : "")}>
      <div className="nav__bar">
        <a href="#" className="nav__brand" aria-label="Ines Madouri, retour en haut">
          <img src="uploads/logo-inesmadouri.png" alt="" className="nav__logo" />
        </a>
        <a href="mailto:studio@ines-madouri.fr" className="nav__cta">
          <span className="nav__cta-full">studio@ines-madouri.fr</span>
          <span className="nav__cta-short">Contact</span>
        </a>
      </div>
    </header>);

}

// ---------- Hero ----------
function Hero() {
  const [show, setShow] = useState(false);

  useEffect(() => {
    const t = setTimeout(() => setShow(true), 60);
    return () => clearTimeout(t);
  }, []);

  return (
    <section className={"hero" + (show ? " show" : "")}>
      <div className="hero__media">
        <video className="hero__video" src="uploads/hero-video.mp4" autoPlay muted loop playsInline poster="uploads/hero-poster.jpg"></video>
        <div className="hero__grain"></div>
      </div>
      <div className="hero__inner">
        <div className="hero__meta">
          <div className="hero__meta-block">
            Côte d'Azur, Antibes
          </div>
        </div>
        <h1 className="hero__title">
          <span className="row"><span>Ines&nbsp;</span></span>
          <span className="row"><span className="it">Madouri</span></span>
        </h1>
        <div className="hero__eyebrow">
          <span>Studio architecture d'intérieur, décoration d'intérieur, design d'espace</span>
        </div>
      </div>
    </section>);

}

// ---------- About / Philosophy ----------
function About() {
  return (
    <section className="intro" id="about">
      <div className="intro__grid">
        <div className="intro__side">
          <div className="intro__portrait reveal">
            <img src="uploads/portrait-ines.webp" alt="Ines Madouri" width="900" height="1080" loading="lazy" decoding="async" />
          </div>
        </div>
        <p className="intro__text reveal">
          Je conçois des intérieurs qui ont du <em>sens et du caractère</em>. J'aime créer des espaces où matières, couleurs, volumes et lumière s'accordent pour donner naissance à une atmosphère singulière, chaleureuse et pleine de personnalité.
          <br /><br />
          Nourrie par l'architecture, le design et les références qui m'inspirent, je cherche à composer des intérieurs <em>cohérents, sensibles et intemporels</em>, où chaque choix participe à l'identité du lieu.
        </p>
      </div>
    </section>);

}

// ---------- Projects ----------
function Projects() {
  const projects = window.PROJECTS;
  return (
    <section className="projects" id="projects">
      <div className="projects__head">
        <h2 className="projects__title reveal">
           <em>Mes projets</em>
        </h2>
      </div>
      <div className="projects__list">
        {projects.map((p) =>
        <article
          key={p.num}
          className={`project project--${p.layout}${p.chrome ? " project--chrome" : ""}${p.gframe ? " project--gframe gframe-" + p.gframe : " frame-" + (p.frame || 1)} reveal`}
          style={{ "--d": ((parseInt(p.num, 10) - 1) % 2) * 320 + "ms" }}
          data-cursor="view"
          onClick={() => { if (p.href) window.location.href = p.href; }}>
          
            <div className="project__media">
              <div className="project__media-frame">
                <img src={p.img} alt={p.name} loading="lazy" />
              </div>
            </div>
            <div className="project__caption">
              <div>
                <h3 className="project__name">{p.name}</h3>
                <div className="project__sub">{p.sub}</div>
              </div>
            </div>
          </article>
        )}
      </div>
    </section>);

}

// ---------- Services ----------
function Services() {
  const list = window.SERVICES;
  return (
    <section className="services" id="services" style={{ backgroundColor: "#520B20" }}>
      <div className="services__inner">
        <div className="services__head">
          <div className="eyebrow">Savoir-faire</div>
          <h2 className="reveal">
            Une pratique <em>polyvalente</em>, du concept au détail.
          </h2>
          <p className="reveal">
            De la recherche d'aménagement à la modélisation, du dessin technique à l'image finale, chaque étape participe à construire un projet cohérent, précis et sensible.
          </p>
        </div>
        <div className="services__list">
          {list.map((s) =>
          <div key={s.num} className="service" data-cursor="hover">
              <div className="service__num">{s.num}</div>
              <div className="service__body">
                <div className="service__name" style={{ color: "rgb(255, 255, 255)" }}>{s.name}</div>
                <div className="service__desc">{s.desc}</div>
              </div>
            </div>
          )}
        </div>
      </div>
    </section>);

}

// ---------- Horizontal gallery (scroll-driven) ----------
// Desktop: pinned section, the track slides horizontally with the scroll.
// Mobile (≤ 880px): a native swipe carousel with scroll-snap; the progress
// bar and the counter follow the track's own scroll position.
function Banner() {
  const wrap = useRef(null);
  const track = useRef(null);
  const bar = useRef(null);
  const [idx, setIdx] = useState(0);
  const imgs = [1, 2, 3, 4, 5, 6].map((n) => "uploads/gal-" + n + ".webp");
  useEffect(() => {
    const w = wrap.current, t = track.current;
    if (!w || !t) return;
    const mq = window.matchMedia("(max-width: 880px)");

    const setupPinned = () => {
      let max = 0, raf = 0;
      const measure = () => {
        max = Math.max(0, t.scrollWidth - window.innerWidth);
        w.style.height = (window.innerHeight + max) + "px";
        update();
      };
      const update = () => {
        const r = w.getBoundingClientRect();
        const travel = Math.max(1, r.height - window.innerHeight);
        const p = Math.min(1, Math.max(0, -r.top / travel));
        t.style.transform = "translate3d(" + (-p * max) + "px,0,0)";
        if (bar.current) bar.current.style.transform = "scaleX(" + p + ")";
      };
      const onScroll = () => { if (!raf) raf = requestAnimationFrame(() => { raf = 0; update(); }); };
      measure();
      // re-measure once images have real widths
      const imgsEl = t.querySelectorAll("img");
      let pending = imgsEl.length;
      imgsEl.forEach((im) => { if (im.complete) { if (--pending === 0) measure(); } else im.addEventListener("load", () => { if (--pending === 0) measure(); }, { once: true }); });
      const ro = new ResizeObserver(measure);
      ro.observe(t);
      window.addEventListener("scroll", onScroll, { passive: true });
      window.addEventListener("resize", measure);
      return () => { window.removeEventListener("scroll", onScroll); window.removeEventListener("resize", measure); ro.disconnect(); cancelAnimationFrame(raf); };
    };

    const setupSwipe = () => {
      w.style.height = "";
      t.style.transform = "";
      let raf = 0;
      const update = () => {
        raf = 0;
        const max = t.scrollWidth - t.clientWidth;
        if (bar.current) bar.current.style.transform = "scaleX(" + (max > 0 ? t.scrollLeft / max : 1) + ")";
        // nearest item to the viewport centre
        const c = t.scrollLeft + t.clientWidth / 2;
        let best = 0, bd = Infinity;
        Array.prototype.forEach.call(t.children, (el, i) => {
          const d = Math.abs(el.offsetLeft + el.offsetWidth / 2 - c);
          if (d < bd) { bd = d; best = i; }
        });
        setIdx(best);
      };
      const onScroll = () => { if (!raf) raf = requestAnimationFrame(update); };
      update();
      t.addEventListener("scroll", onScroll, { passive: true });
      return () => { t.removeEventListener("scroll", onScroll); cancelAnimationFrame(raf); };
    };

    let cleanup = () => {};
    const setup = () => { cleanup(); cleanup = mq.matches ? setupSwipe() : setupPinned(); };
    setup();
    mq.addEventListener("change", setup);
    return () => { cleanup(); mq.removeEventListener("change", setup); };
  }, []);
  return (
    <section className="hgal" ref={wrap}>
      <div className="hgal__sticky">
        <div className="hgal__track" ref={track}>
          {imgs.map((src, i) =>
            <figure key={i} className="hgal__item">
              <img src={src} alt="" loading="lazy" decoding="async" />
            </figure>
          )}
        </div>
        <div className="hgal__progress"><span ref={bar}></span></div>
        <div className="hgal__meta" aria-hidden="true">
          <span>{String(idx + 1).padStart(2, "0")} / {String(imgs.length).padStart(2, "0")}</span>
          <span>Glisser →</span>
        </div>
      </div>
    </section>);
}

// ---------- Press ----------
function Press() {
  const list = window.PRESS;
  return (
    <section className="press">
      <div className="press__inner">
        <div className="press__head">
          <div>
            <div className="eyebrow eyebrow--script">Logiciels</div>
            <h2 className="press__title reveal">Outils <em>de conception</em></h2>
          </div>
        </div>
        <div className="press__list">
          {list.map((p, i) =>
          <div key={i} className="press__item"
            onMouseMove={(e) => { const r = e.currentTarget.getBoundingClientRect(); const x = (e.clientX - r.left) / r.width - .5; const y = (e.clientY - r.top) / r.height - .5; e.currentTarget.style.setProperty("--ry", (x * 8) + "deg"); e.currentTarget.style.setProperty("--rx", (-y * 8) + "deg"); }}
            onMouseLeave={(e) => { e.currentTarget.style.setProperty("--ry", "0deg"); e.currentTarget.style.setProperty("--rx", "0deg"); }}>
              <div className="meta">{p.num}</div>
              <div className="name">{p.name}</div>
              <div className="label">{p.label}</div>
            </div>
          )}
        </div>
      </div>
    </section>);

}

// ---------- Journal ----------
function Journal() {
  const list = window.JOURNAL;
  return (
    <section className="journal" id="journal">
      <div className="journal__inner">
        <div className="journal__head">
          <div>
            <div className="eyebrow">Carnet du studio</div>
            <h2 className="journal__title reveal">Le <em>journal</em></h2>
          </div>
          <div className="projects__nav"><a href="#">Toutes les entrées →</a></div>
        </div>
        <div className="journal__grid">
          {list.map((e, i) =>
          <article key={i} className="entry reveal" data-cursor="view">
              <div className="entry__media">
                <img src={e.img} alt={e.title} loading="lazy" />
              </div>
              <div className="entry__meta">
                <span>{e.cat}</span>
                <span>{e.date}</span>
              </div>
              <h3 className="entry__title">{e.title}</h3>
            </article>
          )}
        </div>
      </div>
    </section>);

}

// ---------- Contact ----------
function Contact() {
  return (
    <section className="contact" id="contact" style={{ backgroundColor: "#520B20" }}>
      <div className="contact__inner">
        <div className="contact__top">
          <div className="contact__intro">
            <h2 className="contact__hero">
              Un projet en tête ?<br />
              Parlons-en.
              <a href="mailto:studio@ines-madouri.fr" aria-label="Écrire un e-mail"><span className="arrow"></span></a>
            </h2>
            <p className="contact__lead">
              Vous êtes architecte, architecte d'intérieur ou professionnel de l'aménagement
              et avez besoin d'un renfort ponctuel ? Échangeons autour de votre projet.
            </p>
            <a href="mailto:studio@ines-madouri.fr" className="contact__cta">→ Prendre contact</a>
          </div>
          <div className="contact__col">
            <h4>Contact</h4>
            <p className="contact__name">Inès Madouri</p>
            <p>Architecture d'intérieur</p>
            <a href="mailto:studio@ines-madouri.fr">studio@ines-madouri.fr</a>
            <a href="tel:+33749373700">07 49 37 37 00</a>
            <div className="contact__social">
              <a href="#">Instagram ↗</a>
              <a href="#">LinkedIn ↗</a>
            </div>
          </div>
        </div>

        <div className="contact__grid"></div>

        <div className="contact__wordmark reveal-write" aria-label="Ines Madouri">
          {"Ines Madouri".split("").map((ch, i) =>
            <span key={i} className="contact__wordmark-ch" style={{ animationDelay: (i * 0.22) + "s" }}>{ch === " " ? "\u00A0" : ch}</span>
          )}
        </div>

        <div className="contact__bottom">
          <span>© 2026 Inès Madouri. Tous droits réservés</span>
        </div>
      </div>
    </section>);

}

// ---------- Custom Cursor ----------
function Cursor() {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let x = 0,y = 0,tx = 0,ty = 0;
    let raf;
    const move = (e) => {tx = e.clientX;ty = e.clientY;el.classList.add("ready");};
    const loop = () => {
      x += (tx - x) * 0.22;
      y += (ty - y) * 0.22;
      el.style.left = x + "px";
      el.style.top = y + "px";
      raf = requestAnimationFrame(loop);
    };
    const over = (e) => {
      const t = e.target.closest("[data-cursor], a, button");
      if (!t || t.closest(".method")) {el.classList.remove("hover", "view");return;}
      const k = t.getAttribute("data-cursor");
      if (k === "view") {el.classList.add("view");el.classList.remove("hover");} else
      {el.classList.add("hover");el.classList.remove("view");}
    };
    window.addEventListener("mousemove", move);
    window.addEventListener("mouseover", over);
    raf = requestAnimationFrame(loop);
    return () => {
      window.removeEventListener("mousemove", move);
      window.removeEventListener("mouseover", over);
      cancelAnimationFrame(raf);
    };
  }, []);
  return <div ref={ref} className="cursor"></div>;
}

// ---------- App ----------
function App() {
  const [loaded, setLoaded] = useState(false);
  useEffect(() => {
    if (loaded) document.body.classList.remove("no-scroll");else
    document.body.classList.add("no-scroll");
  }, [loaded]);

  useReveal();

  return (
    <>
      {!loaded && <Loader onDone={() => setLoaded(true)} />}
      <Cursor />
      <Nav />
      <Hero />
      <About />
      <Projects />
      <Services />
      <Banner />
      {window.Method ? <window.Method /> : null}
      <Press />
      <Contact />
    </>);

}

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