// === Modal selector de WhatsApp (evento `pl-wa-open`) === const WaChooserModal = () => { const [state, setState] = React.useState({ open: false, msg: "" }); React.useEffect(() => { const handler = (e) => setState({ open: true, msg: e.detail?.msg || "" }); window.addEventListener("pl-wa-open", handler); return () => window.removeEventListener("pl-wa-open", handler); }, []); const close = () => setState(s => ({ ...s, open: false })); if (!state.open) return null; const go = (number) => { window.open(`https://wa.me/${number}?text=${encodeURIComponent(state.msg)}`, "_blank", "noreferrer"); close(); }; return ReactDOM.createPortal(
e.stopPropagation()} > {/* Header */}
Contacto
Seleccione un número de contacto
{/* Options */}
, document.body ); }; // Auto-mount modal una sola vez (function mountWaChooser() { if (document.getElementById("pl-wa-chooser-root")) return; const div = document.createElement("div"); div.id = "pl-wa-chooser-root"; document.body.appendChild(div); ReactDOM.createRoot(div).render(); })(); // === Toast al agregar al carrito (evento `pl-cart-added` desde cart.jsx) === const CartAddToast = () => { const [show, setShow] = React.useState(false); const [leaving, setLeave] = React.useState(false); const [title, setTitle] = React.useState(""); const [toastKey, setKey] = React.useState(0); const hideTimer = React.useRef(null); const leaveTimer = React.useRef(null); React.useEffect(() => { if (!document.getElementById("pl-toast-kf")) { const s = document.createElement("style"); s.id = "pl-toast-kf"; s.textContent = ` @keyframes toastIn { 0% { opacity:0; transform:translateX(calc(100% + 48px)) scale(0.94); } 60% { opacity:1; transform:translateX(-10px) scale(1.01); } 80% { transform:translateX(4px) scale(0.995); } 100% { opacity:1; transform:translateX(0) scale(1); } } @keyframes toastOut { 0% { opacity:1; transform:translateX(0) scale(1); } 100% { opacity:0; transform:translateX(calc(100% + 48px)) scale(0.94); } } @keyframes toastProgress { from { width:100%; } to { width:0%; } } `; document.head.appendChild(s); } const onAdded = (e) => { const t = e.detail?.title || ""; setTitle(t.length > 55 ? t.slice(0, 52) + "…" : t); setKey(k => k + 1); setLeave(false); setShow(true); clearTimeout(hideTimer.current); clearTimeout(leaveTimer.current); // start exit animation at 1.5s, fully unmount at 1.9s hideTimer.current = setTimeout(() => setLeave(true), 1500); leaveTimer.current = setTimeout(() => { setShow(false); setLeave(false); }, 1900); }; window.addEventListener("pl-cart-added", onAdded); return () => { window.removeEventListener("pl-cart-added", onAdded); clearTimeout(hideTimer.current); clearTimeout(leaveTimer.current); }; }, []); if (!show || !title) return null; return (
{/* Gold left accent */}
Agregado al carrito
{title}
{/* Progress bar */}
); }; // === Header === // homePath: "" para index.html, "index.html" para catalog page const Header = ({ active, homePath = "" }) => { const [scrolled, setScrolled] = React.useState(false); const [open, setOpen] = React.useState(false); const [catOpen, setCatOpen] = React.useState(false); const [cartN, setCartN] = React.useState(() => typeof window.PL_cartCount === "function" ? window.PL_cartCount() : 0 ); const catTimer = React.useRef(null); React.useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 12); window.addEventListener("scroll", onScroll, { passive: true }); return () => window.removeEventListener("scroll", onScroll); }, []); React.useEffect(() => { const sync = () => setCartN(typeof window.PL_cartCount === "function" ? window.PL_cartCount() : 0); window.addEventListener("pl-cart-change", sync); window.addEventListener("storage", sync); return () => { window.removeEventListener("pl-cart-change", sync); window.removeEventListener("storage", sync); }; }, []); const click = (e, id) => { e.preventDefault(); setOpen(false); if (homePath) { window.location.href = `${homePath}#${id}`; } else { document.getElementById(id)?.scrollIntoView({ behavior: "smooth", block: "start" }); } }; const logoHref = homePath ? homePath : "#inicio"; return ( <>
{/* Logo mark */} homePath ? null : click(e, "inicio")} className="flex items-center gap-2 group"> Psicología Libros
Psicología Libros
Librería especializada
{/* Desktop nav */}
{cartN > 0 && ( {cartN > 99 ? "99+" : cartN} )}
{open && (
)}
); }; // === Logo imagen sin fondo === const LogoSVG = ({ size = 280 }) => ( Psicología Libros ); // === Vitrina del hero === const VITRINE_FALLBACK = [ { pal:"r", short:"Trauma\n& Duelo", title:"Trauma & Duelo", author:"", cat:"trauma" }, { pal:"b", short:"Neuro-\npsicología", title:"Neuropsicología", author:"", cat:"neuro" }, { pal:"coal", short:"La Mente\nHumana", title:"La Mente Humana", author:"", cat:"clinica" }, { pal:"g", short:"Mind-\nfulness", title:"Mindfulness", author:"", cat:"mindfulness" }, { pal:"p", short:"Salud\nMental", title:"Salud Mental", author:"", cat:"salud" }, ]; const VIT_SLOTS = [ { pos:"absolute", style:{ left:"10%", bottom:0, transform:"rotate(-10deg)", zIndex:1 }, w:100, h:150, depth:3 }, { pos:"absolute", style:{ left:"28%", bottom:0, transform:"rotate(-4deg)", zIndex:2 }, w:100, h:152, depth:2, mobileStyle:{ left:"8%", bottom:0, transform:"rotate(-10deg)", zIndex:2 }, mw:88, mh:132 }, { pos:"relative", style:{ zIndex:4 }, w:115, h:172, center:true, depth:0, mobileStyle:{ zIndex:4 }, mw:110, mh:165 }, { pos:"absolute", style:{ right:"28%", bottom:0, transform:"rotate(4deg)", zIndex:2 }, w:100, h:152, depth:2, mobileStyle:{ right:"8%", bottom:0, transform:"rotate(10deg)", zIndex:2 }, mw:88, mh:132 }, { pos:"absolute", style:{ right:"10%", bottom:0, transform:"rotate(10deg)", zIndex:1 }, w:100, h:150, depth:3 }, ]; const VIT_BG = { coal:"#111111", r:"#7a0d12", b:"#0e2640", i:"#efe6d4", g:"#122b1a", p:"#2b0e4a" }; const VIT_TXT = { coal:"#F8F4EA", r:"#F8F4EA", b:"#F8F4EA", i:"#2b2418", g:"#F8F4EA", p:"#F8F4EA" }; const VIT_LBL = { coal:"rgba(201,162,74,0.65)", r:"rgba(229,180,100,0.6)", b:"rgba(160,190,230,0.5)", i:"rgba(91,74,34,0.55)", g:"rgba(120,190,120,0.45)", p:"rgba(190,140,220,0.5)" }; const VIT_ACC = { coal:"#C9A24A", r:"#e8a060", b:"#7aaedc", i:"#9a7a30", g:"#6bbf6b", p:"#b080d8" }; const VitrineBook = ({ book, fb, slot, isCenter }) => { const [hov, setHov] = React.useState(false); const [imgErr, setImgErr] = React.useState(false); const pal = book?.cover?.palette || fb.pal; const short = (book?.cover?.short || fb.short).replace(/\\n/g,"\n"); const title = book?.title || fb.title; const author = book?.author || fb.author; const imgUrl = book ? ((book.image_urls && book.image_urls[0]) || book.image_url || null) : null; const depth = slot.depth ?? 0; /* Use mobile dimensions/position when viewport < 640px */ const isMobile = typeof window !== "undefined" && window.innerWidth < 640; const activeStyle = (isMobile && slot.mobileStyle) ? slot.mobileStyle : slot.style; const w = (isMobile && slot.mw) ? slot.mw : slot.w; const h = (isMobile && slot.mh) ? slot.mh : slot.h; const baseTransform = activeStyle.transform || ""; const dimOpacity = depth === 0 ? 0 : depth === 1 ? 0.08 : depth === 2 ? 0.22 : 0.38; const baseShadow = depth === 0 ? "drop-shadow(0 20px 40px rgba(0,0,0,0.55))" : depth === 2 ? "drop-shadow(0 12px 24px rgba(0,0,0,0.38))" : "drop-shadow(0 8px 16px rgba(0,0,0,0.28))"; const wrapStyle = { position: slot.pos === "relative" ? "relative" : "absolute", ...activeStyle, transition: "transform 0.32s cubic-bezier(0.34,1.56,0.64,1), filter 0.3s ease, z-index 0s", animation: (slot.pos === "relative" && isCenter && !hov) ? "logoFloat 5s ease-in-out infinite" : "none", filter: hov ? "drop-shadow(0 28px 52px rgba(0,0,0,0.65))" : baseShadow, ...(hov ? { transform: baseTransform ? `${baseTransform} scale(1.18) translateY(-16px)` : "scale(1.18) translateY(-16px)", zIndex: 20, } : { transform: baseTransform || undefined, }), cursor: "pointer", }; const handleClick = () => { if (book) { window.location.href = `libro.html?id=${book.id}`; } else { // Busca el primer libro real de esa categoría const match = (window.BOOKS || []).find(b => b.category === fb.cat && b.is_active !== false); window.location.href = match ? `libro.html?id=${match.id}` : "catalogo.html"; } }; return (
setHov(true)} onMouseLeave={() => setHov(false)} onClick={handleClick}>
{/* Real cover image */} {imgUrl && !imgErr && ( {title} setImgErr(true)} style={{ position:"absolute", inset:0, width:"100%", height:"100%", objectFit:"cover", display:"block" }} /> )} {/* Depth dim overlay — darker for far books */} {dimOpacity > 0 && (
)} {/* Inner border */}
{/* Corner accents */}
{/* Texto portada — solo cuando no hay imagen real */} {(!imgUrl || imgErr) && (
P·L
{short.split("\n").map((l,i,a) => {l}{i})}
)} {/* Overlay hover */}
Psicología Libros {title} {book && ( Ver libro → )}
); }; const HeroVitrine = () => { const vitrine = React.useMemo(() => { const books = (window.BOOKS || []).filter(b => b.vitrine_order != null && b.is_active !== false); const slots = [null,null,null,null,null]; books.forEach(b => { if (b.vitrine_order >= 1 && b.vitrine_order <= 5) slots[b.vitrine_order-1] = b; }); return slots; }, []); return (
{VIT_SLOTS.map((slot, i) => ( /* Hide outermost books (i=0 and i=4) on mobile — show only 3 */
))}
); }; // === Hero === const Hero = () => (
{/* ── Fondo decorativo ── */}
{/* Blobs de color */}
{/* Watermark — texto detrás */}
Psicología Libros
{/* Líneas horizontales decorativas */}
{/* Marca de registro — esquina superior izquierda */}
{/* Esquina superior derecha */}
{/* Esquina inferior izquierda */}
{/* Esquina inferior derecha */}
{/* Línea vertical decorativa centro */}
{/* ── Columna izquierda: Logo ── */}
Psicología Libros
Ver catálogo
{/* ── Columna derecha: vitrina de libros ── */}
{/* Gold corner accent */}
{/* Eyebrow */}
Catálogo especializado
{/* Libros en abanico */} {/* Stats */}
+1.000
Títulos
12
Categorías
100%
Especializado
); // === Custom Select — reemplaza setQuery(e.target.value)} placeholder="Buscar por título, autor o tema…" className="flex-1 bg-transparent outline-none text-[14px] placeholder:text-pl-gray/70" aria-label="Buscar libros" /> {query && ( )}
{/* Category select — custom dropdown */}
({ value:c.id, label:c.name, Icon:c.Icon })) ]} /> {(filter !== "all" || query) && ( )}
{/* Active filter chips */} {(filter !== "all" || query) && (
Filtros: {filter !== "all" && ( {catName(filter)} )} {query && ( "{query}" )}
)}
{/* Category sections (default view) */} {showSections ? (
{sections.map(sec => { const I = sec.Icon; return (
{/* Section header */}
{I && }
{sec.name}

{sec.name}

{sec.desc}

{/* Books row — show up to 4 */}
{sec.books.slice(0, 4).map(b => )}
{sec.books.length > 4 && (
)}
); })}
) : allFiltered.length > 0 ? (
{allFiltered.map(b => )}
) : (

Sin resultados

Probá con otra palabra o consultanos por WhatsApp.

)}
); }; // === About === const About = () => (
¿Quiénes somos?

En Psicología Libros creemos que cada página puede abrir una nueva forma de comprender la mente humana.

Somos una librería especializada en libros de psicología, salud mental y desarrollo humano. Nuestro objetivo es acercar materiales de valor a estudiantes, profesionales y lectores interesados en profundizar en el conocimiento, la reflexión y el bienestar emocional.

Seleccionamos libros que aportan claridad, formación y acompañamiento, con una mirada seria, responsable y comprometida con el aprendizaje.

Estanterías de libros en una librería
Sabiduría en cada página
); // === Benefits === const Benefits = () => (
Una librería pensada para quienes buscan conocimiento con propósito} center />
{BENEFITS.map((b, i) => )}
); // === FAQ === const FAQSection = () => { const [open, setOpen] = React.useState(0); return (
FAQ

Preguntas frecuentes

Resolvé las dudas más comunes. Si tu pregunta no está aquí, escribinos por WhatsApp.

{FAQS.map((f, i) => ( setOpen(open === i ? -1 : i)} /> ))}
); }; // === Contact === const Contact = () => (
Contacto

¿Buscás un libro en particular?

Escribinos y te ayudamos a encontrar el material adecuado para tu interés, estudio o formación.

); // === Botón scroll al inicio === const ScrollTopBtn = () => { const [visible, setVisible] = React.useState(false); React.useEffect(() => { const onScroll = () => setVisible(window.scrollY > 300); window.addEventListener("scroll", onScroll, { passive: true }); return () => window.removeEventListener("scroll", onScroll); }, []); if (!visible) return null; return ( ); }; // === Footer === const Footer = () => ( ); Object.assign(window, { Header, LogoSVG, Hero, CategoryQuickNav, FeaturedCarousel, CatalogPreviewCarousel, CatalogBySections, About, Benefits, FAQSection, Contact, Footer, ScrollTopBtn, });