// sections-a.jsx — Nav, Hero, Manifiesto, Vista, Arquitectura, Departamento

// ---- InkReveal: canvas mask que el cursor desvela ----
const InkReveal = React.forwardRef(function InkReveal({
  maskColor = [242, 237, 228],
  brushSize = 190,
  lifetime = 850,
  rStart = 14,
  rVary = 0.42,
  stampStep = 10,
  maxStamps = 180,
  segments = 36,
  wobble = [0.14, 0.08, 0.05],
  gradientInnerRadius = 0.2,
  gradientStops = [0.95, 0.88, 0],
  // true = canvas arranca opaco; false = aparece al primer hover
  startVisible = false,
  // false = el padre controla eventos (canvas con pointer-events:none)
  captureEvents = true,
}, ref) {
  const canvasRef = React.useRef(null);
  const stampsRef = React.useRef([]);
  const runningRef = React.useRef(false);
  const lastPosRef = React.useRef(null);
  const dimsRef = React.useRef({ w: 0, h: 0 });
  const [active, setActive] = React.useState(startVisible);
  const mc = maskColor;

  const resize = React.useCallback(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const parent = canvas.parentElement;
    if (!parent) return;
    const dpr = Math.min(window.devicePixelRatio || 1, 2);
    const rect = parent.getBoundingClientRect();
    const w = rect.width, h = rect.height;
    dimsRef.current = { w, h };
    canvas.width = Math.round(w * dpr);
    canvas.height = Math.round(h * dpr);
    canvas.style.width = w + "px";
    canvas.style.height = h + "px";
    const ctx = canvas.getContext("2d");
    if (!ctx) return;
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    ctx.globalCompositeOperation = "source-over";
    ctx.fillStyle = "rgb(" + mc[0] + "," + mc[1] + "," + mc[2] + ")";
    ctx.fillRect(0, 0, w, h);
  }, []);

  const carveInk = React.useCallback((ctx, x, y, r, seed, alpha) => {
    const g = ctx.createRadialGradient(x, y, r * gradientInnerRadius, x, y, r);
    g.addColorStop(0,   "rgba(0,0,0," + (gradientStops[0] * alpha) + ")");
    g.addColorStop(0.5, "rgba(0,0,0," + (gradientStops[1] * alpha) + ")");
    g.addColorStop(1,   "rgba(0,0,0," + (gradientStops[2] * alpha) + ")");
    ctx.fillStyle = g;
    ctx.beginPath();
    for (let i = 0; i <= segments; i++) {
      const a = (i / segments) * Math.PI * 2;
      const wob = 0.78
        + wobble[0] * Math.sin(a * 3 + seed)
        + wobble[1] * Math.sin(a * 5 + seed * 2.1)
        + wobble[2] * Math.sin(a * 7 + seed * 0.7);
      const px = x + Math.cos(a) * r * wob;
      const py = y + Math.sin(a) * r * wob;
      i === 0 ? ctx.moveTo(px, py) : ctx.lineTo(px, py);
    }
    ctx.closePath();
    ctx.fill();
  }, []);

  const addStamp = React.useCallback((x, y) => {
    const stamps = stampsRef.current;
    if (stamps.length >= maxStamps) stamps.shift();
    stamps.push({
      x, y,
      born: performance.now(),
      seed: Math.random() * Math.PI * 2,
      rmax: brushSize * (1 - rVary + Math.random() * rVary),
    });
  }, []);

  const stampAlong = React.useCallback((x, y) => {
    const last = lastPosRef.current;
    if (!last) {
      addStamp(x, y);
    } else {
      const dx = x - last.x, dy = y - last.y;
      const dist = Math.hypot(dx, dy);
      const steps = Math.max(1, Math.ceil(dist / stampStep));
      for (let i = 1; i <= steps; i++)
        addStamp(last.x + (dx * i) / steps, last.y + (dy * i) / steps);
    }
    lastPosRef.current = { x, y };
  }, []);

  const loop = React.useCallback(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;
    const { w, h } = dimsRef.current;
    const now = performance.now();
    const stamps = stampsRef.current;
    ctx.globalCompositeOperation = "source-over";
    ctx.fillStyle = "rgb(" + mc[0] + "," + mc[1] + "," + mc[2] + ")";
    ctx.fillRect(0, 0, w, h);
    ctx.globalCompositeOperation = "destination-out";
    for (let i = stamps.length - 1; i >= 0; i--) {
      const t = (now - stamps[i].born) / lifetime;
      if (t >= 1) { stamps.splice(i, 1); continue; }
      const ease = 1 - Math.pow(1 - t, 3);
      const r = rStart + (stamps[i].rmax - rStart) * ease;
      carveInk(ctx, stamps[i].x, stamps[i].y, r, stamps[i].seed, 1 - t * t);
    }
    if (stamps.length) requestAnimationFrame(loop);
    else runningRef.current = false;
  }, []);

  const startLoop = React.useCallback(() => {
    if (!runningRef.current) { runningRef.current = true; requestAnimationFrame(loop); }
  }, [loop]);

  React.useEffect(() => {
    if (window.matchMedia("(pointer: coarse)").matches) return;
    resize();
    window.addEventListener("resize", resize);
    return () => window.removeEventListener("resize", resize);
  }, []);

  // Expone stamp() para que el padre pueda disparar el efecto desde su propio onMouseMove
  React.useImperativeHandle(ref, () => ({
    stamp: (clientX, clientY) => {
      const canvas = canvasRef.current;
      if (!canvas) return;
      const rect = canvas.getBoundingClientRect();
      const x = clientX - rect.left;
      const y = clientY - rect.top;
      if (!active) setActive(true);
      stampAlong(x, y);
      startLoop();
    },
    reset: () => { lastPosRef.current = null; },
  }), [active, stampAlong, startLoop]);

  if (typeof window !== "undefined" && window.matchMedia("(pointer: coarse)").matches) return null;

  const getPos = (e) => {
    const rect = e.currentTarget.getBoundingClientRect();
    return { x: e.clientX - rect.left, y: e.clientY - rect.top };
  };

  return (
    <canvas
      ref={canvasRef}
      style={{
        position: "absolute", inset: 0, zIndex: 2,
        cursor: captureEvents ? "none" : "auto",
        opacity: active ? 1 : 0,
        transition: "opacity 0.5s ease",
        pointerEvents: captureEvents ? "all" : "none",
      }}
      onMouseEnter={captureEvents ? (e) => {
        if (!active) setActive(true);
        const p = getPos(e);
        lastPosRef.current = p;
        stampAlong(p.x, p.y);
        startLoop();
      } : undefined}
      onMouseMove={captureEvents ? (e) => { const p = getPos(e); stampAlong(p.x, p.y); startLoop(); } : undefined}
      onMouseLeave={captureEvents ? () => { lastPosRef.current = null; } : undefined}
    />
  );
});

const NAV_LINKS = [
  { href:"#concepto",     label:"Concepto",     num:"01" },
  { href:"#arquitectura", label:"Arquitectura",  num:"02" },
  { href:"#departamento", label:"Departamento",  num:"03" },
  { href:"#amenidades",   label:"Amenidades",    num:"04" },
  { href:"#entorno",      label:"Entorno",       num:"06" },
];

function Nav() {
  const [scrolled, setScrolled] = React.useState(false);
  const [open, setOpen]         = React.useState(false);

  React.useEffect(() => {
    const fn = () => setScrolled(window.scrollY > 50);
    window.addEventListener("scroll", fn, { passive: true });
    return () => window.removeEventListener("scroll", fn);
  }, []);

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

  const close = () => setOpen(false);

  return (
    <>
      <nav className={"nav" + (scrolled ? " scrolled" : "") + (open ? " menu-open" : "")}>
        <a href="#top" className="nav-logo" onClick={close}>
          <img src="assets/logo-light.png" alt="LAVISTA Residences" className="nav-logo-img nav-logo-light" />
          <img src="assets/logo-dark.png"  alt="LAVISTA Residences" className="nav-logo-img nav-logo-dark" />
        </a>
        <div className="nav-links">
          {NAV_LINKS.map(l => <a key={l.href} href={l.href}>{l.label}</a>)}
        </div>
        <a href="#contacto" className="nav-cta">Agendar visita</a>
        <button className={"nav-hamburger" + (open ? " is-open" : "")}
          onClick={() => setOpen(o => !o)}
          aria-label={open ? "Cerrar menú" : "Abrir menú"}>
          <span /><span /><span />
        </button>
      </nav>

      {/* Menú móvil — overlay pantalla completa */}
      <div className={"nav-mobile-menu" + (open ? " is-open" : "")}>
        <div className="nav-mobile-links">
          {NAV_LINKS.map((l, i) =>
            <a key={l.href} href={l.href} className="nav-mobile-link"
              style={{ transitionDelay: open ? `${0.05 + i * 0.06}s` : "0s" }}
              onClick={close}>
              <span className="nav-ml-num">{l.num}</span>
              <span className="nav-ml-label">{l.label}</span>
            </a>
          )}
        </div>
        <a href="#contacto" className="nav-mobile-cta"
          style={{ transitionDelay: open ? "0.35s" : "0s" }}
          onClick={close}>
          Agendar visita
        </a>
      </div>
    </>
  );
}

function Hero() {
  const videoRef = React.useRef(null);
  const [revealed, setRevealed] = React.useState(false);
  const [hinted, setHinted]     = React.useState(false);

  React.useEffect(() => {
    const t = setTimeout(() => setRevealed(true), 1350);
    return () => clearTimeout(t);
  }, []);

  // Genera el estilo de cada línea con blur + rise + fade
  const lineStyle = (delay) => ({
    display: "block",
    opacity:   revealed ? 1 : 0,
    filter:    revealed ? "blur(0px)" : "blur(6px)",
    transform: revealed ? "translateY(0)" : "translateY(60px)",
    transition: [
      `opacity   1.6s cubic-bezier(0.22,1,0.36,1) ${delay}s`,
      `filter    1.8s cubic-bezier(0.22,1,0.36,1) ${delay}s`,
      `transform 1.8s cubic-bezier(0.22,1,0.36,1) ${delay}s`,
    ].join(", "),
  });

  const fadeStyle = (delay) => ({
    opacity:   revealed ? 1 : 0,
    transform: revealed ? "translateY(0)" : "translateY(14px)",
    transition: `opacity 1.2s ease ${delay}s, transform 1.2s ease ${delay}s`,
  });

  return (
    <header className="hero" id="top">
      <div className="hero-image" data-parallax="0.18" onMouseEnter={() => setHinted(true)}>
        <video
          ref={videoRef}
          className="hero-video"
          src="assets/hero-video.mp4"
          autoPlay muted loop playsInline preload="auto"
          poster="assets/render-terraza-lago.jpg">
        </video>
        <InkReveal />
        <div className={"hero-hint" + (hinted ? " hero-hint--hidden" : "")}>
          <span>Mueve el cursor para descubrir la vista</span>
        </div>
      </div>

      <div className="hero-content">
        {/* Meta superior */}
        <div className="hero-meta" style={fadeStyle(0.15)}>
          <div className="hero-meta-block">
            <span>Lago Esmeralda</span>
            <span>Atizapán de Zaragoza</span>
          </div>
          <div className="hero-meta-block" style={{ textAlign: "right" }}>
            <span>Desarrollado por</span>
            <strong style={{ fontFamily: "Manrope" }}>STARBAU</strong>
          </div>
        </div>

        {/* Titular — cada línea emerge con blur y rise */}
        <div className="hero-headline" id="hero-headline">
          <div className="hero-load-line">
            <span className="hero-caps" style={lineStyle(0.28)}>La</span>
          </div>
          <div className="hero-load-line">
            <span className="hero-caps hero-caps-italic" style={lineStyle(0.52)}>Vista</span>
          </div>
          <div className="hero-load-line">
            <span className="hero-caps-sub" style={lineStyle(0.78)}>
              Residencias · Presa Madín · Lago Esmeralda
            </span>
          </div>
        </div>

        {/* Bloque inferior */}
        <div className="hero-bottom" style={{ position: "relative", zIndex: 4 }}>
          <p className="hero-tagline" style={fadeStyle(1.1)}>
            Una vista que permanecerá intacta generación tras generación.
          </p>
          <div className="hero-scroll" style={fadeStyle(1.3)}>
            <span>Descubre</span>
          </div>
          <div className="hero-right" style={fadeStyle(1.2)}>
            <div>4 torres · 12 niveles</div>
            <div>205 m² · 3 recámaras</div>
            <div>Preventa abierta</div>
          </div>
        </div>
      </div>
    </header>);

}

function Manifiesto() {
  // — Halo de vela —
  const haloRef   = React.useRef(null);
  const rafRef    = React.useRef(null);
  const curRef    = React.useRef({ x: 50, y: 50 });
  const tgtRef    = React.useRef({ x: 50, y: 50 });
  const activeRef = React.useRef(false);
  const lerp = (a, b, t) => a + (b - a) * t;

  React.useEffect(() => { return () => cancelAnimationFrame(rafRef.current); }, []);

  const tick = () => {
    const c = curRef.current, t = tgtRef.current;
    c.x = lerp(c.x, t.x, 0.04);
    c.y = lerp(c.y, t.y, 0.04);
    if (haloRef.current) {
      haloRef.current.style.background =
        `radial-gradient(circle 680px at ${c.x.toFixed(2)}% ${c.y.toFixed(2)}%, rgba(184,97,44,0.10) 0%, rgba(184,97,44,0.04) 48%, transparent 70%)`;
    }
    if (Math.abs(c.x - t.x) + Math.abs(c.y - t.y) > 0.05) rafRef.current = requestAnimationFrame(tick);
    else activeRef.current = false;
  };

  const onMove = (e) => {
    const r = e.currentTarget.getBoundingClientRect();
    tgtRef.current = { x: ((e.clientX - r.left) / r.width) * 100, y: ((e.clientY - r.top) / r.height) * 100 };
    if (!activeRef.current) { activeRef.current = true; rafRef.current = requestAnimationFrame(tick); }
  };
  const onLeave = () => {
    tgtRef.current = { x: 50, y: 50 };
    if (!activeRef.current) { activeRef.current = true; rafRef.current = requestAnimationFrame(tick); }
  };

  // — Reveal al scroll —
  const secRef  = React.useRef(null);
  const [vis, setVis] = React.useState(false);
  React.useEffect(() => {
    const el = secRef.current;
    if (!el) return;
    const obs = new IntersectionObserver(([e]) => { if (e.isIntersecting) { setVis(true); obs.disconnect(); } }, { threshold: 0.12 });
    obs.observe(el);
    return () => obs.disconnect();
  }, []);

  // Palabras con marca de énfasis — shimmer delay escalonado
  const WORDS = [
    { w: "El" }, { w: "lujo" }, { w: "verdadero" }, { w: "no" }, { w: "se" },
    { w: "construye" }, { w: "con" }, { w: "ornamento," },
    { w: "sino" }, { w: "con" },
    { w: "silencio,", em: true, sd: "0s"   },
    { w: "luz",         em: true, sd: "-2.2s" },
    { w: "y" },
    { w: "perspectiva.", em: true, sd: "-4.5s" },
  ];

  // Estilo base para palabras normales
  const wStyle = (i) => {
    const d = (i * 0.075 + 0.18).toFixed(3);
    return {
      display: "inline-block",
      marginRight: "0.22em",
      opacity:   vis ? 1 : 0,
      filter:    vis ? "blur(0)" : "blur(7px)",
      transform: vis ? "translateY(0)" : "translateY(22px)",
      transition: [
        `opacity   1.1s cubic-bezier(0.22,1,0.36,1) ${d}s`,
        `filter    1.3s cubic-bezier(0.22,1,0.36,1) ${d}s`,
        `transform 1.1s cubic-bezier(0.22,1,0.36,1) ${d}s`,
      ].join(", "),
    };
  };

  // Estilo para palabras clave: además de blur+rise, letras se ensamblan
  const wStyleKey = (i) => {
    const d = (i * 0.075 + 0.18).toFixed(3);
    return {
      display: "inline-block",
      marginRight: "0.22em",
      opacity:      vis ? 1 : 0,
      filter:       vis ? "blur(0)" : "blur(8px)",
      transform:    vis ? "translateY(0)" : "translateY(26px)",
      letterSpacing: vis ? "-0.01em" : "0.18em",
      transition: [
        `opacity       1.2s cubic-bezier(0.22,1,0.36,1) ${d}s`,
        `filter        1.4s cubic-bezier(0.22,1,0.36,1) ${d}s`,
        `transform     1.2s cubic-bezier(0.22,1,0.36,1) ${d}s`,
        `letter-spacing 1.6s cubic-bezier(0.22,1,0.36,1) ${d}s`,
      ].join(", "),
    };
  };

  const fStyle = (delay) => ({
    opacity:   vis ? 1 : 0,
    transform: vis ? "none" : "translateY(10px)",
    transition: `opacity 1s ease ${delay}s, transform 1s ease ${delay}s`,
  });

  return (
    <section className="manifesto container" id="concepto"
      ref={secRef} onMouseMove={onMove} onMouseLeave={onLeave}
      style={{ position: "relative" }}>

      {/* Halo de vela */}
      <div ref={haloRef} style={{
        position: "absolute", inset: 0, pointerEvents: "none", zIndex: 0,
        background: "radial-gradient(circle 680px at 50% 50%, rgba(184,97,44,0.07) 0%, transparent 70%)",
        mixBlendMode: "multiply",
      }} />

      <div className="manifesto-grid" style={{ position: "relative", zIndex: 1 }}>
        <div className="manifesto-num" style={fStyle(0.1)}>01</div>
        <div className="manifesto-body">
          <span className="eyebrow terracota" style={fStyle(0.15)}>Visión del proyecto</span>
          <h2 className="manifesto-quote" style={{ fontFamily: "Manrope" }}>
            {WORDS.map((item, i) =>
              item.em
                ? (
                  <span key={i} style={wStyleKey(i)}>
                    <span className="mf-key" style={{ animationDelay: item.sd }}>{item.w}</span>
                  </span>
                )
                : <span key={i} style={wStyle(i)}>{item.w}</span>
            )}
          </h2>
          <p style={{ fontSize: "1.05rem", maxWidth: "52ch", lineHeight: 1.65, ...fStyle(1.4) }}>
            LAVISTA Residences nace de una certeza simple y exigente: crear un lugar donde vivir
            bien signifique algo concreto. Una residencia donde la arquitectura, la naturaleza
            y la privacidad convergen en un solo gesto.
          </p>
          <div className="manifesto-attrib" style={fStyle(1.65)}>
            Vivir en LAVISTA es elegir la calma como estilo de vida
          </div>
        </div>
        <div></div>
      </div>
      <span className="horizon-line" style={{ marginTop: "var(--section-y)" }} />
    </section>);

}

function Vista() {
  // — Scroll reveal —
  const secRef = React.useRef(null);
  const [vis, setVis] = React.useState(false);
  React.useEffect(() => {
    const el = secRef.current;
    if (!el) return;
    const obs = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) { setVis(true); obs.disconnect(); }
    }, { threshold: 0.15 });
    obs.observe(el);
    return () => obs.disconnect();
  }, []);

  // — Halo de cursor (screen blend en fondo oscuro) —
  const haloRef   = React.useRef(null);
  const rafRef    = React.useRef(null);
  const curRef    = React.useRef({ x: 30, y: 50 });
  const tgtRef    = React.useRef({ x: 30, y: 50 });
  const activeRef = React.useRef(false);
  const lerp = (a, b, t) => a + (b - a) * t;
  React.useEffect(() => { return () => cancelAnimationFrame(rafRef.current); }, []);

  const tick = () => {
    const c = curRef.current, t = tgtRef.current;
    c.x = lerp(c.x, t.x, 0.035);
    c.y = lerp(c.y, t.y, 0.035);
    if (haloRef.current)
      haloRef.current.style.background =
        `radial-gradient(circle 680px at ${c.x.toFixed(2)}% ${c.y.toFixed(2)}%, rgba(220,155,55,0.13) 0%, rgba(180,97,40,0.06) 45%, transparent 70%)`;
    if (Math.abs(c.x - t.x) + Math.abs(c.y - t.y) > 0.05) rafRef.current = requestAnimationFrame(tick);
    else activeRef.current = false;
  };
  const onMove = (e) => {
    const r = e.currentTarget.getBoundingClientRect();
    tgtRef.current = { x: ((e.clientX - r.left) / r.width) * 100, y: ((e.clientY - r.top) / r.height) * 100 };
    if (!activeRef.current) { activeRef.current = true; rafRef.current = requestAnimationFrame(tick); }
  };
  const onLeave = () => {
    tgtRef.current = { x: 30, y: 50 };
    if (!activeRef.current) { activeRef.current = true; rafRef.current = requestAnimationFrame(tick); }
  };

  // — Palabras del título —
  const TITLE = [
    "Uno","de","los","últimos","predios","con","vista",
    { w:"libre", em:true },
    "a","la","Presa","Madín."
  ];

  const wStyle = (i) => {
    const d = (i * 0.085 + 0.18).toFixed(3);
    return {
      display: "inline-block", marginRight: "0.2em",
      opacity:   vis ? 1 : 0,
      filter:    vis ? "blur(0)" : "blur(9px)",
      transform: vis ? "translateY(0)" : "translateY(26px)",
      transition: [
        `opacity   1.3s cubic-bezier(0.22,1,0.36,1) ${d}s`,
        `filter    1.5s cubic-bezier(0.22,1,0.36,1) ${d}s`,
        `transform 1.3s cubic-bezier(0.22,1,0.36,1) ${d}s`,
      ].join(", "),
    };
  };

  const fStyle = (d) => ({
    opacity:   vis ? 1 : 0,
    transform: vis ? "none" : "translateY(14px)",
    transition: `opacity 1.1s ease ${d}s, transform 1.1s ease ${d}s`,
  });

  return (
    <section className="vista" id="vista"
      ref={secRef} onMouseMove={onMove} onMouseLeave={onLeave}
      style={{ backgroundColor: "rgb(36, 36, 13)", position: "relative" }}>

      {/* Luz de fondo — evoca reflejo de agua */}
      <div className="vista-bg-shimmer" />

      {/* Halo de vela — screen blend (aditivo en oscuro) */}
      <div ref={haloRef} style={{
        position: "absolute", inset: 0, pointerEvents: "none", zIndex: 1,
        background: "radial-gradient(circle 680px at 30% 50%, rgba(220,155,55,0.10) 0%, transparent 70%)",
        mixBlendMode: "screen",
      }} />

      <div className="container" style={{ position: "relative", zIndex: 2 }}>
        <div style={{ maxWidth: "64ch", margin: "0 auto" }}>

          <span className="eyebrow" style={{ color: "#D4A574", ...fStyle(0.1) }}>
            La vista como eje generador
          </span>

          <h2 className="vista-title" style={{ marginTop: "1.5rem" }}>
            {TITLE.map((item, i) => {
              const word = typeof item === "string" ? item : item.w;
              const isEm = item.em;
              return isEm
                ? <span key={i} style={wStyle(i)}>
                    <em className="vista-key">{word}</em>
                  </span>
                : <span key={i} style={wStyle(i)}>{word}</span>;
            })}
          </h2>

          <p className="vista-text" style={fStyle(1.35)}>
            La colindancia con zona federal garantiza, por disposición legal, que ninguna construcción
            podrá obstruir la vista en el futuro. Es un activo que no figura en ningún plano — pero
            que vale más que cualquier acabado: la certeza de que lo que ves hoy, lo verás siempre.
          </p>
          <p className="vista-text" style={{ marginTop: "1rem", ...fStyle(1.6) }}>
            Las torres se orientan para que la fachada principal y las terrazas de mayor jerarquía
            apunten hacia ese paisaje. El elevador es panorámico no como capricho técnico, sino como
            decisión de diseño: cada viaje vertical es también una experiencia visual.
          </p>

        </div>
      </div>
    </section>);
}

function Arquitectura() {
  return (
    <section className="arq container" id="arquitectura">
      <div className="arq-header">
        <div>
          <span className="eyebrow terracota">02 · Arquitectura</span>
          <h2 className="arq-title" style={{ marginTop: "1.2rem" }}>
            Modernidad <em>contenida</em>.
          </h2>
        </div>
        <p className="arq-intro">
          La arquitectura de LAVISTA evita el exceso. Las fachadas hablan un lenguaje contemporáneo
          de volúmenes precisos, concreto aparente y cristal de gran formato, con detalles en negro
          que definen marcos y aberturas con la contundencia de un trazo limpio.
        </p>
      </div>

      <div className="arq-gallery">
        <div className="arq-card">
          <div className="img-reveal">
            <image-slot
              id="arq-fachada"
              shape="rect"
              fit="cover"
              src="assets/render-fachada-torres.png"
              placeholder="Render fachada principal">
            </image-slot>
          </div>
          <div className="arq-card-caption">
            <span>01 · Fachada poniente</span>
            <span>Concreto · Cristal · Negro</span>
          </div>
        </div>
        <div className="arq-card arq-card-tall">
          <div className="img-reveal">
            <image-slot
              id="arq-celosia"
              shape="rect"
              fit="cover"
              src="assets/render-terraza-pareja.jpg"
              placeholder="Celosía vertical de las terrazas">
            </image-slot>
          </div>
          <div className="arq-card-caption">
            <span>02 · Celosía vertical</span>
            <span>Lambrín</span>
          </div>
        </div>
      </div>

      <div className="arq-interiors">
        <div className="arq-interior-card">
          <image-slot id="arq-int-1" shape="rect" fit="cover" src="assets/render-sala.jpg" placeholder="Sala principal" />
        </div>
        <div className="arq-interior-card">
          <image-slot id="arq-int-2" shape="rect" fit="cover" src="assets/render-comedor.jpg" placeholder="Comedor integrado" />
        </div>
        <div className="arq-interior-card">
          <image-slot id="arq-int-3" shape="rect" fit="cover" src="assets/render-terraza-pareja.jpg" placeholder="Terraza con asador" />
        </div>
        <div className="arq-interior-card">
          <image-slot id="arq-int-4" shape="rect" fit="cover" src="assets/render-terraza-lago.jpg" placeholder="Terraza · vista Presa Madín" />
        </div>
      </div>
    </section>);

}

function Departamento() {
  // — Scroll reveal —
  const secRef = React.useRef(null);
  const [vis, setVis] = React.useState(false);
  React.useEffect(() => {
    const el = secRef.current;
    if (!el) return;
    const obs = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) { setVis(true); obs.disconnect(); }
    }, { threshold: 0.12 });
    obs.observe(el);
    return () => obs.disconnect();
  }, []);

  // — Halo de vela —
  const haloRef   = React.useRef(null);
  const rafRef    = React.useRef(null);
  const curRef    = React.useRef({ x: 72, y: 50 });
  const tgtRef    = React.useRef({ x: 72, y: 50 });
  const activeRef = React.useRef(false);
  const lerp = (a, b, t) => a + (b - a) * t;
  React.useEffect(() => { return () => cancelAnimationFrame(rafRef.current); }, []);

  const tick = () => {
    const c = curRef.current, t = tgtRef.current;
    c.x = lerp(c.x, t.x, 0.04);
    c.y = lerp(c.y, t.y, 0.04);
    if (haloRef.current)
      haloRef.current.style.background =
        `radial-gradient(circle 580px at ${c.x.toFixed(2)}% ${c.y.toFixed(2)}%, rgba(184,97,44,0.08) 0%, rgba(184,97,44,0.03) 50%, transparent 70%)`;
    if (Math.abs(c.x - t.x) + Math.abs(c.y - t.y) > 0.05) rafRef.current = requestAnimationFrame(tick);
    else activeRef.current = false;
  };
  const onMove = (e) => {
    const r = e.currentTarget.getBoundingClientRect();
    tgtRef.current = { x: ((e.clientX - r.left) / r.width) * 100, y: ((e.clientY - r.top) / r.height) * 100 };
    if (!activeRef.current) { activeRef.current = true; rafRef.current = requestAnimationFrame(tick); }
  };
  const onLeave = () => {
    tgtRef.current = { x: 72, y: 50 };
    if (!activeRef.current) { activeRef.current = true; rafRef.current = requestAnimationFrame(tick); }
  };

  // — Count-up para specs —
  const SPECS = [
    { l: "Master bedroom",     raw: 19.36, fmt: "19.36", u: " m²" },
    { l: "Estancia",           raw: 24.88, fmt: "24.88", u: " m²" },
    { l: "Terraza con asador", raw: 27.44, fmt: "27.44", u: " m²" },
    { l: "Total construido",   raw: 205,   fmt: "205",   u: " m²" },
    { l: "Estacionamiento",    raw: 2,     fmt: "2",     u: " cajones" },
  ];
  const [counts, setCounts] = React.useState(SPECS.map(s => s.fmt.includes(".") ? "0.00" : "0"));

  React.useEffect(() => {
    if (!vis) return;
    const duration = 1100;
    SPECS.forEach((s, i) => {
      const dec = s.fmt.includes(".") ? 2 : 0;
      const delayMs = 700 + i * 110;
      const startTime = performance.now() + delayMs;
      const run = (now) => {
        if (now < startTime) { requestAnimationFrame(run); return; }
        const p = Math.min((now - startTime) / duration, 1);
        const eased = 1 - Math.pow(1 - p, 3);
        const val = (s.raw * eased).toFixed(dec);
        setCounts(prev => { const n = [...prev]; n[i] = val; return n; });
        if (p < 1) requestAnimationFrame(run);
      };
      requestAnimationFrame(run);
    });
  }, [vis]);

  // — Estilos de animación —
  const fStyle = (d) => ({
    opacity:   vis ? 1 : 0,
    transform: vis ? "none" : "translateY(14px)",
    transition: `opacity 1s ease ${d}s, transform 1s ease ${d}s`,
  });

  const titleWS = (i, isKey) => {
    const d = (0.45 + i * 0.12).toFixed(2);
    return {
      display: "inline-block", marginRight: "0.2em",
      opacity:   vis ? 1 : 0,
      filter:    vis ? "blur(0)" : "blur(6px)",
      transform: vis ? "translateY(0)" : "translateY(20px)",
      ...(isKey ? { letterSpacing: vis ? "-0.02em" : "0.12em" } : {}),
      transition: [
        `opacity   1.1s cubic-bezier(0.22,1,0.36,1) ${d}s`,
        `filter    1.3s cubic-bezier(0.22,1,0.36,1) ${d}s`,
        `transform 1.1s cubic-bezier(0.22,1,0.36,1) ${d}s`,
        ...(isKey ? [`letter-spacing 1.5s cubic-bezier(0.22,1,0.36,1) ${d}s`] : []),
      ].join(", "),
    };
  };

  return (
    <section className="dept" id="departamento"
      ref={secRef} onMouseMove={onMove} onMouseLeave={onLeave}
      style={{ position: "relative" }}>

      {/* Halo de vela */}
      <div ref={haloRef} style={{
        position: "absolute", inset: 0, pointerEvents: "none", zIndex: 0,
        background: "radial-gradient(circle 580px at 72% 50%, rgba(184,97,44,0.06) 0%, transparent 70%)",
        mixBlendMode: "multiply",
      }} />

      <div className="container" style={{ position: "relative", zIndex: 1 }}>
        <div className="dept-grid">

          {/* Imagen — curtain reveal de izquierda a derecha */}
          <div className="dept-image" style={{
            clipPath: vis ? "inset(0 0% 0 0 round 3px)" : "inset(0 100% 0 0 round 3px)",
            transition: "clip-path 1.5s cubic-bezier(0.22,1,0.36,1) 0.15s",
          }}>
            <img
              src="assets/render-torres-cielo.png"
              alt="Fachada torres LAVISTA"
              style={{ width: "100%", aspectRatio: "16/11", objectFit: "cover", display: "block" }}
            />
          </div>

          {/* Contenido */}
          <div>
            <span className="eyebrow terracota" style={fStyle(0.4)}>03 · El departamento</span>

            <h2 className="dept-title" style={{ marginTop: "1.2rem" }}>
              <span style={titleWS(0, true)}>
                <span className="mf-key" style={{ animationDelay: "-1s" }}>205 m²</span>
              </span>
              <span style={titleWS(1, false)}>que</span>
              <span style={titleWS(2, false)}>respiran.</span>
            </h2>

            <p className="dept-text" style={fStyle(0.85)}>
              Las dimensiones no se justifican por la cantidad de habitaciones —son tres recámaras,
              como en cualquier departamento— sino por la generosidad de cada espacio. Dimensiones
              que permiten mobiliario real, circulación holgada y vida cotidiana sin fricción.
            </p>

            <div className="dept-specs">
              {SPECS.map((s, i) => (
                <div className="dept-spec" key={i} style={{
                  opacity:   vis ? 1 : 0,
                  transform: vis ? "none" : "translateY(10px)",
                  transition: `opacity 0.75s ease ${(1.0 + i * 0.1).toFixed(2)}s, transform 0.75s ease ${(1.0 + i * 0.1).toFixed(2)}s`,
                }}>
                  <span className="l">{s.l}</span>
                  <span className="v">{counts[i]}{s.u}</span>
                </div>
              ))}
            </div>

            <p className="dept-text" style={{ marginTop: "2rem", fontStyle: "italic", fontFamily: "var(--font-display)", fontSize: "1.15rem", ...fStyle(1.6) }}>
              Tres recámaras. Una terraza que mira al agua. Un elevador panorámico que convierte
              cada llegada en una pausa.
            </p>
          </div>

        </div>
      </div>
    </section>);

}

Object.assign(window, { Nav, Hero, Manifiesto, Vista, Arquitectura, Departamento, InkReveal });