/* === 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)}
>
{coarse ? "Toca y desliza para ampliar" : "Pasa el ratón para ampliar"}
{captionBottom && {captionBottom}}
);
}
/* AnimatedLabelBackground — placeholder until WebM/MP4 supplied */
function AnimatedLabelBackground({ opacity = 0.55, position = "right", note = "VIDEO IA — etiqueta Paranormal · WebM/MP4" }) {
return (
);
}
/* WineSpecCard — used in product page and home */
function WineSpec({ label, value }) {
return (
);
}
/* 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,
});