/* === Shared: constants, Header, Footer, building blocks === */ const SITE = { domain: "vinodepasto.com", email: "info@vinodepasto.com", buyUrl: "https://bodegasbaron.es/products/vino-blanco-paranormal?utm_source=vinodepasto&utm_medium=referral&utm_campaign=paranormal", buyUrlBlanco: "https://bodegasbaron.es/products/vino-blanco-paranormal?utm_source=vinodepasto&utm_medium=referral&utm_campaign=paranormal_blanco", buyUrlOxidativo: "https://bodegasbaron.es/products/vino-blanco-oxidativo-paranormal-2023?utm_source=vinodepasto&utm_medium=referral&utm_campaign=paranormal_oxidativo", coupon: "PARANORMAL10", }; const NAV = [ { id: "home", label: "Inicio", path: "/" }, { id: "que-es", label: "Vino de Pasto", path: "/que-es-vino-de-pasto" }, { id: "paranormal", label: "Paranormal", path: "/paranormal" }, { id: "bodega", label: "Bodegas Barón", path: "/bodegas-baron" }, { id: "blog", label: "Cuaderno", path: "/blog" }, ]; /* --- minimal hash router --- */ function useRoute() { const [route, setRoute] = React.useState(() => window.location.hash.replace(/^#/, "") || "/"); React.useEffect(() => { const onHash = () => { setRoute(window.location.hash.replace(/^#/, "") || "/"); window.scrollTo({ top: 0, behavior: "instant" }); }; window.addEventListener("hashchange", onHash); return () => window.removeEventListener("hashchange", onHash); }, []); return route; } function go(path) { window.location.hash = path; } function Link({ to, children, className, style, onClick }) { return ( { if (onClick) onClick(e); }} > {children} ); } /* === Header === */ function Header({ route }) { const active = (path) => { if (path === "/") return route === "/" || route === ""; return route === path || route.startsWith(path + "/"); }; return (
Vino de Pasto vinodepasto.com
Comprar Caso 001
); } /* === Footer === */ function Footer() { return ( ); } /* === Reusable bits === */ function SectionNum({ n, children }) { return
{n}{children}
; } function CouponBox({ tone = "light" }) { const dark = tone === "dark"; const [copied, setCopied] = React.useState(false); const copy = () => { navigator.clipboard && navigator.clipboard.writeText(SITE.coupon); setCopied(true); setTimeout(() => setCopied(false), 1600); }; return (
Código de descuento
{SITE.coupon}
{copied ? "Copiado ✓" : "Copiar →"}
); } function ExternalBuyButton({ children = "Comprar en Bodegas Barón", variant = "solid", small = false, href }) { return ( {children} ); } /* === ZoomImage — large product photo with magnifier lupa on hover/move === */ function ZoomImage({ src, alt, zoom = 2.4, lensSize = 220, captionTop, captionBottom }) { const wrapRef = React.useRef(null); const imgRef = React.useRef(null); const lensRef = React.useRef(null); const [active, setActive] = React.useState(false); const [coarse, setCoarse] = React.useState(false); const [touchOn, setTouchOn] = React.useState(false); React.useEffect(() => { setCoarse(window.matchMedia && window.matchMedia("(pointer: coarse)").matches); }, []); const onMove = (e) => { const wrap = wrapRef.current, img = imgRef.current, lens = lensRef.current; if (!wrap || !img || !lens) return; const r = img.getBoundingClientRect(); const pt = e.touches ? e.touches[0] : e; let x = pt.clientX - r.left; let y = pt.clientY - r.top; if (x < 0 || y < 0 || x > r.width || y > r.height) { if (!e.touches) setActive(false); return; } const half = lensSize / 2; // clamp lens within the image const lx = Math.max(half, Math.min(r.width - half, x)); const ly = Math.max(half, Math.min(r.height - half, y)); lens.style.left = (lx - half) + "px"; lens.style.top = (ly - half) + "px"; // background position const bgX = -(x * zoom - half); const bgY = -(y * zoom - half); lens.style.backgroundImage = `url('${src}')`; lens.style.backgroundSize = `${r.width * zoom}px ${r.height * zoom}px`; lens.style.backgroundPosition = `${bgX}px ${bgY}px`; }; const showLens = active || touchOn; return (
{captionTop &&
{captionTop}
}
!coarse && setActive(true)} onMouseLeave={() => setActive(false)} onMouseMove={onMove} onTouchStart={(e) => { setTouchOn(true); onMove(e); }} onTouchMove={(e) => { e.preventDefault(); onMove(e); }} onTouchEnd={() => setTouchOn(false)} > {alt} {captionBottom &&
{captionBottom}
}
); } /* AnimatedLabelBackground — placeholder until WebM/MP4 supplied */ function AnimatedLabelBackground({ opacity = 0.55, position = "right", note = "VIDEO IA — etiqueta Paranormal · WebM/MP4" }) { return (
{note}
); } /* WineSpecCard — used in product page and home */ function WineSpec({ label, value }) { return (
{label}
{value}
); } /* Page wrapper with mount animation */ function Page({ children }) { return
{children}
; } Object.assign(window, { SITE, NAV, useRoute, go, Link, Header, Footer, SectionNum, CouponBox, ExternalBuyButton, AnimatedLabelBackground, WineSpec, Page, ZoomImage, });