(function(){ // desktop-app.jsx — AM Tulum Desktop v2 // Paleta black/cream/gold. Layout 1440px wide. // Mismo data + tokens que el mobile v2. const { useState, useEffect, useRef } = React; // ─── Section wrapper for scroll anchoring ───────── function Section({ id, children, dark = false, padded = true, style = {} }) { return (
{children}
); } function Container({ children, style = {}, refProp }) { return (
{children}
); } function SectionLabel({ num, total, children, color }) { return (
{num} {children} {total && / {total}}
); } // ─── Top Nav ─────────────────────────────────────── function Nav({ lang, setLang, ui }) { const [scrolled, setScrolled] = useState(false); useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 80); window.addEventListener('scroll', onScroll); return () => window.removeEventListener('scroll', onScroll); }, []); const links = [ { id: 'servicios', label: ui.nav.servicios }, { id: 'beneficios', label: ui.nav.beneficios }, { id: 'proceso', label: ui.nav.proceso }, { id: 'portafolio', label: ui.nav.portafolio }, { id: 'contacto', label: ui.nav.contacto }]; const linkColor = scrolled ? AM.ink : 'rgba(255,255,255,0.85)'; const dimColor = scrolled ? 'rgba(10,10,10,0.4)' : 'rgba(255,255,255,0.5)'; return ( ); } // ─── Hero ────────────────────────────────────────── function Hero({ copy, ui }) { // Split title on the italic phrase so we can color it. const titleEs = ui === UI.es; return (

{titleEs ? <>Tu propiedad en Tulum,
cuidada como nuestra. : <>Your Tulum home,
cared for like our own. }

{(copy.hero.sub || '').split('.')[0]}.

{/* Credenciales: Superhost destacado + métricas inline */} {/* Chip Superhost */}
Superhost · {titleEs ? 'desde 2019' : 'since 2019'}
{/* Scroll indicator */}
{ui.hero.scroll}
{/* Stats card — anchored at hero bottom */}
{[ ...copy.stats, { v: '87', l: ui.misc.propertiesInOp }]. map((s, i, arr) =>
{s.v}
{s.l}
)}
); } // ─── Intro / Value Props — Antes/Después interactivo ── function Intro({ ui }) { const [mode, setMode] = React.useState('after'); const ref = React.useRef(null); const [visible, setVisible] = React.useState(false); React.useEffect(() => { if (!ref.current) return; const obs = new IntersectionObserver( ([e]) => e.isIntersecting && setVisible(true), { threshold: 0.2 } ); obs.observe(ref.current); return () => obs.disconnect(); }, []); const titleEs = (ui.intro.eyebrow || '').includes('PROPIETARIOS'); const items = titleEs ? [ { before: 'Te despierta el huésped a las 3am', after: 'Atención 24/7 sin interrumpirte' }, { before: 'Limpiezas inconsistentes', after: 'Staff propio · estándar Superhost' }, { before: 'Ocupación 30-45%', after: 'Ocupación 70-85% promedio' }, { before: 'Reviews 4.3★', after: 'Reviews 4.9★ · Highly Satisfied' }, { before: 'Pricing fijo · pierdes temporada', after: 'Pricing dinámico diario' }] : [ { before: 'Guests waking you at 3am', after: '24/7 attention · without interrupting you' }, { before: 'Inconsistent cleaning', after: 'In-house staff · Superhost standard' }, { before: '30-45% occupancy', after: '70-85% average occupancy' }, { before: 'Reviews 4.3★', after: 'Reviews 4.9★ · Highly Satisfied' }, { before: 'Static pricing · lost season', after: 'Daily dynamic pricing' }]; return (
{ui.intro.eyebrow}

{ui.intro.title[0]}
{ui.intro.title[1]}
{ui.intro.title[2]}.

{titleEs ? 'Esto es lo que cambia con AM Tulum.' : 'This is what changes with AM Tulum.'}

{/* Toggle */}
{[ { id: 'before', l: titleEs ? 'Sin AM' : 'Without AM' }, { id: 'after', l: titleEs ? 'Con AM Tulum' : 'With AM Tulum' }]. map((t) => )}
{/* Lista */}
{items.map((it, i) => { const isAfter = mode === 'after'; return (
{isAfter ? : }
{isAfter ? it.after : it.before}
); })}
); } // ─── Services ───────────────────────────────────── // Service icons (minimal line, 1px stroke) — index aligned with copy.services const SVC_ICONS = [ // 01 Marketing — megaphone / signal (c) => , // 02 Dynamic pricing — line chart with arrow up (c) => , // 03 24/7 hospitality — clock with concierge bell (c) => , // 04 Cleaning & upkeep — broom / sparkle (c) => , // 05 Accounting — ledger / receipt (c) => , // 06 Monthly reports — document with chart (c) => ]; function Services({ copy, ui }) { const [hover, setHover] = useState(-1); return (
{ui.services.eyebrow}

{ui.services.title} {ui.services.titleItalic}.

{/* 6 servicios en una sola fila — icono minimal + label */}
{copy.services.map((s, i) => { const Icon = SVC_ICONS[i]; const active = hover === i; return (
setHover(i)} onMouseLeave={() => setHover(-1)} style={{ padding: '56px 20px 44px', borderRight: i < 5 ? '0.5px solid rgba(245,241,232,0.14)' : 'none', display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center', gap: 24, transition: 'background 0.4s', background: active ? 'rgba(199,162,93,0.04)' : 'transparent', cursor: 'default' }}> {/* number badge */}
0{i + 1}
{/* icon */}
{Icon(active ? AM.gold : 'rgba(245,241,232,0.85)')}
{/* label */}
{s.t}
); })}
{/* footnote */}
· {ui.services.body || 'Todo bajo un solo equipo'} ·
); } // ─── Benefits ───────────────────────────────────── function Benefits({ copy, ui }) { return (
{ui.benefits.eyebrow}

{ui.benefits.title[0]}
{ui.benefits.title[1]} {ui.benefits.titleItalic}.

{ui.benefits.body ?

{ui.benefits.body}

: null}
{copy.benefits.map((it, i) =>
{it.n}

{it.t}

{it.d}

)}
); } // ─── Process ────────────────────────────────────── function Process({ copy, ui }) { return (
{ui.process.eyebrow}

{ui.process.title} {ui.process.titleItalic}.

{/* Connecting line */}
{copy.steps.map((s, i) =>
{s.n}

{s.t}

{s.d}

)}
); } // ─── Properties ─────────────────────────────────── function Properties({ copy, ui }) { return (
{ui.portfolio.eyebrow}

{ui.portfolio.title} {ui.portfolio.titleItalic}.

{ui.portfolio.body}

{copy.properties.map((p, i) => { e.currentTarget.style.transform = 'translateY(-4px)'; e.currentTarget.style.boxShadow = '0 1px 2px rgba(0,0,0,0.04), 0 16px 40px rgba(0,0,0,0.08)'; }} onMouseLeave={(e) => { e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.boxShadow = '0 1px 2px rgba(0,0,0,0.04), 0 8px 24px rgba(0,0,0,0.05)'; }}>

{p.name}

)}
); } // ─── KPIs ───────────────────────────────────────── function KPIs({ copy, ui }) { return (
{ui.kpis.eyebrow}

{ui.kpis.title}
{ui.kpis.titleItalic}.

93%

{ui.kpis.highlightPre} {ui.kpis.highlightStrong} {ui.kpis.highlightPost}

{ui.kpis.source}
{copy.kpis.map((k, i, arr) =>
{k.v}
{k.l}
{k.b}
)}
{/* Decade strip removed — stats card in hero already covers headline numbers. */}
); } // ─── Contact CTA ────────────────────────────────── function Contact({ copy, ui }) { return (
{ui.contact.eyebrow}

{ui.contact.title}
{ui.contact.titleItalic}.

{ui.contact.body}

{"\n"}
{ui.contact.auditTitle}
{ui.contact.audit.map((it, i, arr) =>
{String(i + 1).padStart(2, '0')}
{it}
)}
); } // ─── Footer ─────────────────────────────────────── function Footer({ copy, ui }) { // Each entry: { text, href? } so we can render clickable links. const contactCol = { t: ui.footer.cols[2].t, l: [ { text: copy.contact.wa, href: copy.contact.waHref }, { text: copy.contact.email, href: copy.contact.emailHref }, { text: ui.footer.loc }] }; const navMap = { 'Servicios': '#servicios', 'Comercialización': '#servicios', 'Hospitalidad': '#servicios', 'Limpieza': '#servicios', 'Mantenimiento': '#servicios', 'Marketing': '#servicios', 'Hospitality': '#servicios', 'Cleaning': '#servicios', 'Maintenance': '#servicios', 'Sobre nosotros': '#beneficios', 'About us': '#beneficios', 'Portafolio': '#portafolio', 'Portfolio': '#portafolio', 'Resultados': '#kpis', 'Results': '#kpis', 'Proceso': '#proceso', 'Process': '#proceso' }; const cols = [ { t: ui.footer.cols[0].t, l: ui.footer.cols[0].l.map((x) => ({ text: x, href: navMap[x] || '#servicios' })) }, { t: ui.footer.cols[1].t, l: ui.footer.cols[1].l.map((x) => ({ text: x, href: navMap[x] || '#contacto' })) }, contactCol]; return ( ); } // ─── Sticky WhatsApp + Social ────────────────────── function StickyWA() { return ( e.currentTarget.style.transform = 'scale(1.08)'} onMouseLeave={(e) => e.currentTarget.style.transform = 'scale(1)'}> ); } // ─── Component (mounted by responsive-app or by Desktop v2.html mount call) ──── function DesktopApp() { const [lang, setLang] = useLang(); const { copy, ui } = getContent(lang); return ( <>