// Shared shell — Nav, Footer, CTA, CursorPreview, Placeholder, Reveal
const { useEffect, useState, useRef, useCallback } = React;

// --- Hash routing ---
function parseHash() {
  const h = (window.location.hash || "#/").replace(/^#/, "");
  const parts = h.split("/").filter(Boolean);
  return { route: parts[0] || "home", sub: parts[1] || null };
}
function useRoute() {
  const [r, setR] = useState(parseHash());
  useEffect(() => {
    const onChange = () => {setR(parseHash());window.scrollTo(0, 0);};
    window.addEventListener("hashchange", onChange);
    return () => window.removeEventListener("hashchange", onChange);
  }, []);
  return r;
}
function navTo(route, sub) {
  const path = sub ? `#/${route}/${sub}` : route === "home" ? "#/" : `#/${route}`;
  window.location.hash = path;
}
function Link({ to, sub, children, className, style, onMouseEnter, onMouseLeave, ...rest }) {
  const handle = (e) => {e.preventDefault();navTo(to, sub);};
  return (
    <a href={sub ? `#/${to}/${sub}` : to === "home" ? "#/" : `#/${to}`}
    onClick={handle} className={className} style={style}
    onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} {...rest}>
      {children}
    </a>);

}

// --- TextReveal: parte el texto en letras que se pintan gris→blanco, escalonadas al entrar en viewport ---
function TextReveal({ text, className, style }) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const observer = new IntersectionObserver(
      (entries) => entries.forEach((e) => {
        const letters = e.target.querySelectorAll(".letter");
        if (e.isIntersecting) {
          e.target._trTimers = [];
          letters.forEach((l, i) => {
            e.target._trTimers.push(setTimeout(() => l.classList.add("visible"), i * 30));
          });
        } else {
          (e.target._trTimers || []).forEach(clearTimeout);
          e.target._trTimers = [];
          letters.forEach((l) => l.classList.remove("visible"));
        }
      }),
      { threshold: 0, rootMargin: "-10% 0px -10% 0px" }
    );
    observer.observe(el);
    return () => observer.disconnect();
  }, [text]);
  return (
    <span ref={ref} className={`text-reveal ${className || ""}`} style={style}>
      {String(text).split(" ").map((word, wi) =>
      <React.Fragment key={wi}>
          {wi > 0 ? " " : null}
          <span className="word">
            {word.split("").map((ch, ci) =>
        <span key={ci} className="letter">{ch}</span>
        )}
          </span>
        </React.Fragment>
      )}
    </span>);

}
window.TextReveal = TextReveal;

// --- Lang ---
function useLang() {
  const [lang, setLang] = useState(() => localStorage.getItem("ho_lang") || "es");
  const set = (l) => {localStorage.setItem("ho_lang", l);setLang(l);};
  return [lang, set];
}

// --- Floating Nav ---
function Nav({ route, lang, setLang }) {
  const items = [
  { id: "packaging", label: "Packaging" },
  { id: "branding", label: "Branding" },
  { id: "bag", label: "BAG" },
  { id: "about", label: "About" },
  { id: "blog", label: "Blog" }];

  const [sheet, setSheet] = useState(false);
  const [scrolled, setScrolled] = useState(false);
  useEffect(() => {setSheet(false);}, [route]);
  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 80);
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
  useEffect(() => {
    if (sheet) document.body.style.overflow = "hidden";else
    document.body.style.overflow = "";
    return () => {document.body.style.overflow = "";};
  }, [sheet]);

  return (
    <>
      <header className={`nav ${scrolled ? "scrolled" : ""}`}>
        <div className="nav-inner">
          <Link to="home" className="brand" aria-label="hombreonce home">
            <span className="brand-mark">hombreonce</span><span className="reg">®</span>
          </Link>
          <nav className="nav-center" aria-label="primary">
            {items.map((it) =>
            <Link key={it.id} to={it.id} className={route === it.id ? "active" : ""}>{it.label}</Link>
            )}
          </nav>
          <div className="nav-right">
            <a href="mailto:hola@ignacioelffman.com" className="contact-link">Contact</a>
            <button className="lang-toggle" onClick={() => setLang(lang === "es" ? "en" : "es")} aria-label="toggle language">
              <span className={lang === "es" ? "on" : ""}>ES</span>
              <span className="sep">/</span>
              <span className={lang === "en" ? "on" : ""}>EN</span>
            </button>
            <button className="nav-burger" onClick={() => setSheet(true)} aria-label="open menu">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"><path d="M3 7h18M3 12h18M3 17h18" /></svg>
            </button>
          </div>
        </div>
      </header>

      <div className={`nav-sheet ${sheet ? "open" : ""}`} aria-hidden={!sheet}>
        <div className="nav-sheet-top">
          <Link to="home" className="brand"><span className="brand-mark">hombreonce</span><span className="reg">®</span></Link>
          <button onClick={() => setSheet(false)} className="mono upper" style={{ fontSize: 12, letterSpacing: ".08em" }}>Cerrar ×</button>
        </div>
        <div className="nav-sheet-links">
          {items.map((it) =>
          <Link key={it.id} to={it.id} onClick={() => setSheet(false)}>{it.label}</Link>
          )}
        </div>
        <div className="nav-sheet-bottom">
          <button onClick={() => setLang(lang === "es" ? "en" : "es")} className="lang-toggle">
            <span className={lang === "es" ? "on" : ""}>ES</span>
            <span className="sep">/</span>
            <span className={lang === "en" ? "on" : ""}>EN</span>
          </button>
          <a href="mailto:hola@ignacioelffman.com">hola@ignacioelffman.com</a>
        </div>
      </div>
    </>);

}

// --- Lightbox: tap a hero / carousel image to view it full-width ---
// Active only on the published site (no editor bridge) so it never fights
// the image-slot's own click-to-upload / double-click-to-reframe editing.
function Lightbox() {
  const [src, setSrc] = useState(null);
  useEffect(() => {
    const editable = !!(window.omelette && window.omelette.writeFile);
    if (editable) return; // editing tools own the click in the editor
    const onClick = (e) => {
      const zone = e.target.closest && e.target.closest(".ph-hero, .ph-mat, .hero-bg");
      if (!zone) return;
      const slot = zone.matches("image-slot") ? zone : zone.querySelector("image-slot");
      if (!slot || !slot.shadowRoot) return;
      const img = slot.shadowRoot.querySelector(".frame img");
      const url = img && img.getAttribute("src");
      if (!url) return; // empty slot — nothing to zoom
      e.preventDefault();
      e.stopPropagation();
      setSrc(url);
    };
    document.addEventListener("click", onClick, true);
    document.body.setAttribute("data-published", "");
    return () => document.removeEventListener("click", onClick, true);
  }, []);
  useEffect(() => {
    if (!src) return;
    const onKey = (e) => { if (e.key === "Escape") setSrc(null); };
    document.addEventListener("keydown", onKey);
    document.body.style.overflow = "hidden";
    return () => { document.removeEventListener("keydown", onKey); document.body.style.overflow = ""; };
  }, [src]);
  return (
    <div className={`lightbox ${src ? "open" : ""}`} onClick={() => setSrc(null)}>
      {src ? <img src={src} alt="" onClick={(e) => e.stopPropagation()} /> : null}
      <button className="lb-close" aria-label="Cerrar" onClick={() => setSrc(null)}>
        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M6 6l12 12M18 6L6 18" /></svg>
      </button>
    </div>);

}
window.Lightbox = Lightbox;

// --- CTA + Footer (combined, subtle) ---
function Footer({ lang }) {
  const f = HC.footer[lang];
  const c = HC.cta[lang];
  return (
    <>
      <section className="cta-block">
        <div className="container">
          <span className="lead-small">{c.small}</span>
          <div>
            <a href="https://wa.me/5492214183041" target="_blank" rel="noopener noreferrer" className="big">{c.big}</a>
          </div>
        </div>
      </section>
      <footer className="footer">
        <div className="container">
          <div className="footer-cols">
            <div>
              <h4>{lang === "es" ? "Sitio" : "Sitemap"}</h4>
              <ul>
                <li><Link to="packaging">Packaging</Link></li>
                <li><Link to="branding">Branding</Link></li>
                <li><Link to="bag">BAG</Link></li>
              </ul>
            </div>
            <div>
              <h4>Studio</h4>
              <ul>
                <li><Link to="about">About</Link></li>
                <li><Link to="blog">Blog</Link></li>
                <li><a href="mailto:hola@ignacioelffman.com">Contact</a></li>
              </ul>
            </div>
            <div>
              <h4>Social</h4>
              <ul>
                <li><a href="https://www.instagram.com/hombre.once/" target="_blank" rel="noopener noreferrer">Instagram</a></li>
                <li><a href="https://www.linkedin.com/in/ignacio-elffman/" target="_blank" rel="noopener noreferrer">LinkedIn</a></li>
                <li><a href="https://wa.me/5492214183041" target="_blank" rel="noopener noreferrer">WhatsApp</a></li>
              </ul>
            </div>
          </div>
        </div>
        <div className="footer-mega" aria-hidden="true">
          <span className="footer-mega-text" style={{ fontSize: "min(125px, calc((100vw - 2 * var(--pad-x)) / 7))", fontWeight: "700" }}>
            <TextReveal text="hombreonce®" />
          </span>
        </div>
        <div className="container">
          <div className="footer-bottom">
            <span>{f.copy}</span>
            <span>{f.city}</span>
          </div>
        </div>
      </footer>
    </>);

}

// --- Placeholder ---
// Renders a striped `.ph` box (brand-language empty state) with an
// <image-slot> on top. Drag an image onto it (or click → file picker)
// and the slot persists across reloads via .image-slots.state.json.
// When the slot is filled, the stripes hide and the image takes over.
function Placeholder({ tag, id, className = "", ratio, fit }) {
  const style = ratio ? { aspectRatio: ratio } : undefined;
  // Unique slot id per call-site; the pages already pass distinct ids
  // (PKG-HERO, MKT-01, ME-01, ...) so we use them as-is.
  return (
    <div className={`ph ${className}`} style={style} data-cursor-preview={tag || ""}>
      {id ? <span className="ph-id">{id}</span> : null}
      {id ?
      <image-slot
        id={`SLOT-${id}`}
        shape="rect"
        placeholder={tag || id}
        {...(fit ? { fit } : {})}>
      </image-slot> :

      <span className="ph-tag">{tag}</span>
      }
    </div>);

}

// --- Cursor preview ---
function CursorPreview() {
  const ref = useRef(null);
  const labelRef = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let raf = null;
    let x = 0,y = 0,tx = 0,ty = 0;

    const handleEnter = (e) => {
      const t = e.target.closest("[data-cursor-preview]");
      if (!t) return;
      const label = t.getAttribute("data-cursor-preview") || "Preview";
      if (labelRef.current) labelRef.current.textContent = label;
      el.classList.add("show");
    };
    const handleLeave = (e) => {
      const t = e.target.closest("[data-cursor-preview]");
      if (!t) return;
      const related = e.relatedTarget && e.relatedTarget.closest && e.relatedTarget.closest("[data-cursor-preview]");
      if (related) return;
      el.classList.remove("show");
    };
    const handleMove = (e) => {
      tx = e.clientX;ty = e.clientY;
      if (!raf) raf = requestAnimationFrame(loop);
    };
    const loop = () => {
      x += (tx - x) * 0.18;
      y += (ty - y) * 0.18;
      el.style.left = x + "px";
      el.style.top = y + "px";
      if (Math.abs(tx - x) > 0.5 || Math.abs(ty - y) > 0.5) {
        raf = requestAnimationFrame(loop);
      } else raf = null;
    };

    document.addEventListener("mouseover", handleEnter);
    document.addEventListener("mouseout", handleLeave);
    document.addEventListener("mousemove", handleMove);
    return () => {
      document.removeEventListener("mouseover", handleEnter);
      document.removeEventListener("mouseout", handleLeave);
      document.removeEventListener("mousemove", handleMove);
      if (raf) cancelAnimationFrame(raf);
    };
  }, []);
  return (
    <div className="cursor-preview" ref={ref}>
      <span ref={labelRef} className="tag">Preview</span>
    </div>);

}

// --- VideoPlaceholder ---
// Same striped .ph empty-state as Placeholder, but the slot holds an
// embedded YouTube video instead of an <image-slot>. The URL is persisted
// in localStorage under VID-<id>, so it survives reloads per-browser. The
// embed plays autoplay + muted + loop (controls hidden) to behave like a
// background hero clip.
function parseYouTubeId(input) {
  if (!input) return "";
  const s = input.trim();
  // Already an id (11 chars, letters/digits/_/-)
  if (/^[A-Za-z0-9_-]{11}$/.test(s)) return s;
  try {
    const u = new URL(s);
    if (u.hostname === "youtu.be") return u.pathname.replace(/^\//, "").split("/")[0] || "";
    if (u.hostname.endsWith("youtube.com") || u.hostname.endsWith("youtube-nocookie.com")) {
      const v = u.searchParams.get("v");
      if (v) return v;
      const parts = u.pathname.split("/").filter(Boolean);
      const i = parts.findIndex((p) => p === "embed" || p === "shorts" || p === "live");
      if (i >= 0 && parts[i + 1]) return parts[i + 1];
    }
  } catch (e) {}
  return "";
}

function VideoPlaceholder({ tag, id, className = "", ratio, defaultVideoId = "" }) {
  const style = ratio ? { aspectRatio: ratio } : undefined;
  const key = `VID-${id || "unnamed"}`;
  const [videoId, setVideoId] = useState(() => {
    try {return localStorage.getItem(key) || defaultVideoId || "";} catch (e) {return defaultVideoId || "";}
  });
  const [draft, setDraft] = useState("");
  const [err, setErr] = useState("");

  const save = () => {
    const parsed = parseYouTubeId(draft);
    if (!parsed) {
      setErr("URL de YouTube inválida");
      setTimeout(() => setErr(""), 2400);
      return;
    }
    try {localStorage.setItem(key, parsed);} catch (e) {}
    setVideoId(parsed);
    setDraft("");
    setErr("");
  };

  const clear = () => {
    try {localStorage.removeItem(key);} catch (e) {}
    setVideoId(defaultVideoId || "");
  };

  const embedSrc = videoId ?
  `https://www.youtube-nocookie.com/embed/${videoId}` +
  `?autoplay=1&mute=1&loop=1&playlist=${videoId}` +
  `&controls=0&rel=0&playsinline=1&modestbranding=1` :
  "";

  return (
    <div
      className={`ph video-ph ${className}${videoId ? " is-filled" : ""}`}
      style={style}
      data-cursor-preview={tag || ""}>
      {id && !videoId ? <span className="ph-id">{id}</span> : null}
      {videoId ?
      <iframe
        className="video-ph-media"
        src={embedSrc}
        title={tag || id}
        frameBorder="0"
        referrerPolicy="strict-origin-when-cross-origin"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
        allowFullScreen /> :

      <div className="video-ph-empty">
          <svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
            <polygon points="5 3 19 12 5 21 5 3" />
          </svg>
          <span className="video-ph-cap">{tag || id}</span>
          <span className="video-ph-sub">Pegá la URL de un video de YouTube</span>
          <form
          className="video-ph-form"
          onSubmit={(e) => {e.preventDefault();save();}}>
            <input
            type="text"
            value={draft}
            onChange={(e) => setDraft(e.target.value)}
            placeholder="https://youtube.com/watch?v=..." />
            <button type="submit">Cargar</button>
          </form>
        </div>
      }
      {videoId ?
      <div className="video-ph-ctl">
          <button type="button" onClick={() => {setDraft("");clear();}}>Cambiar video</button>
        </div> :
      null}
      {err ? <div className="video-ph-err">{err}</div> : null}
    </div>);

}

// --- Reveal ---
function Reveal({ children, as = "div", delay = 0, className = "", ...rest }) {
  const ref = useRef(null);
  const [show, setShow] = useState(false);
  useEffect(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) {setTimeout(() => setShow(true), delay);io.disconnect();}
    }, { threshold: 0.12 });
    io.observe(ref.current);
    return () => io.disconnect();
  }, [delay]);
  const Tag = as;
  return <Tag ref={ref} className={`reveal ${show ? "in" : ""} ${className}`} {...rest}>{children}</Tag>;
}

Object.assign(window, {
  useRoute, navTo, Link, useLang,
  Nav, Footer, Placeholder, VideoPlaceholder, CursorPreview, Reveal, Lightbox,
  // Legacy export — inner pages still call <CTA />; now it's part of Footer.
  CTA: () => null
});