/* MAKE MEDIA — composants partagés : hooks, Placeholder, Header, Hero, Footer */
const { useState, useEffect, useRef, useCallback, useMemo } = React;

const NAV = [
{ label: "Coulisses", href: "#bts" },
{ label: "À propos", href: "#apropos" },
{ label: "Contact", href: "#contact" }];

/* ---------- Révélation au scroll (registre + rect check) ----------
   On évite IntersectionObserver : déterministe quel que soit l'état
   de composition de l'iframe d'aperçu. */
const _revealEls = new Set();
let _revThrottle = false;
function _checkReveals() {
  const vh = window.innerHeight;
  _revealEls.forEach((el) => {
    const r = el.getBoundingClientRect();
    if (r.top < vh * 0.92 && r.bottom > -40) {
      el.classList.add("in");
      _revealEls.delete(el);
    }
  });
}
function _scheduleReveal() {
  if (_revThrottle) return;
  _revThrottle = true;
  setTimeout(() => {_revThrottle = false;_checkReveals();}, 60);
}
if (typeof window !== "undefined" && !window.__mmRevealInit) {
  window.__mmRevealInit = true;
  window.addEventListener("scroll", _scheduleReveal, { passive: true });
  window.addEventListener("resize", _scheduleReveal);
}
function useReveal() {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    _revealEls.add(el);
    // setTimeout (et non rAF) : se déclenche même si l'iframe n'est pas peinte
    const t0 = setTimeout(_checkReveals, 0);
    const t1 = setTimeout(_checkReveals, 160);
    return () => {_revealEls.delete(el);clearTimeout(t0);clearTimeout(t1);};
  }, []);
  return ref;
}

/* Composant Reveal : translateY + fade */
function Reveal({ as = "div", className = "", delay = 0, children, ...rest }) {
  const ref = useReveal();
  const Tag = as;
  return (
    <Tag ref={ref} className={`reveal ${className}`} style={{ "--d": `${delay}ms`, ...(rest.style || {}) }} {...rest}>
      {children}
    </Tag>);

}

/* Révélation par masque (image) */
function MaskReveal({ className = "", delay = 0, children, style }) {
  const ref = useReveal({ threshold: 0.12 });
  return (
    <div ref={ref} className={`maskwrap ${className}`} style={{ "--d": `${delay}ms`, ...style }}>
      <div className="maskfill">{children}</div>
    </div>);

}

/* Titre révélé ligne par ligne */
function LineReveal({ lines, className = "", delayStep = 90, style }) {
  const ref = useReveal({ threshold: 0.2 });
  return (
    <span ref={ref} className={className} style={style}>
      {lines.map((ln, i) =>
      <span key={i} className="line-mask" style={{ "--d": `${i * delayStep}ms` }}>
          <span style={{ textAlign: "left", margin: "0px", padding: "0px" }}>{ln}</span>
        </span>
      )}
    </span>);

}

/* Effet machine à écrire : tape puis efface une liste de mots en boucle */
function Typewriter({
  words,
  speed = 90,
  deleteSpeed = 45,
  waitTime = 1600,
  initialDelay = 0,
  loop = true,
  cursorChar = "|",
  className = "",
  cursorClassName = "typewriter__cursor" })
{
  const [display, setDisplay] = useState("");
  const [wordIndex, setWordIndex] = useState(0);
  const [charIndex, setCharIndex] = useState(0);
  const [deleting, setDeleting] = useState(false);
  const [started, setStarted] = useState(initialDelay === 0);

  useEffect(() => {
    if (started) return;
    const t = setTimeout(() => setStarted(true), initialDelay);
    return () => clearTimeout(t);
  }, [started, initialDelay]);

  useEffect(() => {
    if (!started) return;
    const current = words[wordIndex];
    let t;

    if (deleting) {
      if (display === "") {
        setDeleting(false);
        if (wordIndex === words.length - 1 && !loop) return;
        setWordIndex((i) => (i + 1) % words.length);
        setCharIndex(0);
      } else {
        t = setTimeout(() => setDisplay((d) => d.slice(0, -1)), deleteSpeed);
      }
    } else {
      if (charIndex < current.length) {
        t = setTimeout(() => {
          setDisplay((d) => d + current[charIndex]);
          setCharIndex((i) => i + 1);
        }, speed);
      } else if (words.length > 1 || loop) {
        t = setTimeout(() => setDeleting(true), waitTime);
      }
    }
    return () => clearTimeout(t);
  }, [started, display, charIndex, deleting, wordIndex, words, speed, deleteSpeed, waitTime, loop]);

  return (
    <span className={`typewriter ${className}`}>
      {/* fantôme invisible : réserve la largeur RÉELLE du mot le plus large
          (tous les mots superposés) → aucun reflow et aucune lettre coupée */}
      <span className="typewriter__sizer" aria-hidden="true">
        {words.map((w, i) =>
        <span key={i}>{w}<span className="typewriter__cursor-space">{cursorChar}</span></span>
        )}
      </span>
      <span className="typewriter__text">
        <span>{display}</span>
        <span className={cursorClassName} aria-hidden="true">{cursorChar}</span>
      </span>
    </span>);

}

/* ---------- Texte décrypté (React Bits, adapté sans dépendance "motion") ---------- */
const DTXT_STYLES = {
  wrapper: { display: "inline-block", whiteSpace: "pre-wrap" },
  srOnly: {
    position: "absolute", width: "1px", height: "1px", padding: 0, margin: "-1px",
    overflow: "hidden", clip: "rect(0,0,0,0)", border: 0
  }
};

function DecryptedText({
  text,
  speed = 50,
  maxIterations = 10,
  sequential = false,
  revealDirection = "start",
  useOriginalCharsOnly = false,
  characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$%^&*()_+",
  className = "",
  parentClassName = "",
  encryptedClassName = "",
  animateOn = "hover",
  clickMode = "once",
  ...props
}) {
  const [displayText, setDisplayText] = useState(text);
  const [isAnimating, setIsAnimating] = useState(false);
  const [revealedIndices, setRevealedIndices] = useState(new Set());
  const [hasAnimated, setHasAnimated] = useState(false);
  const [isDecrypted, setIsDecrypted] = useState(animateOn !== "click");
  const [direction, setDirection] = useState("forward");

  const containerRef = useRef(null);
  const orderRef = useRef([]);
  const pointerRef = useRef(0);
  const intervalRef = useRef(null);

  const availableChars = useMemo(() => {
    return useOriginalCharsOnly ?
    Array.from(new Set(text.split(""))).filter((char) => char !== " ") :
    characters.split("");
  }, [useOriginalCharsOnly, text, characters]);

  const shuffleText = useCallback(
    (originalText, currentRevealed) => {
      return originalText.
      split("").
      map((char, i) => {
        if (char === " ") return " ";
        if (currentRevealed.has(i)) return originalText[i];
        return availableChars[Math.floor(Math.random() * availableChars.length)];
      }).
      join("");
    },
    [availableChars]
  );

  const computeOrder = useCallback(
    (len) => {
      const order = [];
      if (len <= 0) return order;
      if (revealDirection === "start") {
        for (let i = 0; i < len; i++) order.push(i);
        return order;
      }
      if (revealDirection === "end") {
        for (let i = len - 1; i >= 0; i--) order.push(i);
        return order;
      }
      const middle = Math.floor(len / 2);
      let offset = 0;
      while (order.length < len) {
        if (offset % 2 === 0) {
          const idx = middle + offset / 2;
          if (idx >= 0 && idx < len) order.push(idx);
        } else {
          const idx = middle - Math.ceil(offset / 2);
          if (idx >= 0 && idx < len) order.push(idx);
        }
        offset++;
      }
      return order.slice(0, len);
    },
    [revealDirection]
  );

  const fillAllIndices = useCallback(() => {
    const s = new Set();
    for (let i = 0; i < text.length; i++) s.add(i);
    return s;
  }, [text]);

  const removeRandomIndices = useCallback((set, count) => {
    const arr = Array.from(set);
    for (let i = 0; i < count && arr.length > 0; i++) {
      const idx = Math.floor(Math.random() * arr.length);
      arr.splice(idx, 1);
    }
    return new Set(arr);
  }, []);

  const encryptInstantly = useCallback(() => {
    const emptySet = new Set();
    setRevealedIndices(emptySet);
    setDisplayText(shuffleText(text, emptySet));
    setIsDecrypted(false);
  }, [text, shuffleText]);

  const triggerDecrypt = useCallback(() => {
    if (sequential) {
      orderRef.current = computeOrder(text.length);
      pointerRef.current = 0;
      setRevealedIndices(new Set());
    } else {
      setRevealedIndices(new Set());
    }
    setDirection("forward");
    setIsAnimating(true);
  }, [sequential, computeOrder, text.length]);

  const triggerReverse = useCallback(() => {
    if (sequential) {
      orderRef.current = computeOrder(text.length).slice().reverse();
      pointerRef.current = 0;
      setRevealedIndices(fillAllIndices());
      setDisplayText(shuffleText(text, fillAllIndices()));
    } else {
      setRevealedIndices(fillAllIndices());
      setDisplayText(shuffleText(text, fillAllIndices()));
    }
    setDirection("reverse");
    setIsAnimating(true);
  }, [sequential, computeOrder, fillAllIndices, shuffleText, text]);

  useEffect(() => {
    if (!isAnimating) return;

    let currentIteration = 0;

    const getNextIndex = (revealedSet) => {
      const textLength = text.length;
      switch (revealDirection) {
        case "start":
          return revealedSet.size;
        case "end":
          return textLength - 1 - revealedSet.size;
        case "center":{
            const middle = Math.floor(textLength / 2);
            const offset = Math.floor(revealedSet.size / 2);
            const nextIndex = revealedSet.size % 2 === 0 ? middle + offset : middle - offset - 1;

            if (nextIndex >= 0 && nextIndex < textLength && !revealedSet.has(nextIndex)) {
              return nextIndex;
            }

            for (let i = 0; i < textLength; i++) {
              if (!revealedSet.has(i)) return i;
            }
            return 0;
          }
        default:
          return revealedSet.size;
      }
    };

    intervalRef.current = setInterval(() => {
      setRevealedIndices((prevRevealed) => {
        if (sequential) {
          if (direction === "forward") {
            if (prevRevealed.size < text.length) {
              const nextIndex = getNextIndex(prevRevealed);
              const newRevealed = new Set(prevRevealed);
              newRevealed.add(nextIndex);
              setDisplayText(shuffleText(text, newRevealed));
              return newRevealed;
            } else {
              clearInterval(intervalRef.current);
              setIsAnimating(false);
              setIsDecrypted(true);
              return prevRevealed;
            }
          }
          if (direction === "reverse") {
            if (pointerRef.current < orderRef.current.length) {
              const idxToRemove = orderRef.current[pointerRef.current++];
              const newRevealed = new Set(prevRevealed);
              newRevealed.delete(idxToRemove);
              setDisplayText(shuffleText(text, newRevealed));
              if (newRevealed.size === 0) {
                clearInterval(intervalRef.current);
                setIsAnimating(false);
                setIsDecrypted(false);
              }
              return newRevealed;
            } else {
              clearInterval(intervalRef.current);
              setIsAnimating(false);
              setIsDecrypted(false);
              return prevRevealed;
            }
          }
        } else {
          if (direction === "forward") {
            setDisplayText(shuffleText(text, prevRevealed));
            currentIteration++;
            if (currentIteration >= maxIterations) {
              clearInterval(intervalRef.current);
              setIsAnimating(false);
              setDisplayText(text);
              setIsDecrypted(true);
            }
            return prevRevealed;
          }

          if (direction === "reverse") {
            let currentSet = prevRevealed;
            if (currentSet.size === 0) {
              currentSet = fillAllIndices();
            }
            const removeCount = Math.max(1, Math.ceil(text.length / Math.max(1, maxIterations)));
            const nextSet = removeRandomIndices(currentSet, removeCount);
            setDisplayText(shuffleText(text, nextSet));
            currentIteration++;
            if (nextSet.size === 0 || currentIteration >= maxIterations) {
              clearInterval(intervalRef.current);
              setIsAnimating(false);
              setIsDecrypted(false);
              setDisplayText(shuffleText(text, new Set()));
              return new Set();
            }
            return nextSet;
          }
        }
        return prevRevealed;
      });
    }, speed);

    return () => clearInterval(intervalRef.current);
  }, [
  isAnimating,
  text,
  speed,
  maxIterations,
  sequential,
  revealDirection,
  shuffleText,
  direction,
  fillAllIndices,
  removeRandomIndices,
  characters,
  useOriginalCharsOnly]
  );

  const handleClick = () => {
    if (animateOn !== "click") return;

    if (clickMode === "once") {
      if (isDecrypted) return;
      setDirection("forward");
      triggerDecrypt();
    }

    if (clickMode === "toggle") {
      if (isDecrypted) {
        triggerReverse();
      } else {
        setDirection("forward");
        triggerDecrypt();
      }
    }
  };

  const triggerHoverDecrypt = useCallback(() => {
    if (isAnimating) return;

    setRevealedIndices(new Set());
    setIsDecrypted(false);
    setDisplayText(text);
    setDirection("forward");
    setIsAnimating(true);
  }, [isAnimating, text]);

  const resetToPlainText = useCallback(() => {
    clearInterval(intervalRef.current);
    setIsAnimating(false);
    setRevealedIndices(new Set());
    setDisplayText(text);
    setIsDecrypted(true);
    setDirection("forward");
  }, [text]);

  useEffect(() => {
    if (animateOn !== "view" && animateOn !== "inViewHover") return;

    const observerCallback = (entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting && !hasAnimated) {
          triggerDecrypt();
          setHasAnimated(true);
        }
      });
    };

    const observerOptions = {
      root: null,
      rootMargin: "0px",
      threshold: 0.1
    };

    const observer = new IntersectionObserver(observerCallback, observerOptions);
    const currentRef = containerRef.current;
    if (currentRef) {
      observer.observe(currentRef);
    }

    return () => {
      if (currentRef) {
        observer.unobserve(currentRef);
      }
    };
  }, [animateOn, hasAnimated, triggerDecrypt]);

  useEffect(() => {
    if (animateOn === "click") {
      encryptInstantly();
    } else {
      setDisplayText(text);
      setIsDecrypted(true);
    }
    setRevealedIndices(new Set());
    setDirection("forward");
  }, [animateOn, text, encryptInstantly]);

  const animateProps =
  animateOn === "hover" || animateOn === "inViewHover" ?
  {
    onMouseEnter: triggerHoverDecrypt,
    onMouseLeave: resetToPlainText
  } :
  animateOn === "click" ?
  {
    onClick: handleClick
  } :
  {};

  return (
    <span className={parentClassName} ref={containerRef} style={DTXT_STYLES.wrapper} {...animateProps} {...props}>
      <span style={DTXT_STYLES.srOnly}>{displayText}</span>

      <span aria-hidden="true">
        {displayText.split("").map((char, index) => {
          const isRevealedOrDone = revealedIndices.has(index) || !isAnimating && isDecrypted;

          return (
            <span key={index} className={isRevealedOrDone ? className : encryptedClassName}>
              {char}
            </span>);

        })}
      </span>
    </span>);

}

/* ---------- Placeholder média rayé ---------- */
function Placeholder({ label, dark = false, video = false, style, className = "" }) {
  return (
    <div className={`ph ${dark ? "ph--dark" : ""} ${video ? "ph--video" : ""} ${className}`} style={style}>
      <span className="ph__cap">{label}</span>
    </div>);

}

/* ---------- Parallaxe ---------- */
function useParallax(speed = 0.12) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let raf = 0;
    const onScroll = () => {
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => {
        const amt = parseFloat(getComputedStyle(document.documentElement).getPropertyValue("--parallax")) || 0;
        const r = el.getBoundingClientRect();
        const center = r.top + r.height / 2 - window.innerHeight / 2;
        el.style.transform = `translate3d(0, ${(-center * speed * amt).toFixed(1)}px, 0)`;
      });
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {window.removeEventListener("scroll", onScroll);window.removeEventListener("resize", onScroll);cancelAnimationFrame(raf);};
  }, [speed]);
  return ref;
}


/* ============================================================
   HEADER
   ============================================================ */
function Header({ theme = "hero" }) {
  const { SERVICES } = window.MM_DATA;
  const [hidden, setHidden] = useState(false);
  const [scrolled, setScrolled] = useState(false);
  const [mobileOpen, setMobileOpen] = useState(false);
  const lastY = useRef(0);

  useEffect(() => {
    const onScroll = () => {
      const y = window.scrollY;
      setScrolled(y > 12);
      setHidden(y > 220 && y > lastY.current);
      lastY.current = y;
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);

  useEffect(() => {
    document.body.style.overflow = mobileOpen ? "hidden" : "";
  }, [mobileOpen]);

  const go = (href) => (e) => {
    e.preventDefault();
    setMobileOpen(false);
    if (href === "#top") {
      if (document.querySelector("#top")) {window.scrollTo({ top: 0, behavior: "smooth" });} else {window.location.href = "index.html";}
      return;
    }
    const el = document.querySelector(href);
    if (el) {window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 70, behavior: "smooth" });} else
    {window.location.href = "index.html" + href;}
  };

  return (
    <React.Fragment>
      <header className="header" data-hidden={hidden && !mobileOpen} data-scrolled={theme === "light" ? true : scrolled}>
        <div className="wrap header__bar">
          <a href="#top" className="brand" onClick={go("#top")} aria-label="Make Media — accueil">
            <img src="assets/make-media-logo.png" alt="Make Media" />
            <span className="brand__txt">Make Media</span>
          </a>
          <nav className="nav">
            <div className="has-sub">
              <a href="#services" className="nav__item" onClick={go("#services")}>
                Services <span className="caret" aria-hidden="true"></span>
              </a>
              <div className="submenu">
                {SERVICES.map((s) =>
                <a key={s.id} href={window.MM_serviceHref(s)}>
                    <span className="sidx">{s.idx}</span>{s.name}
                  </a>
                )}
                <a href="location.html">
                  <span className="sidx">07</span>Location
                </a>
              </div>
            </div>
            {NAV.map((n) =>
            <a key={n.href} href={n.href} className="nav__item" onClick={go(n.href)}>{n.label}</a>
            )}
            <a href="/galerie" className="nav__item">Galeries</a>
            <a href="location.html" className="btn nav__cta" style={{ padding: "0.7em 1.2em" }}>
              Location de matériel
            </a>
          </nav>
          <button className="burger" aria-label="Menu" onClick={() => setMobileOpen((v) => !v)}>
            <span></span><span></span><span></span>
          </button>
        </div>
      </header>

      <div className={`mobile-panel ${mobileOpen ? "open" : ""}`}>
        <div className="mobile-panel__top">
          <span className="brand__txt" style={{ color: "var(--paper)" }}>Make Media</span>
          <button className="burger" style={{ borderColor: "rgba(255,255,255,.3)" }} aria-label="Fermer" onClick={() => setMobileOpen(false)}>
            <span style={{ background: "#fff", transform: "rotate(45deg) translate(2px,2px)" }}></span>
            <span style={{ background: "#fff", opacity: 0 }}></span>
            <span style={{ background: "#fff", transform: "rotate(-45deg) translate(2px,-2px)" }}></span>
          </button>
        </div>
        <nav className="mobile-nav">
          <a href="#services" onClick={go("#services")}>Services</a>
          <div className="mobile-sub">
            {SERVICES.map((s) => <a key={s.id} href={window.MM_serviceHref(s)}>{s.name}</a>)}
            <a href="location.html">Location</a>
          </div>
          {NAV.map((n) => <a key={n.href} href={n.href} onClick={go(n.href)}>{n.label}</a>)}
          <a href="/galerie">Galeries</a>
        </nav>
        <a href="location.html" className="btn" style={{ "--bg": "#fff", "--fg": "#0b0b0b", "--bd": "#fff", alignSelf: "flex-start" }}>
          Location de matériel
        </a>
      </div>
    </React.Fragment>);

}

/* ============================================================
   HERO
   ============================================================ */
function Hero({ layout }) {
  const media = useParallax(0.08);
  const videoRef = useRef(null);
  const scrollTo = (href) => (e) => {
    e.preventDefault();
    const el = document.querySelector(href);
    if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 70, behavior: "smooth" });
  };
  // Certains navigateurs ne lancent pas l'autoplay muet sans un nudge explicite
  useEffect(() => {
    const v = videoRef.current;
    if (!v) return;
    const tryPlay = () => { const p = v.play(); if (p && p.catch) p.catch(() => {}); };
    tryPlay();
    v.addEventListener("canplay", tryPlay, { once: true });
    return () => v.removeEventListener("canplay", tryPlay);
  }, []);
  return (
    <section className="hero" id="top" data-layout={layout} data-screen-label="Hero">
      <div className="hero__media" ref={media} style={{ height: "118%", top: "-9%" }}>
        <video
          ref={videoRef}
          className="hero__video"
          src="assets/video/hero.mp4"
          autoPlay
          muted
          loop
          playsInline
          preload="auto"
          aria-hidden="true" />
      </div>
      <div className="hero__scrim"></div>

      <div className="wrap hero__inner">
        <div className="hero__logo reveal in">
          <img src="assets/make-media-logo.png" alt="Make Media" style={{ objectFit: "cover", padding: "0px 1px 5px 0px", width: "160px", margin: "0px 100px 0px 6px" }} />
        </div>
        <div className="hero__topline reveal in">
          <span>AGENCE DE COMMUNICATION DIGITALE
CRÉATION DE CONTENUS
          </span>
          <span>Photo · Vidéo · Design · Studio</span>
          <span>Grenoble — France</span>
        </div>
        <div className="hero__meta" style={{ marginTop: "auto" }}>
          <div>
            <h1 className="display">
              <LineReveal delayStep={120} lines={[
                "Make",
                <Typewriter words={["media", "vidéos", "photos", "design"]} speed={155} deleteSpeed={80} waitTime={2400} />
              ]} />
            </h1>
            <Reveal as="p" className="lede hero__tag" delay={260}>
              Une équipe guidée par la passion.
            </Reveal>
          </div>
          <Reveal className="hero__cta" delay={360}>
            <a href="#expertises" className="btn" onClick={scrollTo("#expertises")}>
              Découvrir nos services <span className="arr">→</span>
            </a>
          </Reveal>
        </div>
      </div>
    </section>);}

/* ============================================================
   FOOTER
   ============================================================ */
function Footer() {
  const { SERVICES, RENTAL_INFO } = window.MM_DATA;
  const go = (href) => (e) => {
    e.preventDefault();
    if (href === "#top") {
      if (document.querySelector("#top")) {window.scrollTo({ top: 0, behavior: "smooth" });} else {window.location.href = "index.html";}
      return;
    }
    const el = document.querySelector(href);
    if (el) {window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 70, behavior: "smooth" });} else
    {window.location.href = "index.html" + href;}
  };
  return (
    <footer className="footer" id="footer">
      <div className="wrap">
        <Reveal>
          <p className="footer__big">Make Media</p>
        </Reveal>
        <div className="footer__cols">
          <div className="fcol">
            <h4>Navigation</h4>
            <ul>
              {NAV.map((n) => <li key={n.href}><a href={n.href} onClick={go(n.href)} className="ulink">{n.label}</a></li>)}
            </ul>
          </div>
          <div className="fcol">
            <h4>Services</h4>
            <ul>
              {SERVICES.map((s) => <li key={s.id}><a href="#services" onClick={go("#services")} className="ulink">{s.name}</a></li>)}
            </ul>
          </div>
          <div className="fcol">
            <h4>Location</h4>
            <ul>
              <li><a href="location.html" className="ulink">Location de matériel</a></li>
              {RENTAL_INFO.addressLines.map((l) => <li key={l}>{l}</li>)}
              <li><a href={`mailto:${RENTAL_INFO.email}`} className="ulink">{RENTAL_INFO.email}</a></li>
            </ul>
          </div>
          <div className="fcol">
            <h4>Réseaux</h4>
            <ul>
              <li><a href={window.MM_DATA.SOCIALS.instagram} target="_blank" rel="noopener" className="ulink">Instagram</a></li>
              {window.MM_DATA.SOCIALS.linkedin && <li><a href={window.MM_DATA.SOCIALS.linkedin} target="_blank" rel="noopener" className="ulink">LinkedIn</a></li>}
            </ul>
          </div>
        </div>
        <div className="footer__bottom">
          <span className="brand__txt">© 2026 Make Media</span>
          <span><a href="mentions-legales.html" className="ulink">Mentions légales</a> · <a href="mentions-legales.html#confidentialite" className="ulink">Confidentialité</a></span>
        </div>
      </div>
    </footer>);

}

Object.assign(window, {
  useReveal, Reveal, MaskReveal, LineReveal, Typewriter, DecryptedText, Placeholder, useParallax,
  Header, Hero, Footer, MM_NAV: NAV
});