{"id":3486,"date":"2025-07-20T19:43:45","date_gmt":"2025-07-20T18:43:45","guid":{"rendered":"https:\/\/strattonapps.com\/?page_id=3486"},"modified":"2025-08-20T12:33:57","modified_gmt":"2025-08-20T11:33:57","slug":"chesterton","status":"publish","type":"page","link":"https:\/\/strattonapps.com\/es\/chesterton\/","title":{"rendered":"chesterton"},"content":{"rendered":"<div data-elementor-type=\"wp-page\" data-elementor-id=\"3486\" class=\"elementor elementor-3486\">\n\t\t\t\t\t\t<div class=\"elementor-inner\">\n\t\t\t\t<div class=\"elementor-section-wrap\">\n\t\t\t\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-b1162aa elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"b1162aa\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t\t\t<div class=\"elementor-row\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-485f973\" data-id=\"485f973\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-column-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t<div class=\"elementor-widget-wrap\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-a3ff7b8 elementor-widget elementor-widget-html\" data-id=\"a3ff7b8\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<!-- <script>\n(function() {\n  \/\/ <<< INICIO: Bloque para evitar reinicializaci\u00f3n >>>\n  if (window.floatingChatInitialized) {\n    console.warn('Floating Chat: Intento de reinicializaci\u00f3n, omitiendo.');\n    return;\n  }\n  window.floatingChatInitialized = true;\n  \/\/ <<< FIN: Bloque para evitar reinicializaci\u00f3n >>>\n\n  const WEBHOOK_URL = 'https:\/\/primary-production-dbbe.up.railway.app\/webhook\/chesterton';\n  const IDs = {\n    button: 'floating-chat-button',\n    window: 'floating-chat-window',\n    body: 'floating-chat-body',\n    input: 'floating-chat-input',\n    sendBtn: 'floating-chat-send',\n    typingIndicator: 'floating-chat-typing-indicator'\n  };\n  const CHAT_HISTORY_KEY = 'floatingChatHistory';\n\n  let chatBodyEl;\n\n  \/\/ --- Funciones de Ayuda (sin cambios) ---\n  function addMessageToUI(type, content, isHtml = false) {\n    if (!chatBodyEl) return;\n    const bubble = document.createElement('div');\n    bubble.className = type === 'user' ? 'user-bubble' : 'server-bubble';\n    if (isHtml && type === 'server') {\n      bubble.innerHTML = content;\n    } else {\n      bubble.textContent = content;\n    }\n    chatBodyEl.appendChild(bubble);\n    chatBodyEl.scrollTop = chatBodyEl.scrollHeight;\n  }\n\n  function saveMessageToHistory(type, content, isHtml = false) {\n    try {\n      const history = JSON.parse(localStorage.getItem(CHAT_HISTORY_KEY)) || [];\n      history.push({ type, content, isHtml, timestamp: new Date().toISOString() });\n      localStorage.setItem(CHAT_HISTORY_KEY, JSON.stringify(history));\n    } catch (e) {\n      console.error(\"Error guardando historial en localStorage:\", e);\n    }\n  }\n\n  function loadChatHistory() {\n    if (!chatBodyEl) return;\n    try {\n      const history = JSON.parse(localStorage.getItem(CHAT_HISTORY_KEY));\n      if (history && Array.isArray(history)) {\n        history.forEach(msg => addMessageToUI(msg.type, msg.content, msg.isHtml));\n      }\n    } catch (e) {\n      console.error(\"Error cargando historial desde localStorage:\", e);\n    }\n  }\n\n  function showTypingIndicator() {\n    if (!chatBodyEl || document.getElementById(IDs.typingIndicator)) return;\n    const indicatorBubble = document.createElement('div');\n    indicatorBubble.id = IDs.typingIndicator;\n    indicatorBubble.className = 'server-bubble typing-indicator';\n    indicatorBubble.innerHTML = `<span><\/span><span><\/span><span><\/span>`;\n    chatBodyEl.appendChild(indicatorBubble);\n    chatBodyEl.scrollTop = chatBodyEl.scrollHeight;\n  }\n\n  function hideTypingIndicator() {\n    const indicator = document.getElementById(IDs.typingIndicator);\n    if (indicator) indicator.remove();\n  }\n  \/\/ --- Fin Funciones de Ayuda ---\n\n\n  function init() {\n    if (document.getElementById(IDs.button)) return;\n\n    const style = document.createElement('style');\n    \/\/ --- INICIO DE ESTILOS ADAPTADOS ---\n    style.textContent = `\n      :root {\n        --chat-primary-color: #6C207E;\n        --chat-primary-hover: #873a9b;\n        --chat-font: \"FS Albert Pro\", sans-serif;\n        --chat-border-light: #e0e0e0;\n        --chat-border-form: #d1d1d1;\n        --chat-bg-light: #f9f9f9;\n        --chat-text-dark: #3e3e3e;\n      }\n      #${IDs.button}, #${IDs.window}, #${IDs.input}, #${IDs.sendBtn} {\n        font-family: var(--chat-font);\n      }\n      #${IDs.button} {\n        position: fixed; bottom: 20px; right: 20px; \/* Movido a la derecha *\/\n        left: auto;\n        width: 60px; height: 60px;\n        border-radius: 8px; \/* M\u00e1s cuadrado *\/\n        background: var(--chat-primary-color);\n        display: flex; align-items: center; justify-content: center;\n        border: none; cursor: pointer;\n        box-shadow: 0 5px 15px rgba(0,0,0,0.15);\n        transition: transform .3s, background-color .3s;\n        z-index: 10000;\n      }\n      #${IDs.button}:hover {\n        transform: scale(1.05);\n        background-color: var(--chat-primary-hover);\n      }\n      #${IDs.button} svg {\n        width: 40px; height: 40px; fill: #fff;\n      }\n\n      #${IDs.window} {\n        position: fixed; bottom: 90px; right: 20px; \/* Movido a la derecha *\/\n        left: auto;\n        width: 370px; max-height: 600px;\n        background: #fff;\n        border: 1px solid var(--chat-border-light);\n        border-radius: 8px;\n        box-shadow: 0 8px 24px rgba(0,0,0,0.1);\n        display: none; flex-direction: column;\n        overflow: hidden; z-index: 9999;\n      }\n      #${IDs.window} header {\n        padding: 16px 20px;\n        font-size: 1.1em;\n        font-weight: 700;\n        color: var(--chat-primary-color);\n        border-bottom: 1px solid var(--chat-border-light);\n        background: var(--chat-bg-light);\n      }\n      #${IDs.body} {\n        flex: 1; padding: 16px; overflow-y: auto; background: #fff;\n      }\n      #${IDs.body} .user-bubble {\n        background: var(--chat-primary-color);\n        color: #fff;\n        padding: 10px 14px;\n        border-radius: 12px 12px 2px 12px; \/* Estilo de burbuja *\/\n        max-width: 80%;\n        margin: 10px 0; float: right; clear: both;\n        font-size: 14px; line-height: 1.4; word-wrap: break-word;\n      }\n      #${IDs.body} .server-bubble {\n        background: #f0f0f0;\n        color: var(--chat-text-dark);\n        padding: 10px 14px;\n        border-radius: 12px 12px 12px 2px; \/* Estilo de burbuja *\/\n        max-width: 80%;\n        margin: 10px 0; float: left; clear: both;\n        font-size: 14px; line-height: 1.4; word-wrap: break-word;\n      }\n      #${IDs.body} .server-bubble a {\n        color: var(--chat-primary-color);\n        text-decoration: underline;\n        font-weight: 600;\n      }\n\n      .typing-indicator {\n        display: flex; align-items: center; padding: 12px 14px;\n      }\n      .typing-indicator span {\n        height: 8px; width: 8px;\n        background-color: #b0b0b0;\n        border-radius: 50%;\n        display: inline-block; margin: 0 2px;\n        animation: bounce 1.4s infinite ease-in-out both;\n      }\n      .typing-indicator span:nth-child(1) { animation-delay: -0.32s; }\n      .typing-indicator span:nth-child(2) { animation-delay: -0.16s; }\n      @keyframes bounce {\n        0%, 80%, 100% { transform: scale(0); }\n        40% { transform: scale(1.0); }\n      }\n\n      .chat-input-container {\n        display: flex; padding: 12px;\n        border-top: 1px solid var(--chat-border-light);\n        background: #fff;\n      }\n      #${IDs.input} {\n        flex: 1; padding: 10px 15px; font-size: 14px;\n        border: 1px solid var(--chat-border-form);\n        border-radius: 4px;\n        outline: none;\n        margin-right: 10px;\n        transition: border-color .2s;\n      }\n      #${IDs.input}:focus {\n        border-color: var(--chat-primary-color);\n      }\n      #${IDs.sendBtn} {\n        padding: 10px 20px;\n        background: var(--chat-primary-color);\n        color: #fff; border: none;\n        border-radius: 4px;\n        cursor: pointer;\n        font-weight: 600; font-size: 14px;\n        text-transform: uppercase;\n        transition: background .2s;\n      }\n      #${IDs.sendBtn}:hover {\n        background: var(--chat-primary-hover);\n      }\n\n      @media (max-width: 600px) {\n        #${IDs.button} {\n          width: 55px; height: 55px;\n          bottom: 16px; right: 16px;\n        }\n        #${IDs.button} svg {\n          width: 28px; height: 28px;\n        }\n        #${IDs.window} {\n          width: calc(100% - 32px);\n          height: 75vh;\n          max-height: none;\n          right: 16px;\n          bottom: 80px;\n          border-radius: 8px;\n        }\n      }\n    `;\n    \/\/ --- FIN DE ESTILOS ADAPTADOS ---\n    document.head.appendChild(style);\n\n    const btn = document.createElement('button');\n    btn.id = IDs.button;\n    btn.type = 'button';\n    btn.setAttribute('aria-label', 'Abrir chat');\n    \/\/ Icono SVG de chat m\u00e1s est\u00e1ndar\n    btn.innerHTML = '<svg viewBox=\"0 0 24 24\"><path d=\"M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z\"\/><\/svg>';\n    document.body.appendChild(btn);\n\n    const win = document.createElement('div');\n    win.id = IDs.window;\n    win.setAttribute('role', 'log');\n    win.setAttribute('aria-live', 'polite');\n    win.innerHTML = `\n      <header>Contacta con nosotros<\/header>\n      <div id=\"${IDs.body}\"><\/div>\n      <div class=\"chat-input-container\">\n        <input id=\"${IDs.input}\" type=\"text\" placeholder=\"Escribe tu mensaje...\" autocomplete=\"off\" aria-label=\"Mensaje a enviar\" \/>\n        <button id=\"${IDs.sendBtn}\" type=\"button\" aria-label=\"Enviar mensaje\">Enviar<\/button>\n      <\/div>\n    `;\n    document.body.appendChild(win);\n\n    chatBodyEl = document.getElementById(IDs.body);\n    const inputEl = document.getElementById(IDs.input);\n    const sendBtn = document.getElementById(IDs.sendBtn);\n\n    if (!chatBodyEl || !inputEl || !sendBtn) {\n        console.error('Chat init: Error al obtener elementos del DOM.');\n        return;\n    }\n\n    loadChatHistory();\n\n    btn.addEventListener('click', () => {\n      const isOpen = win.style.display === 'flex';\n      win.style.display = isOpen ? 'none' : 'flex';\n      btn.setAttribute('aria-label', isOpen ? 'Abrir chat' : 'Cerrar chat');\n      if (win.style.display === 'flex') {\n          if(inputEl) inputEl.focus();\n          if(chatBodyEl) chatBodyEl.scrollTop = chatBodyEl.scrollHeight;\n      }\n    });\n\n    async function sendMessage() {\n      const msg = inputEl.value.trim();\n      if (!msg) return;\n\n      addMessageToUI('user', msg);\n      saveMessageToHistory('user', msg);\n      inputEl.value = '';\n      inputEl.focus();\n\n      showTypingIndicator();\n\n      try {\n        const payload = { message: msg, timestamp: new Date().toISOString(), url: window.location.href };\n        const response = await fetch(WEBHOOK_URL, {\n          method: 'POST',\n          headers: { 'Content-Type': 'application\/json' },\n          body: JSON.stringify(payload)\n        });\n        \n        hideTypingIndicator();\n\n        if (!response.ok) {\n          const errorText = await response.text();\n          throw new Error(`Error del servidor: ${response.status}. Respuesta: ${errorText}`);\n        }\n\n        const contentType = response.headers.get(\"content-type\");\n        if (contentType && contentType.includes(\"application\/json\")) {\n            const data = await response.json();\n            if (data.responseMessage) {\n              addMessageToUI('server', data.responseMessage, true);\n              saveMessageToHistory('server', data.responseMessage, true);\n            } else {\n              const fallbackMsg = \"Respuesta inesperada del servidor.\";\n              addMessageToUI('server', fallbackMsg);\n              saveMessageToHistory('server', fallbackMsg);\n            }\n        } else {\n            const textData = await response.text();\n            addMessageToUI('server', textData);\n            saveMessageToHistory('server', textData);\n        }\n      } catch (err) {\n        console.error('Chat sendMessage: Error:', err);\n        hideTypingIndicator();\n        let errorMsg = \"Error al enviar mensaje. Intenta de nuevo.\";\n        if (err instanceof TypeError && err.message.toLowerCase().includes(\"failed to fetch\")) {\n            errorMsg = \"Error de red. No se pudo conectar con el servidor.\";\n        }\n        addMessageToUI('server', errorMsg);\n        saveMessageToHistory('server', errorMsg);\n      }\n    }\n\n    sendBtn.addEventListener('click', sendMessage);\n    inputEl.addEventListener('keydown', e => {\n      if (e.key === 'Enter' && !e.shiftKey) {\n        e.preventDefault();\n        sendMessage();\n      }\n    });\n  }\n\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', init);\n  } else {\n    init();\n  }\n})();\n<\/script> -->\n\n<script>\n  (function(d,t) {\n    var BASE_URL=\"https:\/\/chat.atomiun.racks.services\";\n    var g=d.createElement(t),s=d.getElementsByTagName(t)[0];\n    g.src=BASE_URL+\"\/packs\/js\/sdk.js\";\n    g.async = true;\n    s.parentNode.insertBefore(g,s);\n    g.onload=function(){\n      window.chatwootSDK.run({\n        websiteToken: '3oB9a9Qzp7MrNzhyFDx6C81H',\n        baseUrl: BASE_URL\n      })\n    }\n  })(document,\"script\");\n<\/script>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9398b7f elementor-widget elementor-widget-html\" data-id=\"9398b7f\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<!DOCTYPE html>\n<html lang=\"es\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Propiedades Inmobiliarias - Adaptado a Chestertons<\/title>\n    \n    <style>\n        \/* --- ESTILOS ADAPTADOS AL DISE\u00d1O DE CHESTERTONS-ATOMIUN.COM (v3) --- *\/\n\n        \/* Estilos Generales *\/\n        body { \n            font-family: \"FS Albert Pro\", sans-serif; \n            margin: 0; \n            padding: 20px; \n            background-color: #f9f9f9; \n            color: #3e3e3e; \n            -webkit-font-smoothing: antialiased;\n            -moz-osx-font-smoothing: grayscale;\n        }\n        body.lightbox-active { overflow: hidden; }\n        a { text-decoration: none; color: inherit; }\n        .container { max-width: 1200px; margin: auto; padding: 20px; }\n        h1 { \n            text-align: center; \n            color: #6C207E; \n            margin-bottom: 40px; \n            font-weight: 700;\n        }\n\n        \/* Filtros *\/\n        #filter-form { \n            display: flex; \n            flex-wrap: wrap; \n            gap: 20px; \n            margin-bottom: 40px; \n            background-color: #fff; \n            padding: 25px; \n            border-radius: 8px; \n            border: 1px solid #e0e0e0;\n            align-items: center; \n        }\n        #filter-form > * { flex: 1 1 180px; }\n        #filter-form input, #filter-form select { \n            padding: 12px 15px; \n            border: 1px solid #d1d1d1; \n            border-radius: 4px; \n            font-size: 0.95em;\n            font-family: \"FS Albert Pro\", sans-serif;\n            background-color: #fff;\n            color: #3e3e3e;\n            width: 100%;\n            box-sizing: border-box;\n        }\n        #filter-form .price-group, #filter-form .surface-group { display: flex; gap: 10px; }\n        \n        #filter-form .surface-filter-container {\n            display: flex;\n            flex-direction: column;\n            gap: 10px;\n        }\n\n        #filter-form button[type=\"reset\"] { \n            background-color: #6c757d;\n            color: white; \n            border: none; \n            padding: 12px 20px; \n            border-radius: 4px; \n            cursor: pointer; \n            font-weight: 600;\n            text-transform: uppercase;\n            font-family: \"FS Albert Pro\", sans-serif;\n            flex-grow: 0.5;\n            transition: background-color 0.3s ease;\n        }\n        #filter-form button[type=\"reset\"]:hover {\n            background-color: #5a6268;\n        }\n\n        #no-results-message { display: none; text-align: center; font-size: 1.2em; color: #7f8c8d; padding: 40px; }\n\n        \/* Tarjeta Clicable *\/\n        .card-link { display: block; }\n\n        \/* Rejilla y Tarjetas *\/\n        #properties-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 30px; }\n        .property-card { \n            background-color: #fff; \n            border-radius: 8px; \n            overflow: hidden; \n            border: 1px solid #e9e9e9;\n            box-shadow: 0 5px 15px rgba(0,0,0,0.05);\n            transition: transform 0.3s ease, box-shadow 0.3s ease; \n            display: flex; \n            flex-direction: column; \n            height: 100%; \n        }\n        .property-card:hover { \n            transform: translateY(-5px); \n            box-shadow: 0 10px 20px rgba(0,0,0,0.08); \n        }\n        .property-image-container { position: relative; width: 100%; aspect-ratio: 16 \/ 10; background-color: #f0f0f0; overflow: hidden; cursor: pointer; }\n        .gallery-wrapper, .gallery-arrow, .gallery-counter, .property-tags { cursor: auto; }\n\n        \/* Galer\u00edas en Tarjeta *\/\n        .gallery-wrapper { display: flex; height: 100%; transition: transform 0.4s ease-in-out; }\n        .gallery-wrapper img { width: 100%; height: 100%; object-fit: cover; flex-shrink: 0; }\n        .gallery-arrow { position: absolute; top: 50%; transform: translateY(-50%); background-color: rgba(0, 0, 0, 0.4); color: white; border: none; cursor: pointer; padding: 10px; border-radius: 50%; z-index: 2; opacity: 0; transition: opacity 0.3s ease; }\n        .property-image-container:hover .gallery-arrow { opacity: 1; }\n        .gallery-arrow.prev { left: 10px; }\n        .gallery-arrow.next { right: 10px; }\n        .gallery-arrow svg { display: block; width: 20px; height: 20px; fill: white; pointer-events: none; }\n        .gallery-counter { position: absolute; bottom: 10px; right: 10px; background-color: rgba(0, 0, 0, 0.6); color: white; padding: 4px 8px; border-radius: 4px; font-size: 0.8em; z-index: 2; }\n        \n        \/* Info de la Tarjeta *\/\n        .property-tags { position: absolute; top: 15px; left: 15px; z-index: 2; display: flex; gap: 8px; }\n        .tag { \n            color: white; \n            padding: 6px 12px; \n            border-radius: 1px;\n            font-size: 0.85em; \n            font-weight: 600; \n            text-transform: uppercase; \n        }\n        .tag.status-tag { background-color: #c0392b; }\n        .tag.tipo-venta { background-color: #6C207E; } \n        .tag.tipo-alquiler { background-color: #6c757d; }\n        \n        .property-info { padding: 25px; display: flex; flex-direction: column; flex-grow: 1; }\n        .property-price { \n            font-size: 1.8em; \n            font-weight: 700; \n            color: #6C207E; \n            margin-bottom: 10px; \n        }\n        .property-title { \n            font-size: 1.25em; \n            font-weight: 600; \n            color: #3e3e3e;\n            margin: 0 0 8px 0; \n            min-height: 44px;\n        }\n        .property-location { font-size: 1em; color: #7f8c8d; margin-bottom: 20px; }\n        .main-features { display: flex; flex-wrap: wrap; gap: 15px 20px; margin-bottom: 10px; border-top: 1px solid #f0f0f0; padding-top: 20px; }\n        .feature { display: flex; align-items: center; gap: 8px; font-size: 0.9em; color: #555; }\n        .feature svg { width: 20px; height: 20px; fill: #7f8c8d; }\n        \n        .extra-features-container { margin-top: 15px; padding-top: 15px; border-top: 1px solid #f0f0f0; border-bottom: 1px solid #f0f0f0; margin-bottom: 15px; padding-bottom: 15px;}\n        .extra-features-list { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; padding: 0; margin: 0; }\n        .extra-feature-item { background-color: #e9ecef; color: #495057; padding: 5px 10px; border-radius: 3px; font-size: 0.8em; }\n\n        \/* Bot\u00f3n de contacto adaptado *\/\n        .contact-button { \n            background-color: #6C207E; \n            color: white; \n            text-align: center; \n            padding: 10px 18px; \n            border-radius: 4px; \n            font-weight: 600;\n            font-size: 0.9em; \n            text-transform: uppercase;\n            font-family: \"FS Albert Pro\", sans-serif;\n            margin-top: auto; \/* <-- MODIFICADO: empuja el bot\u00f3n hacia abajo *\/\n            padding-top: 12px; \/* Alineaci\u00f3n vertical del texto *\/\n            padding-bottom: 12px;\n            cursor: pointer; \n            transition: background-color 0.3s ease;\n            align-self: center;\n            display: inline-block; \/* Necesario para que el <a> se comporte como bot\u00f3n *\/\n        }\n        .contact-button:hover { \n            background-color: #873a9b; \n            color: white;\n        }\n\n        \/* Loader *\/\n        #loader { text-align: center; font-size: 1.5em; padding: 50px; color: #6C207E; }\n\n        \/* Lightbox *\/\n        #lightbox-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.85); z-index: 1000; display: none; align-items: center; justify-content: center; }\n        #lightbox-content { position: relative; max-width: 90vw; max-height: 90vh; }\n        #lightbox-content img { display: block; max-width: 100%; max-height: 100%; object-fit: contain; }\n        .lightbox-arrow { position: absolute; top: 50%; transform: translateY(-50%); background-color: rgba(0, 0, 0, 0.5); color: white; border: none; cursor: pointer; padding: 15px; border-radius: 50%; z-index: 1001; }\n        .lightbox-arrow.prev { left: 20px; }\n        .lightbox-arrow.next { right: 20px; }\n        #lightbox-close { position: absolute; top: 20px; right: 20px; background: rgba(0, 0, 0, 0.5); color: white; border: none; cursor: pointer; padding: 10px; border-radius: 50%; font-size: 1.5em; line-height: 1; z-index: 1002; }\n\n\n        \/* --- INICIO DE ESTILOS RESPONSIVE --- *\/\n        @media (max-width: 1023px) {\n            #properties-grid {\n                grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n            }\n        }\n        \n        @media (max-width: 767px) {\n            .container { padding: 15px; }\n            h1 { font-size: 1.8rem; margin-bottom: 25px; }\n            #properties-grid {\n                grid-template-columns: 1fr;\n                gap: 25px;\n            }\n            #filter-form {\n                flex-direction: column;\n                align-items: stretch;\n                gap: 15px;\n                padding: 20px;\n            }\n            #filter-form > * { flex: 1 1 100%; }\n            #filter-form .price-group,\n            #filter-form .surface-group {\n                display: grid;\n                grid-template-columns: 1fr 1fr;\n                gap: 10px;\n            }\n            .property-title { min-height: auto; }\n        }\n    <\/style>\n<\/head>\n<body>\n    <div class=\"container\">\n        <h1>Nuestras Propiedades<\/h1>\n        <form id=\"filter-form\" action=\"\">\n            <input type=\"search\" id=\"search-input\" placeholder=\"Buscar por t\u00edtulo, ubicaci\u00f3n, tipo...\">\n            <select id=\"filter-type\"><option value=\"\">Cualquier operaci\u00f3n<\/option><option value=\"venta\">Venta<\/option><option value=\"alquiler\">Alquiler<\/option><\/select>\n            <div class=\"price-group\"><input type=\"number\" id=\"min-price\" placeholder=\"Precio M\u00edn. (\u20ac)\"><input type=\"number\" id=\"max-price\" placeholder=\"Precio M\u00e1x. (\u20ac)\"><\/div>\n            \n            <div class=\"surface-filter-container\">\n                <select id=\"filter-surface-type\">\n                    <option value=\"metros_construidos\">Metros Construidos<\/option>\n                    <option value=\"metros_utiles\">Metros \u00datiles<\/option>\n                    <option value=\"metros_edificables\">Metros Edificables<\/option>\n                    <option value=\"metros_oficinas\">Metros de Oficinas<\/option>\n                    <option value=\"metros_parcela\">Metros de Parcela<\/option>\n                <\/select>\n                <div class=\"surface-group\">\n                    <input type=\"number\" id=\"min-surface\" placeholder=\"Superficie M\u00edn. (m\u00b2)\">\n                    <input type=\"number\" id=\"max-surface\" placeholder=\"Superficie M\u00e1x. (m\u00b2)\">\n                <\/div>\n            <\/div>\n\n            <button type=\"reset\">Limpiar<\/button>\n        <input type=\"hidden\" name=\"trp-form-language\" value=\"es\"\/><\/form>\n        <div id=\"loader\">Cargando propiedades...<\/div>\n        <div id=\"properties-grid\"><\/div>\n        <p id=\"no-results-message\">No se han encontrado propiedades que coincidan con su b\u00fasqueda.<\/p>\n    <\/div>\n\n    <div id=\"lightbox-overlay\">\n        <button id=\"lightbox-close\">\u00d7<\/button>\n        <button class=\"lightbox-arrow prev\"><<\/button>\n        <div id=\"lightbox-content\"><\/div>\n        <button class=\"lightbox-arrow next\">><\/button>\n    <\/div>\n\n<script>\n    document.addEventListener('DOMContentLoaded', () => {\n        const dynamicUrl = 'https:\/\/strattonapps.com\/wp-json\/mi-proxy\/v1\/propiedades';\n        let allProperties = [];\n        let currentFilters = {};\n        let lightboxPhotos = [];\n        let lightboxIndex = 0;\n\n        const grid = document.getElementById('properties-grid');\n        const loader = document.getElementById('loader');\n        const noResultsMsg = document.getElementById('no-results-message');\n        const filterForm = document.getElementById('filter-form');\n        const searchInput = document.getElementById('search-input');\n        const typeFilter = document.getElementById('filter-type');\n        const minPriceInput = document.getElementById('min-price');\n        const maxPriceInput = document.getElementById('max-price');\n        \n        const surfaceTypeFilter = document.getElementById('filter-surface-type');\n\n        const minSurfaceInput = document.getElementById('min-surface');\n        const maxSurfaceInput = document.getElementById('max-surface');\n        const lightboxOverlay = document.getElementById('lightbox-overlay');\n        const lightboxContent = document.getElementById('lightbox-content');\n        const lightboxClose = document.getElementById('lightbox-close');\n        const lightboxPrev = lightboxOverlay.querySelector('.prev');\n        const lightboxNext = lightboxOverlay.querySelector('.next');\n\n\n        async function initialize() {\n            try {\n                const response = await fetch(dynamicUrl);\n                if (!response.ok) throw new Error(`Error en la red: ${response.status} ${response.statusText}`);\n                \n                const responseData = await response.json();\n                if (!responseData.success || !responseData.xml) {\n                    throw new Error(\"La respuesta del proxy no contiene el XML esperado.\");\n                }\n\n                const xmlText = responseData.xml;\n                const parser = new DOMParser();\n                const xmlDoc = parser.parseFromString(xmlText, \"application\/xml\");\n\n                if (xmlDoc.querySelector(\"parsererror\")) {\n                    throw new Error(\"Error al procesar el XML. El contenido no es v\u00e1lido.\");\n                }\n\n                const binaryFeaturesMap = { MuelleCarga: \"Muelles de carga\", Vado: \"Vado\", SistemaAntiincendios: \"Sistema Anti-incendios\", SalidaHumos: \"Salida de Humos\", Asfaltado: \"Asfaltado\", Vallado: \"Vallado\", Urbanizado: \"Urbanizado\", Alarma: \"Alarma\", Ascensor: \"Ascensor\", Montacargas: \"Montacargas\", PuenteGrua: \"Puente Gr\u00faa\" };\n\n                allProperties = Array.from(xmlDoc.querySelectorAll('Inmueble')).map(inmueble => {\n                    const getText = (selector) => inmueble.querySelector(selector)?.textContent.trim() || '';\n                    const getFloat = (selector) => parseFloat(getText(selector)) || 0;\n                    const operationNode = inmueble.querySelector('Operaciones > Operacion');\n                    const collectedBinaryFeatures = Object.entries(binaryFeaturesMap).filter(([key]) => getText(key) === '1').map(([, value]) => value);\n\n                    return {\n                        ref: getText('Referencia'),\n                        url: getText('UrlPublica') ? `https:${getText('UrlPublica')}` : '#',\n                        transactionType: operationNode ? operationNode.querySelector('Tipo')?.textContent.trim().toLowerCase() : '',\n                        price: operationNode ? parseFloat(operationNode.querySelector('Precio')?.textContent.trim()) || 0 : 0,\n                        propertyType: getText('Tipo'),\n                        title: getText('Titulo'),\n                        location: getText('Poblacion'),\n                        status: getText('Estado'),\n                        metros_construidos: getFloat('MetrosConstruidos'),\n                        metros_utiles: getFloat('MetrosUtiles'),\n                        metros_edificables: getFloat('MetrosEdificables'),\n                        metros_oficinas: getFloat('MetrosOficinas'),\n                        metros_parcela: getFloat('MetrosParcela'),\n                        altura_techo: getFloat('AlturaTecho'),\n                        ano_construccion: getFloat('AnoConstruccion'),\n                        rooms: getFloat('Habitaciones'),\n                        bathrooms: getFloat('Banos') + getFloat('Aseos'),\n                        altura: getFloat('AlturaTecho'),\n                        ano: getFloat('AnoConstruccion'),\n                        agentEmail: getText('EmailAgente'),\n                        fotos: Array.from(inmueble.querySelectorAll('Foto')).map(f => f.textContent.trim()).filter(url => url),\n                        binaryFeatures: collectedBinaryFeatures,\n                    };\n                }).filter(prop => prop.ref && prop.transactionType);\n\n                renderProperties(allProperties);\n\n            } catch (error) {\n                console.error(\"Error al inicializar las propiedades:\", error);\n                grid.innerHTML = '<p>Ha ocurrido un error al cargar las propiedades. Por favor, int\u00e9ntelo m\u00e1s tarde.<\/p>';\n            } finally {\n                loader.style.display = 'none';\n            }\n        }\n\nfunction renderProperties(propertiesToRender) {\n            grid.innerHTML = '';\n            noResultsMsg.style.display = propertiesToRender.length === 0 ? 'block' : 'none';\n\n            let htmlContent = '';\n            propertiesToRender.forEach(prop => {\n                const formattedPrice = new Intl.NumberFormat('es-ES', { style: 'currency', currency: 'EUR', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(prop.price);\n                const imagesHtml = prop.fotos.length > 0 ? prop.fotos.map(foto => `<img decoding=\"async\" src=\"${foto}\" alt=\"${prop.title}\" loading=\"lazy\">`).join('') : `<img decoding=\"async\" src=\"https:\/\/strattonapps.com\/wp-content\/uploads\/2025\/07\/elementor-placeholder-image.webp\" alt=\"Sin imagen\">`;\n                const binaryFeaturesHtml = prop.binaryFeatures.map(feat => `<li class=\"extra-feature-item\">${feat}<\/li>`).join('');\n\n                htmlContent += `\n                    <a href=\"${prop.url}\" target=\"_blank\" class=\"card-link\">\n                        <div class=\"property-card\" data-ref=\"${prop.ref}\">\n                            <div class=\"property-image-container\" data-total-images=\"${prop.fotos.length}\">\n                                <div class=\"gallery-wrapper\" data-current-index=\"0\">${imagesHtml}<\/div>\n                                <div class=\"property-tags\">\n                                    ${prop.transactionType ? `<div class=\"tag tipo-${prop.transactionType}\">${prop.transactionType}<\/div>` : ''}\n                                    ${prop.status ? `<div class=\"tag status-tag\">${prop.status}<\/div>` : ''}\n                                <\/div>\n                                ${prop.fotos.length > 1 ? `\n                                    <button class=\"gallery-arrow prev\" title=\"Anterior\"><svg viewBox=\"0 0 24 24\"><path d=\"M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z\"\/><\/svg><\/button>\n                                    <button class=\"gallery-arrow next\" title=\"Siguiente\"><svg viewBox=\"0 0 24 24\"><path d=\"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z\"\/><\/svg><\/button>\n                                    <div class=\"gallery-counter\">1 \/ ${prop.fotos.length}<\/div>\n                                ` : ''}\n                            <\/div>\n                            <div class=\"property-info\">\n                                <div class=\"property-price\">${prop.price > 0 ? formattedPrice : 'Consultar'}<\/div>\n                                <h3 class=\"property-title\">${prop.title}<\/h3>\n                                <p class=\"property-location\">${prop.propertyType} en ${prop.location}<\/p>\n                                \n                                <div class=\"main-features\">\n                                    ${prop.rooms > 0 ? `<div class=\"feature\"><span>${prop.rooms} hab.<\/span><\/div>` : ''}\n                                    ${prop.bathrooms > 0 ? `<div class=\"feature\"><span>${prop.bathrooms} ba\u00f1os<\/span><\/div>` : ''}\n                                    ${prop.metros_construidos > 0 ? `<div class=\"feature\"><span>${prop.metros_construidos} m\u00b2 constr.<\/span><\/div>` : ''}\n                                    ${prop.metros_utiles > 0 ? `<div class=\"feature\"><span>${prop.metros_utiles} m\u00b2 \u00fatiles<\/span><\/div>` : ''}\n                                    ${prop.metros_edificables > 0 ? `<div class=\"feature\"><span>${prop.metros_edificables} m\u00b2 edif.<\/span><\/div>` : ''}\n                                    ${prop.metros_oficinas > 0 ? `<div class=\"feature\"><span>${prop.metros_oficinas} m\u00b2 ofic.<\/span><\/div>` : ''}\n                                    ${prop.metros_parcela > 0 ? `<div class=\"feature\"><span>${prop.metros_parcela} m\u00b2 parc.<\/span><\/div>` : ''}\n                                <\/div>\n\n                                ${prop.binaryFeatures.length > 0 ? `<div class=\"extra-features-container\"><ul class=\"extra-features-list\">${binaryFeaturesHtml}<\/ul><\/div>` : ''}\n                                \n                                <!-- --- INICIO DE LA MODIFICACI\u00d3N: El bot\u00f3n ahora es un <button> --- -->\n                                ${prop.agentEmail ? `<button data-mailto=\"${prop.agentEmail}\" class=\"contact-button\">Contactar<\/button>` : ''}\n                                <!-- --- FIN DE LA MODIFICACI\u00d3N --- -->\n                            <\/div>\n                        <\/div>\n                    <\/a>`;\n            });\n            grid.innerHTML = htmlContent;\n        }\n \n\n        function applyFilters() {\n            const searchTerm = searchInput.value.toLowerCase();\n            const typeValue = typeFilter.value;\n            const minPrice = parseFloat(minPriceInput.value) || 0;\n            const maxPrice = parseFloat(maxPriceInput.value) || Infinity;\n            \n            const selectedSurfaceType = surfaceTypeFilter.value;\n            const minSurface = parseFloat(minSurfaceInput.value) || 0;\n            const maxSurface = parseFloat(maxSurfaceInput.value) || Infinity;\n\n            const filtered = allProperties.filter(prop => {\n                const matchesSearch = searchTerm === '' || prop.title.toLowerCase().includes(searchTerm) || prop.location.toLowerCase().includes(searchTerm) || prop.propertyType.toLowerCase().includes(searchTerm);\n                const matchesType = typeValue === '' || prop.transactionType === typeValue;\n                const matchesPrice = prop.price >= minPrice && prop.price <= maxPrice;\n\n                const propertySurface = prop[selectedSurfaceType] || 0;\n                const matchesSurface = propertySurface >= minSurface && propertySurface <= maxSurface;\n\n                return matchesSearch && matchesType && matchesPrice && matchesSurface;\n            });\n            renderProperties(filtered);\n        }\n        \n        function handleCardGallery(arrow) {\n            const container = arrow.closest('.property-image-container');\n            const wrapper = container.querySelector('.gallery-wrapper');\n            const counter = container.querySelector('.gallery-counter');\n            const totalImages = parseInt(container.dataset.totalImages, 10);\n            let currentIndex = parseInt(wrapper.dataset.currentIndex, 10);\n\n            if (arrow.classList.contains('next')) {\n                currentIndex = (currentIndex + 1) % totalImages;\n            } else {\n                currentIndex = (currentIndex - 1 + totalImages) % totalImages;\n            }\n            \n            wrapper.style.transform = `translateX(-${currentIndex * 100}%)`;\n            wrapper.dataset.currentIndex = currentIndex;\n            counter.textContent = `${currentIndex + 1} \/ ${totalImages}`;\n        }\n\n        function openLightbox(propertyRef, startIndex = 0) {\n            const property = allProperties.find(p => p.ref === propertyRef);\n            if (!property || property.fotos.length === 0) return;\n\n            lightboxPhotos = property.fotos;\n            lightboxIndex = startIndex;\n            document.body.classList.add('lightbox-active');\n            lightboxOverlay.style.display = 'flex';\n            updateLightboxImage();\n        }\n\n        function closeLightbox() {\n            document.body.classList.remove('lightbox-active');\n            lightboxOverlay.style.display = 'none';\n            lightboxContent.innerHTML = '';\n        }\n\n        function updateLightboxImage() {\n            lightboxContent.innerHTML = `<img decoding=\"async\" src=\"${lightboxPhotos[lightboxIndex]}\" alt=\"Imagen ${lightboxIndex + 1}\">`;\n        }\n\n        function showNextImage() {\n            lightboxIndex = (lightboxIndex + 1) % lightboxPhotos.length;\n            updateLightboxImage();\n        }\n\n        function showPrevImage() {\n            lightboxIndex = (lightboxIndex - 1 + lightboxPhotos.length) % lightboxPhotos.length;\n            updateLightboxImage();\n        }\n\n        filterForm.addEventListener('input', applyFilters);\n        filterForm.addEventListener('reset', () => {\n            setTimeout(() => applyFilters(), 0);\n        });\n\n        grid.addEventListener('click', (event) => {\n            const arrow = event.target.closest('.gallery-arrow');\n            const contactBtn = event.target.closest('.contact-button');\n            const imageContainer = event.target.closest('.property-image-container');\n\n            \/\/ Si se hace clic en el bot\u00f3n de contacto\n            if (contactBtn) {\n                \/\/ Prevenimos la acci\u00f3n por defecto (navegar) del enlace <a> padre\n                event.preventDefault();\n                event.stopPropagation();\n                \/\/ Abrimos el cliente de correo\n                window.location.href = `mailto:${contactBtn.dataset.mailto}`;\n                return;\n            }\n\n            \/\/ Si se pulsa una flecha de la galer\u00eda, se previene el enlace de la tarjeta y se mueve la galer\u00eda.\n            if (arrow) {\n                event.preventDefault();\n                event.stopPropagation();\n                handleCardGallery(arrow);\n                return;\n            }\n\n            \/\/ Si se pulsa en la imagen, se previene el enlace y se abre el visor de im\u00e1genes (lightbox).\n            if (imageContainer) {\n                event.preventDefault();\n                event.stopPropagation();\n                const ref = imageContainer.closest('.property-card').dataset.ref;\n                const galleryWrapper = imageContainer.querySelector('.gallery-wrapper');\n                const startIndex = galleryWrapper ? parseInt(galleryWrapper.dataset.currentIndex, 10) : 0;\n                openLightbox(ref, startIndex);\n                return;\n            }\n        });\n\n\n        lightboxClose.addEventListener('click', closeLightbox);\n        lightboxNext.addEventListener('click', showNextImage);\n        lightboxPrev.addEventListener('click', showPrevImage);\n        lightboxOverlay.addEventListener('click', (e) => {\n            if (e.target === lightboxOverlay) closeLightbox();\n        });\n        document.addEventListener('keydown', (e) => {\n            if (lightboxOverlay.style.display === 'flex') {\n                if (e.key === 'Escape') closeLightbox();\n                if (e.key === 'ArrowRight') showNextImage();\n                if (e.key === 'ArrowLeft') showPrevImage();\n            }\n        });\n\n        initialize();\n    });\n<\/script>\n\n<\/body>\n<\/html>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t<\/div>\n\t\t\t\t\t<\/div>","protected":false},"excerpt":{"rendered":"<p>Propiedades Inmobiliarias &#8211; Adaptado a Chestertons Nuestras Propiedades Cualquier operaci\u00f3nVentaAlquiler Metros ConstruidosMetros \u00datilesMetros EdificablesMetros de OficinasMetros de Parcela Limpiar Cargando propiedades&#8230; No se han encontrado propiedades que coincidan con su b\u00fasqueda. \u00d7<\/p>","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":[],"_links":{"self":[{"href":"https:\/\/strattonapps.com\/es\/wp-json\/wp\/v2\/pages\/3486"}],"collection":[{"href":"https:\/\/strattonapps.com\/es\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/strattonapps.com\/es\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/strattonapps.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/strattonapps.com\/es\/wp-json\/wp\/v2\/comments?post=3486"}],"version-history":[{"count":247,"href":"https:\/\/strattonapps.com\/es\/wp-json\/wp\/v2\/pages\/3486\/revisions"}],"predecessor-version":[{"id":3847,"href":"https:\/\/strattonapps.com\/es\/wp-json\/wp\/v2\/pages\/3486\/revisions\/3847"}],"wp:attachment":[{"href":"https:\/\/strattonapps.com\/es\/wp-json\/wp\/v2\/media?parent=3486"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}