/* ========== La Chismosa components ========== */

/* ---------- Custom Cursor ---------- */
const CURSOR_LABELS = [
  { sel: ".envelope-wrap, .envelope-cta, .envelope",  text: "Abrir"     },
  { sel: ".press-card",                               text: "Leer"      },
  { sel: ".carousel-card, .office-item",              text: "Ver"       },
  { sel: ".contact-cta",                              text: "Escribir"  },
  { sel: ".holding-card",                             text: "Descubrir" },
  { sel: ".silencio-sticky",                          text: "Subir"     },
];

function Cursor() {
  const dot   = React.useRef(null);
  const ring  = React.useRef(null);
  const lbl   = React.useRef(null);
  const pos   = React.useRef({ x: -100, y: -100 });
  const target = React.useRef({ x: -100, y: -100 });

  React.useEffect(() => {
    const onMove = (e) => {
      target.current = { x: e.clientX, y: e.clientY };
      if (dot.current)
        dot.current.style.transform = `translate(${e.clientX}px, ${e.clientY}px) translate(-50%, -50%)`;
    };

    const onOver = (e) => {
      if (!ring.current) return;
      const el = e.target;

      if (el.closest("a, button, [data-hover], .envelope-cta, .office-item, .press-card, .services-foot .item, .map-pin")) {
        ring.current.classList.add("hover");
      } else {
        ring.current.classList.remove("hover");
      }

      if (lbl.current) {
        const match = CURSOR_LABELS.find(({ sel }) => el.closest(sel));
        if (match) {
          lbl.current.textContent = match.text;
          lbl.current.classList.add("visible");
        } else {
          lbl.current.classList.remove("visible");
        }
      }
    };

    let raf;
    const loop = () => {
      pos.current.x += (target.current.x - pos.current.x) * 0.18;
      pos.current.y += (target.current.y - pos.current.y) * 0.18;
      if (ring.current)
        ring.current.style.transform = `translate(${pos.current.x}px, ${pos.current.y}px) translate(-50%, -50%)`;
      if (lbl.current)
        lbl.current.style.transform = `translate(${pos.current.x + 26}px, ${pos.current.y - 14}px)`;
      raf = requestAnimationFrame(loop);
    };
    loop();

    window.addEventListener("mousemove", onMove);
    window.addEventListener("mouseover", onOver);
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseover", onOver);
    };
  }, []);

  return (
    <>
      <div ref={dot} className="cursor-dot" />
      <div ref={ring} className="cursor-ring" />
      <span ref={lbl} className="cursor-label" />
    </>);
}

/* ---------- Navigation ---------- */
function Nav({ active, onNav }) {
  const [scrolled, setScrolled] = React.useState(false);
  React.useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 40);
    window.addEventListener("scroll", onScroll);
    return () => window.removeEventListener("scroll", onScroll);
  }, []);

  const links = [
  { id: "nosotros", label: "Nosotros" },
  { id: "servicios", label: "Servicios" },
  { id: "prensa", label: "Prensa" },
  { id: "proyectos", label: "Proyectos" },
  { id: "contacto", label: "Contacto" }];


  return (
    <header className={`nav ${scrolled ? "scrolled" : ""}`}>
      <a className="nav-brand" href="#top" onClick={(e) => {e.preventDefault();onNav("top");}}>
        La<span className="amp">·</span><em>Chismosa</em>
      </a>
      <nav>
        <ul className="nav-links">
          {links.map((l) =>
          <li key={l.id}>
              <a href={`#${l.id}`}
            className={active === l.id ? "active" : ""}
            onClick={(e) => {e.preventDefault();onNav(l.id);}}>
                <span>{l.label}</span>
                <span>{l.label}</span>
              </a>
            </li>
          )}
        </ul>
      </nav>
      <div className="nav-right">
        <span className="lang-toggle">
          <span className="on">ES</span>
          <span className="sep">|</span>
          <span className="off">EN</span>
        </span>
      </div>
    </header>);

}

/* ---------- Intro / persiana ---------- */
function Intro({ onDone }) {
  const N = 10;
  const ref = React.useRef(null);

  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const strips = el.querySelectorAll(".intro-strip");
    const half = N / 2;
    strips.forEach((s, i) => {
      const dist = Math.abs(i - (half - 0.5));
      s.style.setProperty("--d", `${(dist / half) * 0.3}s`);
      s.style.transformOrigin = i < half ? "top center" : "bottom center";
    });
    const t1 = setTimeout(() => el.classList.add("opening"), 900);
    const t2 = setTimeout(() => onDone?.(), 900 + 680 + 300 + 120);
    return () => { clearTimeout(t1); clearTimeout(t2); };
  }, []);

  return (
    <div ref={ref} className="intro">
      {Array.from({ length: N }).map((_, i) => (
        <div key={i} className="intro-strip" />
      ))}
      <div className="intro-mark">
        <div className="intro-mark-text">
          La <em>Chismosa</em>
        </div>
      </div>
    </div>
  );
}

/* ---------- Invisible ink ---------- */
function InkText({ children }) {
  const ref    = React.useRef(null);
  const heat   = React.useRef(0);
  const rafRef = React.useRef(null);

  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const onMove = (e) => {
      const r = el.getBoundingClientRect();
      const dist = Math.hypot(e.clientX - (r.left + r.width / 2), e.clientY - (r.top + r.height / 2));
      const radius = Math.max(r.width * 0.5, 80) + 170;
      const target = Math.max(0, 1 - dist / radius);
      cancelAnimationFrame(rafRef.current);
      const tick = () => {
        heat.current += (target - heat.current) * 0.08;
        el.style.setProperty("--heat", heat.current.toFixed(3));
        if (Math.abs(heat.current - target) > 0.002)
          rafRef.current = requestAnimationFrame(tick);
      };
      tick();
    };
    window.addEventListener("mousemove", onMove, { passive: true });
    return () => { window.removeEventListener("mousemove", onMove); cancelAnimationFrame(rafRef.current); };
  }, []);

  return <span ref={ref} className="ink">{children}</span>;
}

/* ---------- Redacted text ---------- */
function Redacted({ children, delay = 0 }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    el.style.setProperty("--rd", `${delay}s`);
    const io = new IntersectionObserver(([entry]) => {
      if (!entry.isIntersecting) return;
      io.disconnect();
      el.classList.add("revealed");
    }, { threshold: 0.9, rootMargin: "0px 0px -120px 0px" });
    io.observe(el);
    return () => io.disconnect();
  }, [delay]);
  return (
    <span ref={ref} className="redacted">
      {children}
      <span className="redacted-bar" aria-hidden="true" />
    </span>
  );
}

/* ---------- HERO / Envelope ---------- */
function Hero({ onReveal }) {
  const [state, setState] = React.useState("closed");
  const heroRef = React.useRef(null);

  React.useEffect(() => {
    const t = setTimeout(() => {setState("opening");}, 600);
    return () => clearTimeout(t);
  }, []);

  React.useEffect(() => {
    if (state === "opening") {
      const t = setTimeout(() => {setState("revealed");onReveal?.();}, 1400);
      return () => clearTimeout(t);
    }
  }, [state]);

  React.useEffect(() => {
    const onMove = (e) => {
      if (!heroRef.current) return;
      const r = heroRef.current.getBoundingClientRect();
      const x = (e.clientX - r.left) / r.width - 0.5;
      const y = (e.clientY - r.top) / r.height - 0.5;
      const bg = heroRef.current.querySelector(".video-placeholder");
      if (bg) bg.style.transform = `scale(${state === "revealed" ? 1.02 : 1.08}) translate(${x * -20}px, ${y * -20}px)`;
    };
    window.addEventListener("mousemove", onMove);
    return () => window.removeEventListener("mousemove", onMove);
  }, [state]);

  return (
    <section id="top" ref={heroRef} className={`hero ${state}`}>
      <div className="hero-bg">
        <div className="video-placeholder">
          {state === "revealed" && <img src="assets/hero-chismosa.gif" alt="" />}
        </div>
        <div className="vignette" />
      </div>

      <div className="envelope-wrap" onClick={() => state === "closed" && setState("opening")}>
        <div className="envelope">
          <div className="envelope-stamp">
            <span>LCH<br />2026</span>
          </div>
          <div className="envelope-body">
            <div className="envelope-tag">
              INVITACIÓN <span className="dot" /> N.º 001
            </div>
            <div className="envelope-title">Para quien comunica <em className="it">con intención.</em></div>
            <div className="envelope-addr">
              Una carta abierta de<br />La Chismosa — Luxury Communications
            </div>
            <button className="envelope-cta"
            onClick={(e) => {e.stopPropagation();setState("opening");}}>
              Abrir carta <span className="arrow" />
            </button>
          </div>
          <div className="envelope-flap" />
          <div className="envelope-seal">L</div>
        </div>
      </div>

      <div className="hero-content">
        <div className="hero-top">
          <div className="eyebrow">backed by a global group</div>
          <div className="hero-who">
            Una agencia global de comunicación para marcas que entienden el lujo como lenguaje.
          </div>
        </div>

        <div className="hero-center">
          <div className="hero-kicker">— Who we are —</div>
          <h1 className="hero-headline">
            <div className="line"><span style={{ fontSize: "160px" }}>"Porque el lujo</span></div>
            <div className="line"><span style={{ fontSize: "160px" }}>también se <em className="it">comunica</em>."</span></div>
          </h1>
        </div>

        <div className="hero-bottom">
          <div className="hero-signature">
            Antonio de Juan
            <small>Founder · Managing Director</small>
          </div>
          <div className="hero-scroll">
            <span>Scroll</span>
            <div className="hero-scroll-line" />
          </div>
          <div style={{ textAlign: "right" }}>
            © MMXXVI<br />
            <span style={{ opacity: .6 }}>Málaga · Madrid · Brasil · Cancún · Miami · CDMX</span>
          </div>
        </div>
      </div>
    </section>);

}

/* ---------- Manifesto ---------- */
function Manifesto() {
  return (
    <section id="nosotros" className="manifesto">
      <div className="manifesto-inner">
        <div className="manifesto-label">— 01 / Nosotros</div>
        <div className="manifesto-body">
          <h2 className="display">
            Construimos <em style={{ color: "var(--crimson)" }}><Redacted delay={0}>narrativas</Redacted></em> para marcas donde el detalle
            <span className="highlight" style={{ color: "var(--crimson)" }}> no decora: </span>
            <em style={{ color: "var(--crimson)" }}><Redacted delay={0.22}>define</Redacted></em>.
          </h2>
          <p>
            Somos un estudio de comunicación dedicado exclusivamente al universo del lujo —
            hospitality, gastronomía, arquitectura y diseño interior. Un equipo pequeño, respaldado
            por un grupo global, que trabaja pieza a pieza, palabra a palabra.
          </p>
          <p>
            No vendemos ruido. Ofrecemos <Redacted delay={0}>criterio</Redacted>, <Redacted delay={0.22}>discreción</Redacted> y una red de relaciones
            cultivada durante más de dos décadas en las cinco ciudades que marcan la conversación.
          </p>
          <div className="ink-phrase">
            <InkText>Sabemos lo que no se publica.</InkText>
          </div>
        </div>
      </div>
    </section>);

}

/* ---------- Services Marquee ---------- */
const SERVICES = [
"Food & Beverage",
"Hotel Brands",
"Interior Design & FF&E",
"Architecture",
"Assets Guardianship",
"Communication"];


function ServicesMarquee() {
  const row1 = React.useRef(null);
  const row2 = React.useRef(null);

  React.useEffect(() => {
    let raf,x1 = 0,x2 = 0;
    const tick = () => {
      x1 -= 0.4;
      x2 += 0.4;
      if (row1.current) row1.current.style.transform = `translate3d(${x1}px,0,0)`;
      if (row2.current) row2.current.style.transform = `translate3d(${x2}px,0,0)`;

      if (row1.current) {
        const w = row1.current.scrollWidth / 2;
        if (Math.abs(x1) >= w) x1 = 0;
      }
      if (row2.current) {
        const w = row2.current.scrollWidth / 2;
        if (x2 >= w) x2 = 0;
      }
      raf = requestAnimationFrame(tick);
    };
    tick();
    return () => cancelAnimationFrame(raf);
  }, []);

  const row = (dup = false) =>
  <>
      {SERVICES.map((s, i) =>
    <span key={i + (dup ? 10 : 0)} className={`marquee-item ${i % 2 ? "outlined" : ""}`}>
          <span className="num">0{i + 1}</span>
          {s}
          <span className="marquee-dot" />
        </span>
    )}
    </>;


  return (
    <section id="servicios" className="services">
      <div className="services-head">
        <div>
          <div className="eyebrow">— 02 / Servicios</div>
          <h2>Seis disciplinas, <em style={{ color: "var(--highlight)" }}>una misma gramática</em> del lujo.</h2>
        </div>
        <div className="pill">+ 500 Proyectos</div>
      </div>

      <div className="marquee" ref={row1}>
        {row()}{row(true)}
      </div>
      <div className="marquee" style={{ marginTop: 24 }} ref={row2}>
        {row()}{row(true)}
      </div>

      <div className="services-foot">
        {SERVICES.map((s, i) =>
        <div className="item" key={s}>
            <span className="idx">0{i + 1} —</span>
            {s}
          </div>
        )}
      </div>
    </section>);

}

/* ---------- Presence / Stats ---------- */
function Presence() {
  const stats = [
  { n: "+20", em: "países", label: "Experience in over", desc: "Presencia operativa en más de veinte mercados." },
  { n: "+20", em: "países", label: "Trusted by over", desc: "Marcas de lujo que confían su relato a nuestro criterio." },
  { n: "+20", em: "países", label: "Success achieved in", desc: "Proyectos de comunicación, estrategia y custodia de activos." }];

  return (
    <section className="presence">
      <div className="presence-head">
        <div>
          <div className="eyebrow">— 03 / Global presence</div>
        </div>
        <h2>Respaldados por un grupo <em style={{ color: "var(--crimson)" }}>global</em>, con base estratégica en cinco ciudades clave.</h2>
      </div>
      <div className="stats">
        {stats.map((s, i) =>
        <div className="stat" key={i}>
            <span className="num" style={{ fontSize: "76px" }}>{s.n} <em style={{ color: "var(--crimson)" }}>{s.em}</em></span>
            <span className="label">{s.label}</span>
            <span className="desc">{s.desc}</span>
          </div>
        )}
      </div>
    </section>);

}

/* ---------- Interactive Map ---------- */
const OFFICES = [
{ id: "malaga", city: "Málaga", country: "España", addr: "Hacienda del Mar — Meliá Collection, meeting center", x: 48.8, y: 36.5, status: "open", img: "assets/maps/malaga.png" },
{ id: "madrid", city: "Madrid", country: "España", addr: "C/ de Fernando Santos, Chamberí, 27", x: 47.2, y: 34.0, status: "open", img: "assets/maps/madrid.png" },
{ id: "brasil", city: "Brasil", country: "Sudamérica", addr: "São Paulo", x: 35.2, y: 67.0, status: "open", img: "assets/maps/rio-de-janeiro.png" },
{ id: "cancun", city: "Cancún", country: "México", addr: "Carretera Aeropuerto-Bonfil km 11.5", x: 24.6, y: 49.0, status: "open", img: "assets/maps/cancun.png" },
{ id: "miami", city: "Miami", country: "USA", addr: "Miami, FL", x: 25.8, y: 45.0, status: "open", img: "assets/maps/miami.png" },
{ id: "cdmx", city: "CDMX", country: "México", addr: "Campus corporativo de Coyoacán, PB T2", x: 22.0, y: 52.0, status: "open", img: "assets/maps/mexico-city.png" },
{ id: "middle", city: "Middle East", country: "", addr: "Dubai / Riyadh", x: 60.0, y: 47.0, status: "open", img: "assets/maps/riyadh.png" }];


function WorldMap() {
  const [active, setActive] = React.useState("malaga");
  const activeOffice = OFFICES.find((o) => o.id === active) || OFFICES[0];
  const activeIdx = OFFICES.findIndex((o) => o.id === active);

  return (
    <section id="proyectos" className="map-section">
      <div className="map-head">
        <div>
          <div className="eyebrow">— 04 / Our workspaces</div>
          <h2>Conversaciones <em style={{ color: "var(--crimson)" }}>a escala global</em>, decisiones en persona.</h2>
        </div>
        <div className="eyebrow" style={{ alignSelf: 'flex-end' }}>Let's talk · +34 900 155 155</div>
      </div>

      <div className="carousel-wrap">
        <div className="carousel-stage" style={{ height: "720px" }}>
          {OFFICES.map((o, i) => {
            const offset = i - activeIdx;
            const abs = Math.abs(offset);
            const pos = abs === 0 ? "center" : abs === 1 ? offset < 0 ? "left" : "right" : "hidden";
            return (
              <div key={o.id} className={`carousel-card ${pos} ${o.status}`}
              onClick={() => setActive(o.id)}>
                <div className="carousel-thumb">
                  {o.img && <img src={o.img} alt={o.city} className="carousel-map" />}
                  <div className="carousel-cap">{o.city} · {o.status === "open" ? "open" : "coming soon"}</div>
                </div>
                <div className="carousel-meta">
                  <span className="carousel-city">{o.city}</span>
                  <span className="carousel-addr">{o.addr}</span>
                </div>
              </div>);

          })}
        </div>

        <div className="carousel-side">
          <div className="carousel-side-head">
            <span>{String(activeIdx + 1).padStart(2, '0')} / {String(OFFICES.length).padStart(2, '0')}</span>
            <span>{activeOffice.status === "open" ? "Open now" : "Coming soon"}</span>
          </div>
          <div className="office-list">
            {OFFICES.map((o) =>
            <button key={o.id}
            className={`office-item ${o.status === "soon" ? "soon" : ""} ${active === o.id ? "active" : ""}`}
            onClick={() => setActive(o.id)}
            onMouseEnter={() => setActive(o.id)}>
                <div className="row1">
                  <span className="name">{o.city}</span>
                  <span className="tag" style={{ color: "var(--crimson)" }}>{o.status === "open" ? "Open" : "Coming soon"}</span>
                </div>
                <div className="addr">{o.addr}</div>
              </button>
            )}
          </div>
        </div>
      </div>
    </section>);

}

/* ---------- Press / Projects ---------- */
const PRESS = [
{ meta: "Hospitality · 2025", title: "Relanzamiento de marca para un resort boutique en la Costa del Sol.", cap: "Hacienda · coastal", cls: "wide",
  img: "https://images.unsplash.com/photo-1520250497591-112f2f40a3f4?w=900&q=85&fit=crop" },
{ meta: "F&B · 2025", title: "Apertura editorial de un restaurante firma en Ciudad de México.", cap: "Interior · dining", cls: "tall",
  img: "https://images.unsplash.com/photo-1414235077428-338989a2e8c0?w=900&q=85&fit=crop" },
{ meta: "Architecture · 2024", title: "Narrativa de producto para un estudio de arquitectura residencial.", cap: "Atelier · studio", cls: "third",
  img: "https://images.unsplash.com/photo-1600585154340-be6161a56a0c?w=900&q=85&fit=crop" },
{ meta: "Prensa · 2024", title: "Campaña internacional · 28 medios internacionales de referencia.", cap: "Press · portfolio", cls: "third",
  img: "https://images.unsplash.com/photo-1511578314322-379afb476865?w=900&q=85&fit=crop" },
{ meta: "Interior · 2024", title: "Guardianship de marca para una colección de mobiliario de autor.", cap: "FF&E · detail", cls: "third",
  img: "https://images.unsplash.com/photo-1555041469-a586c61ea9bc?w=900&q=85&fit=crop" }];


/* ---------- Billboard ---------- */
function Billboard() {
  const ref = React.useRef(null);
  const [progress, setProgress] = React.useState(0);

  React.useEffect(() => {
    const onScroll = () => {
      const el = ref.current;
      if (!el) return;
      const r = el.getBoundingClientRect();
      const vh = window.innerHeight;
      // 0 when section enters viewport from below, 1 when it leaves at top
      const p = 1 - (r.top + r.height / 2) / (vh + r.height / 2);
      setProgress(Math.max(0, Math.min(1, p)));
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);

  const shift = (progress - 0.5) * 60; // parallax for the photo

  // Reveal animation choreography (driven by scroll progress 0 → 1)
  // 0.20 → 0.45: pink panel sweeps in (clip-path)
  // 0.42 → 0.58: line 1 ("It's time to")
  // 0.50 → 0.66: line 2 ("Tell YOUR Story")
  // 0.66 → 0.80: wordmark
  const stage = (a, b) => Math.max(0, Math.min(1, (progress - a) / (b - a)));
  const panelP = stage(0.18, 0.46);
  const line1P = stage(0.40, 0.58);
  const line2P = stage(0.50, 0.68);
  const markP = stage(0.66, 0.82);

  // panel drops from top like a canvas falling with gravity (ease-in²)
  const easedP = panelP * panelP;
  const panelClip = `inset(0% 0% ${(1 - easedP) * 100}% 0%)`;

  return (
    <section className="billboard" ref={ref} aria-label="It's time to tell your story">
      <div className="billboard-media" style={{ transform: `translate3d(0, ${shift}px, 0) scale(1.06)` }}>
        <div className="billboard-stage">
          <img src="assets/billboard-tell-your-story.png" alt="" />
          {/* Animated mupi overlay — covers and replays the pink billboard */}
          <div className="mupi" aria-hidden="true">
            <div className="mupi-panel" style={{ clipPath: panelClip, WebkitClipPath: panelClip }}>
              <div className="mupi-grain" />
              <div className="mupi-lines">
                <div className="mupi-line">
                  <span className="mupi-line-inner" style={{
                    transform: `translateY(${(1 - line1P) * 110}%)`,
                    opacity: line1P
                  }}>
                    It's time to
                  </span>
                </div>
                <div className="mupi-line mupi-line-2">
                  <span className="mupi-line-inner" style={{
                    transform: `translateY(${(1 - line2P) * 110}%)`,
                    opacity: line2P
                  }}>
                    Tell <em>YOUR</em> Story
                  </span>
                </div>
              </div>
              <div className="mupi-mark" style={{
                opacity: markP,
                transform: `translateY(${(1 - markP) * 14}px)`
              }}>
                <span className="mupi-mark-la">la</span>
                <span className="mupi-mark-name">CHISMOSA</span>
              </div>
            </div>
          </div>
        </div>
      </div>

      {/* (mupi moved inside billboard-media so it tracks the photo crop) */}

      <div className="billboard-overlay">
        <div className="billboard-eyebrow">— Manifesto</div>
        <div className="billboard-meta">
          <span>La Chismosa</span>
          <span className="dot">·</span>
          <span>Out-of-home</span>
          <span className="dot">·</span>
          <span>Madrid / 2025</span>
        </div>
      </div>
    </section>);

}


function Press() {
  return (
    <section id="prensa" className="press">
      <div className="press-head">
        <div>
          <div className="eyebrow">— 05 / Prensa & Proyectos</div>
          <h2>Un portfolio <em style={{ color: "var(--crimson)" }}>editado</em>. Seleccionado, nunca acumulado.</h2>
        </div>
        <div className="eyebrow">Archivo completo →</div>
      </div>
      <div className="press-grid">
        {PRESS.map((p, i) =>
        <article key={i} className={`press-card ${p.cls}`}>
            <div className="thumb">
              {p.img && <img src={p.img} alt={p.cap} loading="lazy" />}
              <div className="placeholder" />
              <div className="caption">{p.cap}</div>
            </div>
            <div className="meta">
              <span>{p.meta}</span>
              <span>0{i + 1} / 0{PRESS.length}</span>
            </div>
            <h3 className="title">{p.title}</h3>
          </article>
        )}
      </div>
    </section>);

}

/* ---------- Holding / Sister brands ---------- */
const HOLDING_BRANDS = [
{ cat: "Food & Beverage", name: "TalentChef", italic: false, tone: 1 },
{ cat: "Hotel Brands", name: "Tailors", italic: true, tone: 2 },
{ cat: "Interior Design & FF&E", name: "Guinda", italic: true, tone: 3 },
{ cat: "Architecture", name: "Alia", italic: true, tone: 4 },
{ cat: "Assets Guardianship", name: "Libo", italic: true, tone: 5 },
{ cat: "Communication", name: "la CHISMOSA", italic: true, tone: 6, current: true }];


function Holding() {
  return (
    <section className="holding" aria-label="LittleBig Hospitality Group">
      <div className="holding-head">
        <div className="eyebrow">— 06 / Backed by</div>
        <div className="holding-mark">
          <span className="hm-little">LITTLE</span><span className="hm-big">BIG</span>
          <span className="hm-sub">Hospitality Group</span>
        </div>
        <p className="holding-lede">
          Una familia de marcas especialistas. Cada disciplina, una firma propia. <em>Una sola conversación.</em>
        </p>
      </div>

      <div className="holding-grid">
        {HOLDING_BRANDS.map((b, i) =>
        <a key={i} href="#" className={`holding-card ${b.current ? "is-current" : ""}`}
        onClick={(e) => e.preventDefault()}>
            <div className="holding-cat">{b.cat}</div>
            <div className="holding-thumb">
              <div className={`holding-stripes tone-${b.tone}`} />
              <div className="holding-name">
                {b.italic ?
              <span style={{ fontStyle: "italic", fontWeight: 400 }}>{b.name}</span> :

              b.name.split(/(Chef)/).map((part, j) =>
              part === "Chef" ?
              <span key={j} style={{ fontStyle: "italic", fontWeight: 300 }}>{part}</span> :
              <span key={j} style={{ fontWeight: 600 }}>{part}</span>
              )
              }
              </div>
              <div className="holding-arrow" aria-hidden="true">
                <svg viewBox="0 0 24 24" width="20" height="20">
                  <path d="M6 18L18 6M18 6H8M18 6V16" stroke="currentColor" strokeWidth="1.4" fill="none" strokeLinecap="square" />
                </svg>
              </div>
              {b.current && <div className="holding-current">— You are here</div>}
            </div>
          </a>
        )}
      </div>
    </section>);

}


/* ---------- Contact / Footer ---------- */
function Contact() {
  const megaRef = React.useRef(null);

  React.useEffect(() => {
    const el = megaRef.current;
    if (!el) return;
    const letters = el.querySelectorAll(".footer-mega-letter");
    const io = new IntersectionObserver(([entry]) => {
      if (!entry.isIntersecting) return;
      io.disconnect();
      letters.forEach((l, i) => l.style.setProperty("--ld", `${i * 0.048}s`));
      requestAnimationFrame(() => letters.forEach(l => l.classList.add("in")));
    }, { threshold: 0.25 });
    io.observe(el);
    return () => io.disconnect();
  }, []);

  return (
    <section id="contacto" className="contact">
      <div className="contact-inner">
        <div className="contact-kicker">LET'S TALK · 07 / CONTACTO</div>
        <h2>
          Si el chisme <em style={{ color: "var(--highlight)" }}>importa,</em><br />
          hablemos <em style={{ color: "var(--highlight)" }}>en <Redacted delay={0.1}>persona</Redacted>.</em>
        </h2>
        <div className="ink-phrase" style={{ marginTop: "32px", marginBottom: "-16px" }}>
          <InkText>Ya sabemos que te interesa.</InkText>
        </div>
        <a href="mailto:hola@lachismosa.com" className="contact-cta">
          hola@lachismosa.com <span className="arrow" />
        </a>

        <div className="contact-grid">
          <div className="contact-col">
            <h5>EUROPA</h5>
            <p>Málaga<br /><small>Hacienda del Mar · Meliá</small></p>
            <p>Madrid<br /><small>C/ Fernando Santos 27 · Chamberí</small></p>
          </div>
          <div className="contact-col">
            <h5>LATAM</h5>
            <p>Cancún<br /><small>Carretera Bonfil km 11.5</small></p>
            <p>CDMX<br /><small>Coyoacán · Campus PB T2</small></p>
            <p>Miami<br /><small>Coming soon</small></p>
          </div>
          <div className="contact-col">
            <h5>Expansión</h5>
            <p>Brasil<br /><small>Coming soon · São Paulo</small></p>
            <p>Middle East<br /><small>Coming soon · Dubai</small></p>
          </div>
          <div className="contact-col">
            <h5>Direct</h5>
            <p>+34 900 155 155<br /><small>Mon — Fri · 09:00–19:00 CET</small></p>
            <p>Instagram<br /><small>@lachismosa.luxury</small></p>
            <p>LinkedIn<br /><small>/la-chismosa</small></p>
          </div>
        </div>

        <div ref={megaRef} className="footer-mega" aria-label="La Chismosa.">
          {Array.from("La Chismosa.").map((ch, i) =>
          <span key={i} className="footer-mega-letter" aria-hidden="true">
              {ch === " " ? "\u00A0" : ch}
            </span>
          )}
        </div>

        <div className="footer-bottom">
          <div>© MMXXVI · La Chismosa · Luxury Communications</div>
          <div className="footer-bottom-links">
            <a href="#">Aviso Legal</a>
            <a href="#">Política de privacidad</a>
            <a href="#">Cookies</a>
          </div>
        </div>
      </div>
    </section>);

}

Object.assign(window, {
  Intro, Cursor, Nav, Hero, Manifesto, ServicesMarquee, Presence, WorldMap, Press, Contact, Redacted, InkText
});