{"id":8,"date":"2026-08-25T12:32:57","date_gmt":"2026-08-25T12:32:57","guid":{"rendered":"https:\/\/mcpbim.com\/?page_id=8"},"modified":"2026-09-18T13:18:19","modified_gmt":"2026-09-18T13:18:19","slug":"edibim","status":"publish","type":"page","link":"https:\/\/mcpbim.com\/","title":{"rendered":"EdiBIM"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"8\" class=\"elementor elementor-8\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-88e7f62 e-grid editeca-live-plan e-con-full e-con e-parent\" data-id=\"88e7f62\" data-element_type=\"container\" data-e-type=\"container\" id=\"mcpbim\">\n\t\t<div class=\"elementor-element elementor-element-fea9ac7 e-con e-atomic-element e-flexbox-base e-fea9ac7-ce63dd6 edibim-hero-contenido \" data-id=\"fea9ac7\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"fea9ac7\">\n    \t\t<div class=\"elementor-element elementor-element-3a4cb36 edibim-hero-titulo elementor-widget elementor-widget-text-editor\" data-id=\"3a4cb36\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h1><strong data-pm-slice=\"0 0 []\">EDIBIM PARA REVIT<\/strong><\/h1>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-c3d2a46 edibim-hero-copy elementor-widget elementor-widget-text-editor\" data-id=\"c3d2a46\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p class=\"isSelectedEnd\">Convierte instrucciones en acciones reales dentro de tu modelo: consulta, crea, modifica y documenta con inteligencia artificial.<\/p><p><strong>T\u00fa decides qu\u00e9 hacer. EdiBIM lo ejecuta. T\u00fa compruebas el resultado.<\/strong><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-0b6e52f e-con e-atomic-element e-div-block-base e-0b6e52f-08eb8c2 edibim-hero-botones \" data-id=\"0b6e52f\" data-element_type=\"e-div-block\" data-e-type=\"e-div-block\" data-interaction-id=\"0b6e52f\">\n    \t\t\t<a href=\"#accion-edibim\" target=\"_self\" class=\"e-a27703c-1941b99 g-b4b6b19 e-button-base\" data-interaction-id=\"a27703c\">\n\t\tVER EDIBIM EN ACCI\u00d3N\n\t<\/a>\t\t\t\t\t<a href=\"#edibim-acesso\" target=\"_self\" class=\"e-2044749-90da56a e-button-base\" data-interaction-id=\"2044749\">\n\t\tACCEDER A EDIBIM\n\t<\/a>\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-5804715 e-con e-atomic-element e-div-block-base \" data-id=\"5804715\" data-element_type=\"e-div-block\" data-e-type=\"e-div-block\" data-interaction-id=\"5804715\">\n    \n<\/div>\n<div class=\"elementor-element elementor-element-aaae4f6 e-con e-atomic-element e-div-block-base \" data-id=\"aaae4f6\" data-element_type=\"e-div-block\" data-e-type=\"e-div-block\" data-interaction-id=\"aaae4f6\">\n    \n<\/div>\n\n<\/div>\n<div class=\"elementor-element elementor-element-d40d44b e-con e-atomic-element e-flexbox-base e-d40d44b-c104855 edibim-hero-claim \" data-id=\"d40d44b\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"d40d44b\">\n    \t\t<div class=\"elementor-element elementor-element-92c9756 elementor-widget__width-inherit elementor-widget elementor-widget-text-editor\" data-id=\"92c9756\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h1><strong>TU CRITERIO BIM.<\/strong><\/h1><h1><strong>EDIBIM LO EJECUTA EN REVIT.<\/strong><\/h1>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-cfac7f6 elementor-widget elementor-widget-html\" data-id=\"cfac7f6\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<style>\r\n\r\n\/* TU CRITERIO BIM *\/\r\n.elementor-element-92c9756{\r\n    position: relative !important;\r\n    z-index: 10 !important;\r\n}\r\n\r\n\/* Desktop grande *\/\r\n.elementor-element-92c9756 h1{\r\n    font-size: clamp(32px, 3vw, 58px) !important;\r\n    line-height: 1.05 !important;\r\n    margin: 0 !important;\r\n}\r\n\r\n\/* Port\u00e1til \/ pantalla media *\/\r\n@media (max-width: 1400px){\r\n\r\n    .elementor-element-92c9756{\r\n        transform: translateY(-15px) !important;\r\n    }\r\n\r\n    .elementor-element-92c9756 h1{\r\n        font-size: clamp(28px, 3vw, 46px) !important;\r\n    }\r\n}\r\n\r\n\/* Pantalla m\u00e1s estrecha *\/\r\n@media (max-width: 1100px){\r\n\r\n    .elementor-element-92c9756{\r\n        transform: translateY(-30px) !important;\r\n    }\r\n\r\n    .elementor-element-92c9756 h1{\r\n        font-size: clamp(24px, 3.2vw, 38px) !important;\r\n    }\r\n}\r\n\r\n\/* Tablet *\/\r\n@media (max-width: 900px){\r\n\r\n    .elementor-element-92c9756{\r\n        transform: translateY(-45px) !important;\r\n    }\r\n\r\n    .elementor-element-92c9756 h1{\r\n        font-size: clamp(22px, 4vw, 32px) !important;\r\n    }\r\n}\r\n\r\n\/* M\u00f3vil *\/\r\n@media (max-width: 767px){\r\n\r\n    .elementor-element-92c9756{\r\n        transform: translateY(-20px) !important;\r\n    }\r\n\r\n    .elementor-element-92c9756 h1{\r\n        font-size: clamp(20px, 7vw, 28px) !important;\r\n        line-height: 1.1 !important;\r\n    }\r\n}\r\n\r\n<\/style>\t\t\t\t<\/div>\n\t\t\n<\/div>\n\t\t<div class=\"elementor-element elementor-element-8200093 elementor-widget elementor-widget-html\" data-id=\"8200093\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<style>\n\/* ============================================================\n   EDITECA \u00b7 PLANO ARQUITECT\u00d3NICO INTERACTIVO + MOBILIARIO\n   Versi\u00f3n de un solo plano centrado\n   ============================================================ *\/\n\n.editeca-live-plan{\n    position:relative !important;\n    overflow:hidden !important;\n    background:#201716 !important;\n    isolation:isolate;\n}\n\n#editeca-live-plan-svg{\n    position:absolute !important;\n    inset:0 !important;\n    width:100% !important;\n    height:100% !important;\n    z-index:0 !important;\n    pointer-events:none !important;\n}\n\n#editeca-live-plan-svg svg{\n    display:block;\n    width:100%;\n    height:100%;\n    overflow:hidden;\n}\n\n\/* Contenido Elementor por encima *\/\n.editeca-live-plan > *:not(#editeca-live-plan-svg):not(#editeca-global-cursor){\n    \n    z-index:5 !important;\n}\n\n\/* Habitaciones *\/\n.elp-room{\n    fill:#b6c8bd;\n    fill-opacity:.016;\n    transition:fill-opacity .25s ease;\n}\n\n.elp-room.active{\n    fill-opacity:.085;\n}\n\n\/* Muros *\/\n.elp-outer-wall{\n    fill:none;\n    stroke:#cedbd2;\n    stroke-width:7;\n    stroke-opacity:.72;\n    stroke-linejoin:miter;\n    stroke-linecap:square;\n    vector-effect:non-scaling-stroke;\n}\n\n.elp-wall{\n    fill:none;\n    stroke:#baccc0;\n    stroke-width:3;\n    stroke-opacity:.58;\n    stroke-linecap:square;\n    vector-effect:non-scaling-stroke;\n}\n\n.elp-wall.active{\n    stroke:#e4eee7;\n    stroke-opacity:.95;\n    stroke-width:3.6;\n}\n\n\/* Puertas *\/\n.elp-door{\n    fill:none;\n    stroke:#aebfb5;\n    stroke-width:1.25;\n    stroke-opacity:.48;\n    vector-effect:non-scaling-stroke;\n}\n\n\/* Ventanas *\/\n.elp-window{\n    stroke:#d3dfd6;\n    stroke-width:1.25;\n    stroke-opacity:.52;\n    vector-effect:non-scaling-stroke;\n}\n\n\/* Nodos *\/\n.elp-node{\n    fill:#cfddd3;\n    opacity:.16;\n}\n\n\/* ============================================================\n   MOBILIARIO CENITAL\n   ============================================================ *\/\n\n.elp-furniture{\n    fill:none;\n    stroke:#c7d6cc;\n    stroke-width:1.35;\n    stroke-opacity:.45;\n    vector-effect:non-scaling-stroke;\n    transition:stroke-opacity .18s ease, fill-opacity .18s ease;\n}\n\n.elp-furniture-soft{\n    fill:#b8cabe;\n    fill-opacity:.018;\n    stroke:#c7d6cc;\n    stroke-width:1.05;\n    stroke-opacity:.34;\n    vector-effect:non-scaling-stroke;\n}\n\n.elp-furniture-detail{\n    fill:none;\n    stroke:#c7d6cc;\n    stroke-width:.9;\n    stroke-opacity:.34;\n    vector-effect:non-scaling-stroke;\n}\n\n.elp-furniture-fill{\n    fill:#c7d6cc;\n    fill-opacity:.022;\n    stroke:#c7d6cc;\n    stroke-width:1;\n    stroke-opacity:.38;\n    vector-effect:non-scaling-stroke;\n}\n\n.elp-furniture-accent{\n    fill:none;\n    stroke:#d9e5dd;\n    stroke-width:.85;\n    stroke-opacity:.42;\n    vector-effect:non-scaling-stroke;\n}\n\n.elp-furniture-room.active .elp-furniture,\n.elp-furniture-room.active .elp-furniture-soft{\n    stroke-opacity:.92;\n}\n\n.elp-furniture-room.active .elp-furniture-soft{\n    fill-opacity:.045;\n}\n\n.elp-furniture-room.active .elp-furniture-detail{\n    stroke-opacity:.62;\n}\n\n\/*\n * Las recolocaciones por colisi\u00f3n se hacen en SVG,\n * pero mantenemos un cambio visual limpio y discreto.\n *\/\n.elp-furniture-room > *{\n    transform-box:fill-box;\n    transform-origin:center;\n}\n\n\/* Cursor local *\/\n.elp-pointer{\n    fill:#e3ede6;\n    opacity:0;\n}\n\n.elp-pointer-ring{\n    fill:none;\n    stroke:#d7e4da;\n    stroke-width:1;\n    opacity:0;\n    vector-effect:non-scaling-stroke;\n}\n\n.elp-pointer-glow{\n    fill:url(#elpPointerGlow);\n    opacity:0;\n}\n\n\/* Cursor global *\/\n#editeca-global-cursor{\n    position:fixed;\n    left:0;\n    top:0;\n    width:7.6px;\n    height:7.6px;\n    border-radius:50%;\n    background:#e3ede6;\n    pointer-events:none;\n    z-index:999;\n    opacity:0;\n    transform:translate(-50%,-50%);\n    will-change:left,top;\n    transition:opacity .18s ease;\n}\n\n#editeca-global-cursor::before{\n    content:\"\";\n    position:absolute;\n    left:50%;\n    top:50%;\n    width:24px;\n    height:24px;\n    border-radius:50%;\n    border:1px solid rgba(215,228,218,.40);\n    transform:translate(-50%,-50%);\n    animation:elpCursorPulse 2s ease-in-out infinite;\n}\n\n#editeca-global-cursor::after{\n    content:\"\";\n    position:absolute;\n    left:50%;\n    top:50%;\n    width:55px;\n    height:55px;\n    border-radius:50%;\n    transform:translate(-50%,-50%);\n    background:radial-gradient(\n        circle,\n        rgba(227,238,230,.20) 0%,\n        rgba(203,220,207,.07) 40%,\n        rgba(203,220,207,0) 72%\n    );\n}\n\n@keyframes elpCursorPulse{\n    0%,100%{\n        transform:translate(-50%,-50%) scale(.90);\n        opacity:.35;\n    }\n    50%{\n        transform:translate(-50%,-50%) scale(1.12);\n        opacity:.65;\n    }\n}\n\n@media (hover:none){\n    #editeca-global-cursor{\n        display:none;\n    }\n}\n<\/style>\n\n<script>\n(function(){\n\nfunction iniciarPlanoEditeca(){\n\n    const section = document.querySelector('.editeca-live-plan');\n\n    if(!section){\n        console.warn('No encuentro .editeca-live-plan');\n        return;\n    }\n\n    section.querySelectorAll(\n        '#editeca-live-plan-svg'\n    ).forEach(el => el.remove());\n\n    \/* ========================================================\n       SVG \u00b7 SOLO UN PLANO Y CENTRADO\n       ======================================================== *\/\n\n    const layer = document.createElement('div');\n    layer.id = 'editeca-live-plan-svg';\n\n    layer.innerHTML = `\n    <svg\n        viewBox=\"0 0 1200 900\"\n        preserveAspectRatio=\"xMidYMid meet\"\n        xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n        aria-hidden=\"true\"\n    >\n        <defs>\n            <radialGradient id=\"elpPointerGlow\">\n                <stop offset=\"0%\" stop-color=\"#e3eee6\" stop-opacity=\".28\"\/>\n                <stop offset=\"45%\" stop-color=\"#cbdccf\" stop-opacity=\".08\"\/>\n                <stop offset=\"100%\" stop-color=\"#cbdccf\" stop-opacity=\"0\"\/>\n            <\/radialGradient>\n        <\/defs>\n\n        <!--\n            PLAN_CENTER = se encarga de centrar\/mover el plano\n            PLAN_A      = se encarga \u00fanicamente de la escala\n\n            NO CAMBIES transform aqu\u00ed.\n            Para mover\/escalar usa PLAN_VIEW en el JavaScript.\n        -->\n        <g id=\"PLAN_CENTER\">\n            <g id=\"PLAN_A\">\n                <g id=\"elpRoomsA\"><\/g>\n            <g id=\"elpFurnitureA\"><\/g>\n            <g id=\"elpWallsA\"><\/g>\n            <g id=\"elpDoorsA\"><\/g>\n            <g id=\"elpWindowsA\"><\/g>\n            <g id=\"elpNodesA\"><\/g>\n\n                <circle id=\"elpPointerGlowA\" class=\"elp-pointer-glow\" r=\"55\"\/>\n                <circle id=\"elpPointerRingA\" class=\"elp-pointer-ring\" r=\"12\"\/>\n                <circle id=\"elpPointerA\" class=\"elp-pointer\" r=\"3.8\"\/>\n            <\/g>\n        <\/g>\n    <\/svg>`;\n\n    section.insertAdjacentElement('afterbegin',layer);\n\n    const svg = layer.querySelector('svg');\n    const planCenterGroup = layer.querySelector('#PLAN_CENTER');\n    const group = layer.querySelector('#PLAN_A');\n    const roomsGroup = layer.querySelector('#elpRoomsA');\n    const furnitureGroup = layer.querySelector('#elpFurnitureA');\n    const wallsGroup = layer.querySelector('#elpWallsA');\n    const doorsGroup = layer.querySelector('#elpDoorsA');\n    const windowsGroup = layer.querySelector('#elpWindowsA');\n    const nodesGroup = layer.querySelector('#elpNodesA');\n\n    const pointerCircle = layer.querySelector('#elpPointerA');\n    const pointerRing = layer.querySelector('#elpPointerRingA');\n    const pointerGlow = layer.querySelector('#elpPointerGlowA');\n\n    \/* Cursor global \u00fanico para ambas secciones *\/\nlet globalCursor = document.querySelector('#editeca-global-cursor');\n\nif(!globalCursor){\n    globalCursor = document.createElement('div');\n    globalCursor.id = 'editeca-global-cursor';\n    document.body.appendChild(globalCursor);\n}\n\n    let globalCursorX = 0;\n    let globalCursorY = 0;\n    let globalTargetX = 0;\n    let globalTargetY = 0;\n    let globalCursorActivo = false;\n\n    \/* ========================================================\n       POSICI\u00d3N Y ESCALA DEL PLANO\n       ========================================================\n\n       SOLO TIENES QUE TOCAR ESTOS 3 VALORES:\n\n       scale   = tama\u00f1o general del plano\n                 0.85 -> m\u00e1s peque\u00f1o\n                 1.00 -> tama\u00f1o normal\n                 1.15 -> m\u00e1s grande\n                 1.30 -> mucho m\u00e1s grande\n\n       offsetX = mover horizontalmente\n                 negativo -> izquierda\n                 positivo -> derecha\n\n       offsetY = mover verticalmente\n                 negativo -> arriba\n                 positivo -> abajo\n\n       IMPORTANTE:\n       El plano se centra AUTOM\u00c1TICAMENTE antes de aplicar estos offsets.\n       Por tanto puedes cambiar scale todas las veces que quieras y\n       seguir\u00e1 centrado.\n       ======================================================== *\/\n\n    const PLAN_VIEW = {\n        scale:0.80,\n        offsetX:100,\n        offsetY:-90\n    };\n\n    const CONFIG = {\n        \/* M\u00e1s radio y bastante m\u00e1s desplazamiento que en la versi\u00f3n anterior *\/\n        wallRadius:135,\n        wallMovement:46,\n        roomExpansion:52,\n        easing:.072\n    };\n\n    const BASE = {\n        left:170,\n        right:995,\n        top:100,\n        bottom:650\n    };\n\n    \/*\n     * El dibujo tiene una peque\u00f1a prolongaci\u00f3n inferior hasta Y=690.\n     * Usamos esos l\u00edmites para calcular su centro geom\u00e9trico real.\n     *\/\n    const PLAN_BOUNDS = {\n        left:BASE.left,\n        right:BASE.right,\n        top:BASE.top,\n        bottom:690\n    };\n\n    function actualizarVistaPlano(){\n\n        const vb=svg.viewBox.baseVal;\n\n        \/*\n         * Centro del SVG \/ viewBox.\n         *\/\n        const svgCenterX=\n            vb.x+\n            vb.width\/2;\n\n        const svgCenterY=\n            vb.y+\n            vb.height\/2;\n\n        \/*\n         * Centro geom\u00e9trico real del plano.\n         *\/\n        const planCenterX=\n            PLAN_BOUNDS.left+\n            (PLAN_BOUNDS.right-PLAN_BOUNDS.left)\/2;\n\n        const planCenterY=\n            PLAN_BOUNDS.top+\n            (PLAN_BOUNDS.bottom-PLAN_BOUNDS.top)\/2;\n\n        \/*\n         * GRUPO EXTERIOR:\n         * coloca el centro del plano en el centro del SVG\n         * y despu\u00e9s aplica el desplazamiento manual.\n         *\/\n        planCenterGroup.setAttribute(\n            'transform',\n            `translate(${\n                svgCenterX+PLAN_VIEW.offsetX\n            } ${\n                svgCenterY+PLAN_VIEW.offsetY\n            })`\n        );\n\n        \/*\n         * GRUPO INTERIOR:\n         * escala alrededor del centro geom\u00e9trico del plano.\n         *\n         * As\u00ed cambiar scale NO altera su centrado.\n         *\/\n        group.setAttribute(\n            'transform',\n            `scale(${PLAN_VIEW.scale}) translate(${\n                -planCenterX\n            } ${\n                -planCenterY\n            })`\n        );\n    }\n\n    \/*\n     * Atajos opcionales para poder mover\/escalar el plano\n     * desde la consola sin editar otra parte del c\u00f3digo:\n     *\n     * editecaPlano.setScale(1.15)\n     * editecaPlano.setPosition(0,-40)\n     * editecaPlano.setView(1.10,20,-30)\n     *\/\n    window.editecaPlano = {\n        setScale(scale){\n            PLAN_VIEW.scale=Number(scale);\n            actualizarVistaPlano();\n        },\n\n        setPosition(x=0,y=0){\n            PLAN_VIEW.offsetX=Number(x);\n            PLAN_VIEW.offsetY=Number(y);\n            actualizarVistaPlano();\n        },\n\n        setView(scale=1,x=0,y=0){\n            PLAN_VIEW.scale=Number(scale);\n            PLAN_VIEW.offsetX=Number(x);\n            PLAN_VIEW.offsetY=Number(y);\n            actualizarVistaPlano();\n        },\n\n        getView(){\n            return {...PLAN_VIEW};\n        }\n    };\n\n    \/*\n     * Aplicamos el centrado inicial.\n     *\/\n    actualizarVistaPlano();\n\n    const ORIGINAL = {\n        x1:365,\n        x2:555,\n        x3:760,\n        y1:275,\n        y2:470\n    };\n\n    const LIMITS = {\n        \/*\n         * Permitimos cambios mucho m\u00e1s visibles.\n         * Los gaps m\u00ednimos evitan que una habitaci\u00f3n colapse.\n         *\/\n        x1min:285,\n        x1max:450,\n        x2max:690,\n        x3max:865,\n        minGapX:110,\n\n        y1min:205,\n        y1max:350,\n        y2max:565,\n        minGapY:115\n    };\n\n    const state = {\n        x1:ORIGINAL.x1,\n        x2:ORIGINAL.x2,\n        x3:ORIGINAL.x3,\n        y1:ORIGINAL.y1,\n        y2:ORIGINAL.y2,\n\n        targetX1:ORIGINAL.x1,\n        targetX2:ORIGINAL.x2,\n        targetX3:ORIGINAL.x3,\n        targetY1:ORIGINAL.y1,\n        targetY2:ORIGINAL.y2\n    };\n\n    const NS = 'http:\/\/www.w3.org\/2000\/svg';\n\n    function crearSVG(tipo,clase){\n        const element = document.createElementNS(NS,tipo);\n        if(clase) element.setAttribute('class',clase);\n        return element;\n    }\n\n    function setRect(el,x,y,w,h,rx=0){\n        el.setAttribute('x',x);\n        el.setAttribute('y',y);\n        el.setAttribute('width',Math.max(1,w));\n        el.setAttribute('height',Math.max(1,h));\n        if(rx) el.setAttribute('rx',rx);\n    }\n\n    function setLine(el,x1,y1,x2,y2){\n        el.setAttribute('x1',x1);\n        el.setAttribute('y1',y1);\n        el.setAttribute('x2',x2);\n        el.setAttribute('y2',y2);\n    }\n\n    function clamp(v,min,max){\n        return Math.max(min,Math.min(max,v));\n    }\n\n    function bbox(points){\n        const xs = points.map(p=>p[0]);\n        const ys = points.map(p=>p[1]);\n        return {\n            left:Math.min(...xs),\n            right:Math.max(...xs),\n            top:Math.min(...ys),\n            bottom:Math.max(...ys),\n            width:Math.max(...xs)-Math.min(...xs),\n            height:Math.max(...ys)-Math.min(...ys)\n        };\n    }\n\n    function puntoDentro(x,y,polygon){\n        let dentro=false;\n\n        for(let i=0,j=polygon.length-1;i<polygon.length;j=i++){\n            const xi=polygon[i][0];\n            const yi=polygon[i][1];\n            const xj=polygon[j][0];\n            const yj=polygon[j][1];\n\n            const intersecta =\n                ((yi>y)!==(yj>y)) &&\n                (x < ((xj-xi)*(y-yi))\/(yj-yi)+xi);\n\n            if(intersecta) dentro=!dentro;\n        }\n\n        return dentro;\n    }\n\n    function distanciaSegmento(px,py,ax,ay,bx,by){\n        const vx=bx-ax;\n        const vy=by-ay;\n        const wx=px-ax;\n        const wy=py-ay;\n        const lengthSquared=vx*vx+vy*vy;\n\n        let t = lengthSquared ? (wx*vx+wy*vy)\/lengthSquared : 0;\n        t=Math.max(0,Math.min(1,t));\n\n        const x=ax+vx*t;\n        const y=ay+vy*t;\n        const dx=px-x;\n        const dy=py-y;\n\n        return {\n            distance:Math.sqrt(dx*dx+dy*dy),\n            x,y,t\n        };\n    }\n\n    \/* ========================================================\n       HABITACIONES\n       ======================================================== *\/\n\n    const rooms = [\n        {\n            name:'DORMITORIO',\n            type:'bedroom',\n            getPoints(){\n                return [\n                    [BASE.left,BASE.top],\n                    [state.x1,BASE.top],\n                    [state.x1,state.y1],\n                    [BASE.left,state.y1]\n                ];\n            }\n        },\n        {\n            name:'DORMITORIO',\n            type:'bedroom',\n            getPoints(){\n                return [\n                    [state.x1,BASE.top],\n                    [state.x2,BASE.top],\n                    [state.x2,state.y1],\n                    [state.x1,state.y1]\n                ];\n            }\n        },\n        {\n            name:'BA\u00d1O',\n            type:'bath',\n            getPoints(){\n                return [\n                    [state.x2,BASE.top],\n                    [state.x3,BASE.top],\n                    [state.x3,state.y1],\n                    [state.x2,state.y1]\n                ];\n            }\n        },\n        {\n            name:'DORMITORIO',\n            type:'bedroom',\n            getPoints(){\n                return [\n                    [state.x3,BASE.top],\n                    [BASE.right,BASE.top],\n                    [BASE.right,state.y1],\n                    [state.x3,state.y1]\n                ];\n            }\n        },\n        {\n            name:'ESTUDIO',\n            type:'flex',\n            getPoints(){\n                return [\n                    [BASE.left,state.y1],\n                    [state.x2,state.y1],\n                    [state.x2,state.y2],\n                    [BASE.left,state.y2]\n                ];\n            }\n        },\n        {\n            name:'SAL\u00d3N',\n            type:'living',\n            getPoints(){\n                return [\n                    [state.x2,state.y1],\n                    [BASE.right,state.y1],\n                    [BASE.right,state.y2],\n                    [state.x3,state.y2],\n                    [state.x3,590],\n                    [state.x2,590]\n                ];\n            }\n        },\n        {\n            name:'BA\u00d1O',\n            type:'bath',\n            getPoints(){\n                return [\n                    [BASE.left,state.y2],\n                    [state.x1,state.y2],\n                    [state.x1,BASE.bottom],\n                    [BASE.left,BASE.bottom]\n                ];\n            }\n        },\n        {\n            name:'TERRAZA',\n            type:'terrace',\n            getPoints(){\n                return [\n                    [state.x1,state.y2],\n                    [state.x3,state.y2],\n                    [state.x3,BASE.bottom],\n                    [state.x1,BASE.bottom]\n                ];\n            }\n        },\n        {\n            name:'COCINA',\n            type:'kitchen',\n            getPoints(){\n                return [\n                    [state.x3,state.y2],\n                    [BASE.right,state.y2],\n                    [BASE.right,BASE.bottom],\n                    [state.x3,BASE.bottom]\n                ];\n            }\n        }\n    ];\n\n    rooms.forEach(room=>{\n        const polygon=crearSVG('polygon','elp-room');\n        roomsGroup.appendChild(polygon);\n        room.element=polygon;\n    });\n\n    \/* ========================================================\n       MOBILIARIO\n       Cada estancia tiene su propio grupo.\n       ======================================================== *\/\n\n    function crearGrupoMueble(room){\n        const g=crearSVG('g','elp-furniture-room');\n        furnitureGroup.appendChild(g);\n        room.furnitureGroup=g;\n        return g;\n    }\n\n    function rectFurniture(g,cls='elp-furniture'){\n        const r=crearSVG('rect',cls);\n        g.appendChild(r);\n        return r;\n    }\n\n    function lineFurniture(g,cls='elp-furniture-detail'){\n        const l=crearSVG('line',cls);\n        g.appendChild(l);\n        return l;\n    }\n\n    function ellipseFurniture(g,cls='elp-furniture'){\n        const e=crearSVG('ellipse',cls);\n        g.appendChild(e);\n        return e;\n    }\n\n    function circleFurniture(g,cls='elp-furniture'){\n        const c=crearSVG('circle',cls);\n        g.appendChild(c);\n        return c;\n    }\n\n    function pathFurniture(g,cls='elp-furniture-detail'){\n        const p=crearSVG('path',cls);\n        g.appendChild(p);\n        return p;\n    }\n\n    function polylineFurniture(g,cls='elp-furniture-detail'){\n        const p=crearSVG('polyline',cls);\n        g.appendChild(p);\n        return p;\n    }\n\n    function setCircle(el,cx,cy,r){\n        el.setAttribute('cx',cx);\n        el.setAttribute('cy',cy);\n        el.setAttribute('r',r);\n    }\n\n    function setEllipse(el,cx,cy,rx,ry){\n        el.setAttribute('cx',cx);\n        el.setAttribute('cy',cy);\n        el.setAttribute('rx',rx);\n        el.setAttribute('ry',ry);\n    }\n\n    rooms.forEach(room=>{\n        const g=crearGrupoMueble(room);\n\n        if(room.type==='bedroom'){\n            room.furniture={\n                bed:rectFurniture(g,'elp-furniture-fill'),\n                headboard:lineFurniture(g,'elp-furniture-accent'),\n                pillow1:rectFurniture(g,'elp-furniture'),\n                pillow2:rectFurniture(g,'elp-furniture'),\n                blanket:polylineFurniture(g,'elp-furniture-detail'),\n                duvet:lineFurniture(g,'elp-furniture-detail'),\n\n                night1:rectFurniture(g,'elp-furniture'),\n                night2:rectFurniture(g,'elp-furniture'),\n                lamp1:circleFurniture(g,'elp-furniture-accent'),\n                lamp2:circleFurniture(g,'elp-furniture-accent'),\n\n                closet:rectFurniture(g,'elp-furniture'),\n                closetLine1:lineFurniture(g,'elp-furniture-detail'),\n                closetLine2:lineFurniture(g,'elp-furniture-detail'),\n\n                plantPot:circleFurniture(g,'elp-furniture-detail'),\n                plantA:lineFurniture(g,'elp-furniture-detail'),\n                plantB:lineFurniture(g,'elp-furniture-detail'),\n                plantC:lineFurniture(g,'elp-furniture-detail')\n            };\n        }\n\n        if(room.type==='bath'){\n            room.furniture={\n                shower:rectFurniture(g,'elp-furniture-fill'),\n                showerDiag1:lineFurniture(g,'elp-furniture-detail'),\n                showerDiag2:lineFurniture(g,'elp-furniture-detail'),\n\n                wc:ellipseFurniture(g,'elp-furniture'),\n                wcInner:ellipseFurniture(g,'elp-furniture-detail'),\n                wcTank:rectFurniture(g,'elp-furniture'),\n\n                vanity:rectFurniture(g,'elp-furniture-fill'),\n                sinkBowl:ellipseFurniture(g,'elp-furniture-accent'),\n                tap:lineFurniture(g,'elp-furniture-detail'),\n\n                mirror:rectFurniture(g,'elp-furniture-detail')\n            };\n        }\n\n        if(room.type==='study'){\n            room.furniture={\n                desk:rectFurniture(g,'elp-furniture-fill'),\n                chair:circleFurniture(g,'elp-furniture'),\n                chairBack:lineFurniture(g,'elp-furniture-detail'),\n                shelf:rectFurniture(g,'elp-furniture'),\n                shelf1:lineFurniture(g,'elp-furniture-detail'),\n                shelf2:lineFurniture(g,'elp-furniture-detail'),\n                monitor:rectFurniture(g,'elp-furniture-accent'),\n                keyboard:rectFurniture(g,'elp-furniture-detail'),\n                rug:rectFurniture(g,'elp-furniture-detail'),\n                plantPot:circleFurniture(g,'elp-furniture-detail'),\n                plantA:lineFurniture(g,'elp-furniture-detail'),\n                plantB:lineFurniture(g,'elp-furniture-detail')\n            };\n        }\n\n        if(room.type==='flex'){\n            room.furniture={\n                \/* modo sal\u00f3n *\/\n                sofa:rectFurniture(g,'elp-furniture-fill'),\n                sofaSeat:rectFurniture(g,'elp-furniture-detail'),\n                coffee:rectFurniture(g,'elp-furniture'),\n                tvUnit:rectFurniture(g,'elp-furniture'),\n                tv:rectFurniture(g,'elp-furniture-accent'),\n                sideTable:rectFurniture(g,'elp-furniture'),\n                console:rectFurniture(g,'elp-furniture'),\n\n                \/* modo estudio *\/\n                desk:rectFurniture(g,'elp-furniture-fill'),\n                monitor:rectFurniture(g,'elp-furniture-accent'),\n                keyboard:rectFurniture(g,'elp-furniture-detail'),\n                chair:circleFurniture(g,'elp-furniture'),\n                chairBack:lineFurniture(g,'elp-furniture-detail'),\n                shelf:rectFurniture(g,'elp-furniture'),\n                shelf1:lineFurniture(g,'elp-furniture-detail'),\n                shelf2:lineFurniture(g,'elp-furniture-detail')\n            };\n        }\n\n        if(room.type==='living'){\n            room.furniture={\n                sofaA:rectFurniture(g,'elp-furniture-fill'),\n                sofaB:rectFurniture(g,'elp-furniture-fill'),\n                cushion1:rectFurniture(g,'elp-furniture-detail'),\n                cushion2:rectFurniture(g,'elp-furniture-detail'),\n                cushion3:rectFurniture(g,'elp-furniture-detail'),\n\n                table:rectFurniture(g,'elp-furniture'),\n                tableInner:rectFurniture(g,'elp-furniture-detail'),\n\n                tvUnit:rectFurniture(g,'elp-furniture'),\n                tv:rectFurniture(g,'elp-furniture-accent'),\n\n                armchair:rectFurniture(g,'elp-furniture-fill'),\n                armchairSeat:rectFurniture(g,'elp-furniture-detail'),\n                sideTable:rectFurniture(g,'elp-furniture'),\n                console:rectFurniture(g,'elp-furniture'),\n\n                plantPot:circleFurniture(g,'elp-furniture-detail'),\n                plantA:lineFurniture(g,'elp-furniture-detail'),\n                plantB:lineFurniture(g,'elp-furniture-detail'),\n                plantC:lineFurniture(g,'elp-furniture-detail')\n            };\n        }\n\n        if(room.type==='terrace'){\n            room.furniture={\n                table:ellipseFurniture(g,'elp-furniture'),\n                tableInner:ellipseFurniture(g,'elp-furniture-detail'),\n\n                chair1:circleFurniture(g),\n                chair2:circleFurniture(g),\n                chair3:circleFurniture(g),\n                chair4:circleFurniture(g),\n\n                planter1:rectFurniture(g,'elp-furniture-fill'),\n                planter2:rectFurniture(g,'elp-furniture-fill'),\n\n                plant1a:lineFurniture(g,'elp-furniture-detail'),\n                plant1b:lineFurniture(g,'elp-furniture-detail'),\n                plant2a:lineFurniture(g,'elp-furniture-detail'),\n                plant2b:lineFurniture(g,'elp-furniture-detail')\n            };\n        }\n\n        if(room.type==='kitchen'){\n            room.furniture={\n                counterTop:rectFurniture(g,'elp-furniture-fill'),\n                counterSide:rectFurniture(g,'elp-furniture-fill'),\n\n                module1:lineFurniture(g,'elp-furniture-detail'),\n                module2:lineFurniture(g,'elp-furniture-detail'),\n                module3:lineFurniture(g,'elp-furniture-detail'),\n\n                sink:rectFurniture(g,'elp-furniture-accent'),\n                sinkDivider:lineFurniture(g,'elp-furniture-detail'),\n                tap:lineFurniture(g,'elp-furniture-detail'),\n\n                hob:rectFurniture(g,'elp-furniture-accent'),\n                burner1:circleFurniture(g,'elp-furniture-detail'),\n                burner2:circleFurniture(g,'elp-furniture-detail'),\n                burner3:circleFurniture(g,'elp-furniture-detail'),\n                burner4:circleFurniture(g,'elp-furniture-detail'),\n\n                island:rectFurniture(g,'elp-furniture-fill'),\n                islandInner:rectFurniture(g,'elp-furniture-detail'),\n                stool1:circleFurniture(g,'elp-furniture-detail'),\n                stool2:circleFurniture(g,'elp-furniture-detail'),\n                stool3:circleFurniture(g,'elp-furniture-detail'),\n\n                fridge:rectFurniture(g,'elp-furniture'),\n                fridgeLine:lineFurniture(g,'elp-furniture-detail')\n            };\n        }\n    });\n\n    function updateBedroom(room){\n        const b=bbox(room.getPoints());\n        const f=room.furniture;\n        const idx=rooms.indexOf(room);\n\n        const m=14;\n        const doorBand=70;\n\n        const usableW=b.width-m*2;\n        const usableH=b.height-m*2-doorBand;\n\n        \/*\n         * DOBLE por defecto.\n         * Individual solo cuando el dormitorio realmente se estrecha.\n         *\/\n        const single =\n            usableW < 152 ||\n            usableH < 108;\n\n        \/*\n         * Eliminamos cualquier s\u00edmbolo decorativo.\n         *\/\n        [\n            f.plantPot,\n            f.plantA,\n            f.plantB,\n            f.plantC\n        ].forEach(el=>{\n            if(el) el.style.opacity='0';\n        });\n\n        \/*\n         * PROPORCI\u00d3N REALISTA:\n         * la cama SIEMPRE es claramente m\u00e1s larga que ancha.\n         *\/\n        const doubleW=clamp(usableW*.48,70,96);\n        const doubleH=clamp(Math.max(doubleW*1.28, usableH*.72),92,126);\n\n        const singleW=clamp(usableW*.30,40,54);\n        const singleH=clamp(Math.max(singleW*1.65, usableH*.74),80,112);\n\n        const night=single ? 13 : 17;\n        const gap=single ? 5 : 7;\n\n        \/*\n         * HABITACI\u00d3N CENTRAL IZQUIERDA (idx 1):\n         * variedad: cama pegada a pared izquierda.\n         *\/\n        if(idx===1){\n\n            \/*\n             * SEGUNDO DORMITORIO:\n             * cama apoyada REALMENTE en el muro inferior.\n             * Cabecero abajo y cama orientada hacia arriba.\n             *\/\n            const bedW = single\n                ? clamp(usableW*.34,44,56)\n                : clamp(usableW*.50,72,94);\n\n            const bedH = single\n                ? clamp(Math.max(bedW*1.75, usableH*.78),88,118)\n                : clamp(Math.max(bedW*1.42, usableH*.82),108,136);\n\n            const localNight = single ? 13 : 16;\n            const localGap = single ? 5 : 7;\n\n            \/*\n             * La puerta est\u00e1 en el extremo inferior derecho.\n             * Reservamos ese sector y pegamos la cama al tramo libre\n             * del muro inferior.\n             *\/\n            const doorReserve=62;\n            const bedBottom=b.bottom-8;\n\n            const totalW = single\n                ? bedW+localGap+localNight\n                : localNight+localGap+bedW+localGap+localNight;\n\n            const maxStartX=\n                b.right-doorReserve-totalW;\n\n            const startX=clamp(\n                b.left+18,\n                b.left+14,\n                Math.max(b.left+14,maxStartX)\n            );\n\n            const bedX = single\n                ? startX\n                : startX+localNight+localGap;\n\n            const bedY=bedBottom-bedH;\n\n            setRect(f.bed,bedX,bedY,bedW,bedH,3);\n\n            \/*\n             * Cabecero tocando visualmente el muro inferior.\n             *\/\n            setLine(\n                f.headboard,\n                bedX,\n                bedBottom-2,\n                bedX+bedW,\n                bedBottom-2\n            );\n\n            if(single){\n                f.pillow2.style.opacity='0';\n\n                setRect(\n                    f.pillow1,\n                    bedX+bedW*.20,\n                    bedBottom-23,\n                    bedW*.60,\n                    16,\n                    2\n                );\n            }else{\n                f.pillow2.style.opacity='1';\n\n                const pg=5;\n                const pw=(bedW-18-pg)\/2;\n\n                setRect(f.pillow1,bedX+7,bedBottom-24,pw,18,2);\n                setRect(f.pillow2,bedX+7+pw+pg,bedBottom-24,pw,18,2);\n            }\n\n            \/*\n             * Manta simple.\n             *\/\n            setLine(\n                f.duvet,\n                bedX+6,\n                bedY+bedH*.43,\n                bedX+bedW-6,\n                bedY+bedH*.43\n            );\n\n            f.blanket.setAttribute(\n                'points',\n                [\n                    [bedX+6,bedY+bedH*.31],\n                    [bedX+bedW-6,bedY+bedH*.31]\n                ].map(p=>p.join(',')).join(' ')\n            );\n\n            \/*\n             * Mesillas tambi\u00e9n contra el muro inferior.\n             *\/\n            if(single){\n                f.night1.style.opacity='0';\n                f.lamp1.style.opacity='0';\n\n                setRect(\n                    f.night2,\n                    bedX+bedW+localGap,\n                    bedBottom-localNight,\n                    localNight,\n                    localNight,\n                    2\n                );\n\n                setCircle(\n                    f.lamp2,\n                    bedX+bedW+localGap+localNight\/2,\n                    bedBottom-localNight\/2,\n                    4\n                );\n            }else{\n                f.night1.style.opacity='1';\n                f.lamp1.style.opacity='1';\n                f.night2.style.opacity='1';\n                f.lamp2.style.opacity='1';\n\n                setRect(\n                    f.night1,\n                    startX,\n                    bedBottom-localNight,\n                    localNight,\n                    localNight,\n                    2\n                );\n\n                setRect(\n                    f.night2,\n                    bedX+bedW+localGap,\n                    bedBottom-localNight,\n                    localNight,\n                    localNight,\n                    2\n                );\n\n                setCircle(\n                    f.lamp1,\n                    startX+localNight\/2,\n                    bedBottom-localNight\/2,\n                    4\n                );\n\n                setCircle(\n                    f.lamp2,\n                    bedX+bedW+localGap+localNight\/2,\n                    bedBottom-localNight\/2,\n                    4\n                );\n            }\n\n            \/*\n             * Armario arriba a la derecha para no competir con la cama.\n             *\/\n            const closetW=clamp(usableW*.27,34,48);\n            const closetH=17;\n            const closetX=b.right-m-closetW;\n            const closetY=b.top+m;\n\n            setRect(f.closet,closetX,closetY,closetW,closetH,1);\n            setLine(f.closetLine1,closetX+closetW*.33,closetY,closetX+closetW*.33,closetY+closetH);\n            setLine(f.closetLine2,closetX+closetW*.66,closetY,closetX+closetW*.66,closetY+closetH);\n\n            return;\n        }\n\n        \/*\n         * RESTO DE DORMITORIOS:\n         * cama con cabecero contra pared superior.\n         * Incluso cuando es peque\u00f1a sigue siendo VERTICAL Y LARGA.\n         *\/\n        const bedW=single ? singleW : doubleW;\n        const bedH=single ? singleH : doubleH;\n\n        const total=\n            single\n            ? bedW+gap+night\n            : night+gap+bedW+gap+night;\n\n        const shift=idx===3 ? 7 : 0;\n\n        const startX=\n            b.left+\n            (b.width-total)\/2+\n            shift;\n\n        const bedX=\n            single\n            ? startX\n            : startX+night+gap;\n\n        const bedY=b.top+m;\n\n        setRect(f.bed,bedX,bedY,bedW,bedH,3);\n        setLine(f.headboard,bedX,bedY+2,bedX+bedW,bedY+2);\n\n        if(single){\n            f.pillow2.style.opacity='0';\n\n            setRect(\n                f.pillow1,\n                bedX+bedW*.20,\n                bedY+7,\n                bedW*.60,\n                16,\n                2\n            );\n        }else{\n            f.pillow2.style.opacity='1';\n\n            const pg=5;\n            const pw=(bedW-18-pg)\/2;\n\n            setRect(f.pillow1,bedX+7,bedY+7,pw,18,2);\n            setRect(f.pillow2,bedX+7+pw+pg,bedY+7,pw,18,2);\n        }\n\n        setLine(\n            f.duvet,\n            bedX+6,\n            bedY+bedH*.60,\n            bedX+bedW-6,\n            bedY+bedH*.60\n        );\n\n        \/*\n         * Quitamos por completo el zigzag de manta.\n         *\/\n        f.blanket.setAttribute(\n            'points',\n            [\n                [bedX+6,bedY+bedH*.70],\n                [bedX+bedW-6,bedY+bedH*.70]\n            ].map(p=>p.join(',')).join(' ')\n        );\n\n        if(single){\n            f.night1.style.opacity='0';\n            f.lamp1.style.opacity='0';\n\n            setRect(\n                f.night2,\n                bedX+bedW+gap,\n                bedY+3,\n                night,\n                night,\n                2\n            );\n\n            setCircle(\n                f.lamp2,\n                bedX+bedW+gap+night\/2,\n                bedY+3+night\/2,\n                4\n            );\n        }else{\n            f.night1.style.opacity='1';\n            f.lamp1.style.opacity='1';\n\n            setRect(f.night1,startX,bedY+3,night,night,2);\n            setRect(f.night2,bedX+bedW+gap,bedY+3,night,night,2);\n\n            setCircle(f.lamp1,startX+night\/2,bedY+3+night\/2,4);\n            setCircle(f.lamp2,bedX+bedW+gap+night\/2,bedY+3+night\/2,4);\n        }\n\n        \/*\n         * Armario contra pared lateral.\n         * Dormitorio derecho usa pared izquierda para dar variedad.\n         *\/\n        const closetW=18;\n        const closetH=single ? 34 : 44;\n\n        const closetX=\n            idx===3\n            ? b.left+m\n            : b.right-m-closetW;\n\n        const preferredY=b.top+m+bedH+10;\n\n        const canPlaceBelow=\n            preferredY+closetH <=\n            b.bottom-doorBand-8;\n\n        const closetY=\n            canPlaceBelow\n            ? preferredY\n            : b.top+m;\n\n        setRect(f.closet,closetX,closetY,closetW,closetH,1);\n        setLine(f.closetLine1,closetX+6,closetY,closetX+6,closetY+closetH);\n        setLine(f.closetLine2,closetX+12,closetY,closetX+12,closetY+closetH);\n    }\n\n    function updateBath(room){\n        const b=bbox(room.getPoints());\n        const f=room.furniture;\n\n        const m=12;\n        const isUpper=(room===rooms[2]);\n\n        const doorBand=70;\n\n        const safeTop=\n            isUpper\n            ? b.top+m\n            : b.top+m+doorBand;\n\n        const safeBottom=\n            isUpper\n            ? b.bottom-m-doorBand\n            : b.bottom-m;\n\n        const safeW=b.width-m*2;\n        const safeH=safeBottom-safeTop;\n\n        \/*\n         * BA\u00d1ERA por defecto.\n         * Solo pasa a ducha si el ba\u00f1o se estrecha.\n         *\/\n        const useShower=\n            safeW < 170 ||\n            safeH < 92;\n\n        let wetX=b.left+m;\n        let wetY;\n        let wetW;\n        let wetH;\n\n        if(useShower){\n\n            wetW=clamp(Math.min(safeW,safeH)*.30,36,48);\n            wetH=wetW;\n\n            wetY=\n                isUpper\n                ? safeTop\n                : safeBottom-wetH;\n\n            setRect(\n                f.shower,\n                wetX,\n                wetY,\n                wetW,\n                wetH,\n                2\n            );\n\n            setLine(\n                f.showerDiag1,\n                wetX+4,\n                wetY+4,\n                wetX+wetW-4,\n                wetY+wetH-4\n            );\n\n            setLine(\n                f.showerDiag2,\n                wetX+wetW-4,\n                wetY+4,\n                wetX+4,\n                wetY+wetH-4\n            );\n\n        }else{\n\n            wetW=clamp(safeW*.46,82,108);\n            wetH=42;\n\n            wetY=\n                isUpper\n                ? safeTop\n                : safeBottom-wetH;\n\n            setRect(\n                f.shower,\n                wetX,\n                wetY,\n                wetW,\n                wetH,\n                9\n            );\n\n            \/*\n             * Ba\u00f1era limpia, sin cruces.\n             *\/\n            setLine(\n                f.showerDiag1,\n                wetX+10,\n                wetY+wetH*.50,\n                wetX+wetW-10,\n                wetY+wetH*.50\n            );\n\n            setLine(\n                f.showerDiag2,\n                wetX+wetW-15,\n                wetY+7,\n                wetX+wetW-15,\n                wetY+wetH-7\n            );\n        }\n\n        \/*\n         * LAVABO SIEMPRE AL LADO del elemento h\u00famedo.\n         * Nunca encima.\n         *\/\n        const gap=12;\n        const vanityW=clamp(safeW*.24,32,44);\n        const vanityH=24;\n\n        const vanityX=wetX+wetW+gap;\n\n        const vanityY=\n            isUpper\n            ? wetY\n            : wetY+wetH-vanityH;\n\n        if(\n            vanityX+vanityW <=\n            b.right-m-48\n        ){\n            setRect(\n                f.vanity,\n                vanityX,\n                vanityY,\n                vanityW,\n                vanityH,\n                2\n            );\n\n            setEllipse(\n                f.sinkBowl,\n                vanityX+vanityW\/2,\n                vanityY+vanityH\/2,\n                vanityW*.25,\n                6\n            );\n\n            setLine(\n                f.tap,\n                vanityX+vanityW\/2,\n                vanityY+4,\n                vanityX+vanityW\/2,\n                vanityY+9\n            );\n\n            \/*\n             * Espejo oculto para evitar rect\u00e1ngulos atravesando muros.\n             *\/\n            f.mirror.style.opacity='0';\n\n        }else{\n\n            \/*\n             * Si no cabe al lado, lavabo en pared izquierda\n             * pero separado verticalmente del plato\/ba\u00f1era.\n             *\/\n            const vW=24;\n            const vH=36;\n\n            const vX=b.left+m;\n            const vY=\n                isUpper\n                ? wetY+wetH+12\n                : wetY-vH-12;\n\n            setRect(f.vanity,vX,vY,vW,vH,2);\n\n            setEllipse(\n                f.sinkBowl,\n                vX+vW\/2,\n                vY+vH\/2,\n                6.5,\n                9\n            );\n\n            setLine(\n                f.tap,\n                vX+3,\n                vY+vH\/2,\n                vX+9,\n                vY+vH\/2\n            );\n\n            f.mirror.style.opacity='0';\n        }\n\n        \/*\n         * WC pegado a pared derecha.\n         *\/\n        const wcY=\n            safeTop+\n            safeH*.56;\n\n        const tankW=13;\n        const tankH=27;\n        const tankX=b.right-m-tankW;\n        const tankY=wcY-tankH\/2;\n\n        setRect(f.wcTank,tankX,tankY,tankW,tankH,2);\n\n        setEllipse(\n            f.wc,\n            tankX-18,\n            wcY,\n            19,\n            12\n        );\n\n        setEllipse(\n            f.wcInner,\n            tankX-18,\n            wcY,\n            11,\n            7\n        );\n    }\n\n    function updateStudy(room){\n        const b=bbox(room.getPoints());\n        const f=room.furniture;\n        const m=18;\n\n        \/*\n         * Puerta en pared derecha: dejamos esa zona libre.\n         * Escritorio siempre pegado a pared superior o izquierda.\n         *\/\n        const safeRight=b.right-84;\n        const availableW=safeRight-(b.left+m);\n\n        if(availableW>=135){\n\n            const deskW=clamp(availableW*.58,100,170);\n            const x=b.left+m;\n            const y=b.top+m;\n\n            setRect(f.desk,x,y,deskW,30,2);\n            setRect(f.monitor,x+deskW*.34,y+5,deskW*.30,8,1);\n            setRect(f.keyboard,x+deskW*.38,y+17,deskW*.24,5,1);\n\n            setCircle(f.chair,x+deskW*.5,y+55,13);\n            setLine(f.chairBack,x+deskW*.5-10,y+69,x+deskW*.5+10,y+69);\n\n            \/*\n             * Estanter\u00eda pegada a pared inferior.\n             *\/\n            const shelfW=clamp(availableW*.46,68,112);\n            const shelfX=b.left+m;\n            const shelfY=b.bottom-m-22;\n\n            setRect(f.shelf,shelfX,shelfY,shelfW,22,1);\n            setLine(f.shelf1,shelfX+shelfW*.33,shelfY,shelfX+shelfW*.33,shelfY+22);\n            setLine(f.shelf2,shelfX+shelfW*.66,shelfY,shelfX+shelfW*.66,shelfY+22);\n\n            setRect(f.rug,x+18,y+78,clamp(availableW*.50,75,125),46,4);\n\n        }else{\n\n            \/*\n             * Estudio estrecho: escritorio pegado a pared izquierda.\n             *\/\n            const deskH=clamp((b.height-m*2)*.48,76,108);\n            const x=b.left+m;\n            const y=b.top+m;\n\n            setRect(f.desk,x,y,30,deskH,2);\n            setRect(f.monitor,x+5,y+deskH*.35,8,deskH*.28,1);\n            setRect(f.keyboard,x+17,y+deskH*.39,5,deskH*.20,1);\n\n            setCircle(f.chair,x+54,y+deskH*.5,13);\n            setLine(f.chairBack,x+66,y+deskH*.5-10,x+66,y+deskH*.5+10);\n\n            const shelfW=clamp(b.width*.42,62,96);\n            const shelfY=b.bottom-m-22;\n\n            setRect(f.shelf,b.left+m,shelfY,shelfW,22,1);\n            setLine(f.shelf1,b.left+m+shelfW*.33,shelfY,b.left+m+shelfW*.33,shelfY+22);\n            setLine(f.shelf2,b.left+m+shelfW*.66,shelfY,b.left+m+shelfW*.66,shelfY+22);\n\n            setRect(f.rug,x+66,y+12,42,clamp(deskH*.80,58,84),4);\n        }\n\n        \/*\n         * Planta contra esquina inferior izquierda.\n         *\/\n        const px=b.left+m+10;\n        const py=b.bottom-m-34;\n\n        setCircle(f.plantPot,px,py,5);\n        setLine(f.plantA,px,py-3,px-6,py-12);\n        setLine(f.plantB,px,py-3,px+6,py-12);\n    }\n\n\n    function updateFlexRoom(room){\n        const b=bbox(room.getPoints());\n        const f=room.furniture;\n\n        const m=18;\n        const doorStrip=92;\n\n        const safeRight=b.right-doorStrip;\n        const usableW=safeRight-(b.left+m);\n        const usableH=b.height-m*2;\n\n        const small=\n            usableW < 225 ||\n            usableH < 142;\n\n        if(small){\n\n            [\n                f.sofa,\n                f.sofaSeat,\n                f.coffee,\n                f.tvUnit,\n                f.tv,\n                f.sideTable,\n                f.console\n            ].forEach(el=>el.style.opacity='0');\n\n            [\n                f.desk,\n                f.monitor,\n                f.keyboard,\n                f.chair,\n                f.chairBack,\n                f.shelf,\n                f.shelf1,\n                f.shelf2\n            ].forEach(el=>el.style.opacity='1');\n\n            const deskH=clamp(usableH*.54,74,104);\n\n            const deskX=b.left+m;\n            const deskY=b.top+m;\n\n            setRect(f.desk,deskX,deskY,28,deskH,2);\n            setRect(f.monitor,deskX+5,deskY+deskH*.34,8,deskH*.28,1);\n            setRect(f.keyboard,deskX+16,deskY+deskH*.39,5,deskH*.20,1);\n\n            setCircle(f.chair,deskX+52,deskY+deskH*.5,11);\n            setLine(f.chairBack,deskX+63,deskY+deskH*.5-9,deskX+63,deskY+deskH*.5+9);\n\n            const shelfW=clamp(usableW*.42,60,92);\n            const shelfY=b.bottom-m-20;\n\n            setRect(f.shelf,b.left+m,shelfY,shelfW,20,1);\n            setLine(f.shelf1,b.left+m+shelfW*.33,shelfY,b.left+m+shelfW*.33,shelfY+20);\n            setLine(f.shelf2,b.left+m+shelfW*.66,shelfY,b.left+m+shelfW*.66,shelfY+20);\n\n        }else{\n\n            [\n                f.desk,\n                f.monitor,\n                f.keyboard,\n                f.chair,\n                f.chairBack,\n                f.shelf,\n                f.shelf1,\n                f.shelf2\n            ].forEach(el=>el.style.opacity='0');\n\n            [\n                f.sofa,\n                f.sofaSeat,\n                f.coffee,\n                f.tvUnit,\n                f.tv,\n                f.sideTable,\n                f.console\n            ].forEach(el=>el.style.opacity='1');\n\n            const tvW=18;\n            const tvH=clamp(usableH*.46,64,88);\n\n            const tvX=b.left+m;\n            const tvY=b.top+(b.height-tvH)\/2;\n\n            setRect(f.tvUnit,tvX,tvY,tvW,tvH,1);\n            setRect(f.tv,tvX+4,tvY+8,10,tvH-16,1);\n\n            const sofaW=34;\n            const sofaH=clamp(usableH*.55,84,116);\n\n            const sofaX=safeRight-sofaW-16;\n            const sofaY=b.top+(b.height-sofaH)\/2;\n\n            setRect(f.sofa,sofaX,sofaY,sofaW,sofaH,5);\n            setRect(f.sofaSeat,sofaX+7,sofaY+8,sofaW-14,sofaH-16,3);\n\n            const gapStart=tvX+tvW;\n            const gapEnd=sofaX;\n\n            const coffeeW=26;\n            const coffeeH=clamp(usableH*.28,44,62);\n\n            const coffeeX=\n                gapStart+\n                (gapEnd-gapStart-coffeeW)\/2;\n\n            const coffeeY=\n                b.top+\n                (b.height-coffeeH)\/2;\n\n            setRect(f.coffee,coffeeX,coffeeY,coffeeW,coffeeH,4);\n\n            \/*\n             * Mueble auxiliar bajo pegado a pared superior.\n             *\/\n            const consoleW=clamp(usableW*.30,54,82);\n            const consoleX=b.left+m+26;\n            const consoleY=b.top+m;\n\n            setRect(\n                f.console,\n                consoleX,\n                consoleY,\n                consoleW,\n                16,\n                1\n            );\n\n            \/*\n             * Mesita auxiliar al lado del sof\u00e1.\n             *\/\n            const side=20;\n            const sx=sofaX-side-10;\n            const sy=sofaY+sofaH-side;\n\n            if(\n                sx > coffeeX+coffeeW+8\n            ){\n                setRect(f.sideTable,sx,sy,side,side,4);\n                f.sideTable.style.opacity='1';\n            }else{\n                f.sideTable.style.opacity='0';\n            }\n        }\n    }\n\n    function updateLiving(room){\n        const f=room.furniture;\n\n        [\n            f.plantPot,\n            f.plantA,\n            f.plantB,\n            f.plantC\n        ].forEach(el=>{\n            if(el) el.style.opacity='0';\n        });\n\n        const margin=18;\n        const doorClear=112;\n\n        const safe = {\n            left: state.x2 + doorClear,\n            right: BASE.right - margin,\n            top: state.y1 + margin,\n            bottom: state.y2 - margin\n        };\n\n        safe.width=Math.max(1,safe.right-safe.left);\n        safe.height=Math.max(1,safe.bottom-safe.top);\n\n        function rectBox(x,y,w,h){\n            return {\n                x,y,w,h,\n                right:x+w,\n                bottom:y+h\n            };\n        }\n\n        function overlaps(a,b,gap=8){\n            return !(\n                a.right+gap <= b.x ||\n                a.x >= b.right+gap ||\n                a.bottom+gap <= b.y ||\n                a.y >= b.bottom+gap\n            );\n        }\n\n        function insideSafe(box){\n            return (\n                box.x >= safe.left &&\n                box.y >= safe.top &&\n                box.right <= safe.right &&\n                box.bottom <= safe.bottom\n            );\n        }\n\n        \/*\n         * TV pegada a pared derecha.\n         *\/\n        const tvUnitW=18;\n        const tvUnitH=clamp(safe.height*.48,62,90);\n\n        const tvX=safe.right-tvUnitW;\n        const tvY=safe.top+(safe.height-tvUnitH)\/2-6;\n\n        const tvBox=rectBox(tvX,tvY,tvUnitW,tvUnitH);\n\n        setRect(f.tvUnit,tvBox.x,tvBox.y,tvBox.w,tvBox.h,1);\n        setRect(f.tv,tvBox.x+4,tvBox.y+8,tvBox.w-8,tvBox.h-16,1);\n\n        const narrow=\n            safe.width < 255 ||\n            safe.height < 145;\n\n        \/*\n         * Sof\u00e1 vertical y M\u00c1S CERCA de la TV.\n         * El conjunto se concentra en la mitad derecha del sal\u00f3n.\n         *\/\n        const sofaW=narrow ? 30 : 36;\n        const sofaH=clamp(\n            safe.height*(narrow ? .46 : .56),\n            narrow ? 64 : 80,\n            narrow ? 86 : 112\n        );\n\n        const desiredGapToTV = narrow ? 72 : 92;\n\n        let sofaX=\n            tvBox.x -\n            desiredGapToTV -\n            sofaW;\n\n        sofaX=clamp(\n            sofaX,\n            safe.left+18,\n            tvBox.x-sofaW-52\n        );\n\n        let sofaY=\n            tvBox.y+\n            (tvBox.h-sofaH)\/2;\n\n        sofaY=clamp(\n            sofaY,\n            safe.top,\n            safe.bottom-sofaH\n        );\n\n        const sofaBox=rectBox(\n            sofaX,\n            sofaY,\n            sofaW,\n            sofaH\n        );\n\n        setRect(f.sofaA,sofaBox.x,sofaBox.y,sofaBox.w,sofaBox.h,6);\n\n        \/*\n         * Chaise solo en modo amplio y si cabe sin tocar nada.\n         *\/\n        f.sofaB.style.opacity='0';\n\n        if(!narrow && safe.width>320){\n            const chaiseW=58;\n            const chaiseH=29;\n\n            const chaiseBox=rectBox(\n                sofaBox.x,\n                sofaBox.bottom-chaiseH,\n                chaiseW,\n                chaiseH\n            );\n\n            if(\n                insideSafe(chaiseBox) &&\n                !overlaps(chaiseBox,tvBox,10)\n            ){\n                f.sofaB.style.opacity='1';\n                setRect(f.sofaB,chaiseBox.x,chaiseBox.y,chaiseBox.w,chaiseBox.h,5);\n            }\n        }\n\n        \/*\n         * Cojines.\n         *\/\n        const cushionW=18;\n        const cushionH=narrow ? 20 : 24;\n\n        setRect(f.cushion1,sofaBox.x+6,sofaBox.y+7,cushionW,cushionH,3);\n        setRect(f.cushion2,sofaBox.x+6,sofaBox.y+sofaBox.h*.43,cushionW,cushionH,3);\n        setRect(f.cushion3,sofaBox.x+6,sofaBox.bottom-cushionH-7,cushionW,cushionH,3);\n\n        \/*\n         * Mesa m\u00e1s cerca del sof\u00e1 y de la TV.\n         * Sigue siendo vertical y collision-safe.\n         *\/\n        const leftLimit=sofaBox.right+10;\n        const rightLimit=tvBox.x-10;\n        const availableGap=rightLimit-leftLimit;\n\n        let tableW=narrow ? 20 : 26;\n        let tableH=clamp(\n            safe.height*(narrow ? .25 : .30),\n            narrow ? 36 : 42,\n            narrow ? 50 : 60\n        );\n\n        if(availableGap < tableW+10){\n            tableW=Math.max(14,availableGap-10);\n        }\n\n        let tableX=\n            leftLimit+\n            Math.max(0,(availableGap-tableW)\/2);\n\n        let tableY=\n            sofaBox.y+\n            (sofaBox.h-tableH)\/2;\n\n        tableY=clamp(\n            tableY,\n            safe.top,\n            safe.bottom-tableH\n        );\n\n        const tableBox=rectBox(\n            tableX,\n            tableY,\n            tableW,\n            tableH\n        );\n\n        const tableLegal=\n            tableBox.w>=14 &&\n            insideSafe(tableBox) &&\n            !overlaps(tableBox,sofaBox,5) &&\n            !overlaps(tableBox,tvBox,5);\n\n        if(tableLegal){\n            f.table.style.opacity='1';\n\n            setRect(\n                f.table,\n                tableBox.x,\n                tableBox.y,\n                tableBox.w,\n                tableBox.h,\n                4\n            );\n\n            if(!narrow && tableBox.w>=20){\n                f.tableInner.style.opacity='1';\n\n                setRect(\n                    f.tableInner,\n                    tableBox.x+4,\n                    tableBox.y+5,\n                    Math.max(8,tableBox.w-8),\n                    Math.max(10,tableBox.h-10),\n                    3\n                );\n            }else{\n                f.tableInner.style.opacity='0';\n            }\n        }else{\n            f.table.style.opacity='0';\n            f.tableInner.style.opacity='0';\n        }\n\n        \/*\n         * Sill\u00f3n solo si queda hueco real.\n         * Se intenta colocar cerca del conjunto, no lejos.\n         *\/\n        f.armchair.style.opacity='0';\n        f.armchairSeat.style.opacity='0';\n\n        if(!narrow){\n            const chairSize=30;\n\n            const candidates=[\n                rectBox(\n                    sofaBox.x-42,\n                    sofaBox.bottom-chairSize,\n                    chairSize,\n                    chairSize\n                ),\n                rectBox(\n                    sofaBox.x-42,\n                    sofaBox.y,\n                    chairSize,\n                    chairSize\n                )\n            ];\n\n            const occupied=[sofaBox,tvBox];\n            if(tableLegal) occupied.push(tableBox);\n\n            const chairBox=\n                candidates.find(candidate=>\n                    insideSafe(candidate) &&\n                    occupied.every(o=>!overlaps(candidate,o,8))\n                );\n\n            if(chairBox){\n                f.armchair.style.opacity='1';\n                f.armchairSeat.style.opacity='1';\n\n                setRect(\n                    f.armchair,\n                    chairBox.x,\n                    chairBox.y,\n                    chairBox.w,\n                    chairBox.h,\n                    6\n                );\n\n                setRect(\n                    f.armchairSeat,\n                    chairBox.x+6,\n                    chairBox.y+6,\n                    chairBox.w-12,\n                    chairBox.h-12,\n                    4\n                );\n            }\n        }\n\n        \/*\n         * Mueble auxiliar bajo, pegado a pared superior.\n         * Solo aparece si no interfiere con puerta ni sof\u00e1.\n         *\/\n        f.console.style.opacity='0';\n        f.sideTable.style.opacity='0';\n\n        const consoleW=clamp(safe.width*.22,52,76);\n        const consoleH=15;\n        const consoleBox=rectBox(\n            safe.left+10,\n            safe.top,\n            consoleW,\n            consoleH\n        );\n\n        if(\n            insideSafe(consoleBox) &&\n            !overlaps(consoleBox,sofaBox,10) &&\n            !overlaps(consoleBox,tvBox,10)\n        ){\n            f.console.style.opacity='1';\n            setRect(\n                f.console,\n                consoleBox.x,\n                consoleBox.y,\n                consoleBox.w,\n                consoleBox.h,\n                1\n            );\n        }\n\n        \/*\n         * Mesita auxiliar junto al sof\u00e1 si queda hueco real.\n         *\/\n        const sideSize=18;\n        const sideCandidate=rectBox(\n            sofaBox.x-sideSize-9,\n            sofaBox.bottom-sideSize,\n            sideSize,\n            sideSize\n        );\n\n        const occupiedForSide=[sofaBox,tvBox];\n        if(tableLegal) occupiedForSide.push(tableBox);\n\n        if(\n            insideSafe(sideCandidate) &&\n            occupiedForSide.every(\n                o=>!overlaps(sideCandidate,o,7)\n            )\n        ){\n            f.sideTable.style.opacity='1';\n\n            setRect(\n                f.sideTable,\n                sideCandidate.x,\n                sideCandidate.y,\n                sideCandidate.w,\n                sideCandidate.h,\n                4\n            );\n        }\n    }\n\n    function updateTerrace(room){\n        const b=bbox(room.getPoints());\n        const f=room.furniture;\n\n        const m=18;\n\n        \/*\n         * Corredor sagrado hacia puerta de cocina.\n         *\/\n        const rightDoorStrip=94;\n\n        const safeLeft=b.left+m;\n        const safeRight=b.right-rightDoorStrip;\n        const safeTop=b.top+m;\n        const safeBottom=b.bottom-m;\n\n        const innerW=safeRight-safeLeft;\n        const innerH=safeBottom-safeTop;\n\n        const cx=safeLeft+innerW\/2;\n        const cy=safeTop+innerH\/2;\n\n        [\n            f.planter1,\n            f.planter2,\n            f.plant1a,\n            f.plant1b,\n            f.plant2a,\n            f.plant2b\n        ].forEach(el=>{\n            if(el) el.style.opacity='0';\n        });\n\n        const round=\n            Math.abs(innerW-innerH) <\n            Math.min(innerW,innerH)*.38;\n\n        let rx;\n        let ry;\n\n        if(round){\n            rx=clamp(Math.min(innerW,innerH)*.20,25,38);\n            ry=rx;\n        }else{\n            rx=clamp(innerW*.20,34,52);\n            ry=clamp(innerH*.15,21,28);\n        }\n\n        setEllipse(f.table,cx,cy,rx,ry);\n        setEllipse(f.tableInner,cx,cy,Math.max(10,rx-6),Math.max(8,ry-5));\n\n        \/*\n         * Sillas algo m\u00e1s separadas.\n         *\/\n        const chairDX=rx+26;\n        const chairDY=ry+26;\n\n        const chairs=[\n            [cx-chairDX,cy],\n            [cx+chairDX,cy],\n            [cx,cy-chairDY],\n            [cx,cy+chairDY]\n        ];\n\n        [f.chair1,f.chair2,f.chair3,f.chair4].forEach((c,i)=>{\n            setCircle(c,chairs[i][0],chairs[i][1],9);\n        });\n    }\n\n    function updateKitchen(room){\n        const b=bbox(room.getPoints());\n        const f=room.furniture;\n\n        const m=12;\n        const counter=24;\n        const doorStrip=88;\n\n        const sideX=b.right-m-counter;\n\n        \/*\n         * Dos distribuciones:\n         * - normal: L superior + derecha + barra inferior;\n         * - estrecha: derecha + inferior, sin barra.\n         *\/\n        const narrowKitchen=\n            b.width < 185 ||\n            b.height < 150;\n\n        \/*\n         * Ocultamos siempre las l\u00edneas de m\u00f3dulos.\n         *\/\n        f.module1.style.opacity='0';\n        f.module2.style.opacity='0';\n        f.module3.style.opacity='0';\n\n        if(narrowKitchen){\n\n            \/*\n             * ================================================\n             * COCINA ESTRECHA\n             * ================================================\n             *\n             * PARED DERECHA:\n             * solo encimera continua.\n             *\n             * PARED INFERIOR:\n             * nueva encimera con fuegos + frigor\u00edfico en extremo.\n             *\n             * Nada de barra\/taburetes.\n             *\/\n\n            \/*\n             * La encimera superior se reutiliza como tramo INFERIOR.\n             *\/\n            const bottomY=b.bottom-m-counter;\n            const bottomX=b.left+m+doorStrip;\n            const bottomW=Math.max(\n                40,\n                sideX-bottomX+counter\n            );\n\n            setRect(\n                f.counterTop,\n                bottomX,\n                bottomY,\n                bottomW,\n                counter,\n                1\n            );\n\n            \/*\n             * Pared derecha: encimera limpia.\n             *\/\n            setRect(\n                f.counterSide,\n                sideX,\n                b.top+m,\n                counter,\n                Math.max(54,b.height-m*2),\n                1\n            );\n\n            \/*\n             * Fregadero integrado en pared derecha,\n             * zona superior.\n             *\/\n            const sinkW=14;\n            const sinkH=clamp(b.height*.22,30,42);\n\n            const sinkX=sideX+5;\n            const sinkY=b.top+m+18;\n\n            setRect(\n                f.sink,\n                sinkX,\n                sinkY,\n                sinkW,\n                sinkH,\n                2\n            );\n\n            setLine(\n                f.sinkDivider,\n                sinkX,\n                sinkY+sinkH*.5,\n                sinkX+sinkW,\n                sinkY+sinkH*.5\n            );\n\n            setLine(\n                f.tap,\n                sideX+2,\n                sinkY+sinkH*.5,\n                sideX+7,\n                sinkY+sinkH*.5\n            );\n\n            \/*\n             * FUEGOS EN EL MURO INFERIOR.\n             *\/\n            const hobW=34;\n            const hobH=18;\n\n            const hobX=bottomX+12;\n            const hobY=bottomY+3;\n\n            setRect(\n                f.hob,\n                hobX,\n                hobY,\n                hobW,\n                hobH,\n                2\n            );\n\n            const burners=[\n                [hobX+8,hobY+5],\n                [hobX+18,hobY+5],\n                [hobX+28,hobY+5],\n                [hobX+13,hobY+13]\n            ];\n\n            [f.burner1,f.burner2,f.burner3,f.burner4].forEach((c,i)=>{\n                c.style.opacity='1';\n                setCircle(c,burners[i][0],burners[i][1],3.4);\n            });\n\n            \/*\n             * Frigor\u00edfico en extremo inferior derecho.\n             *\/\n            const fridgeW=20;\n            const fridgeH=counter;\n\n            const fridgeX=b.right-m-fridgeW;\n            const fridgeY=bottomY;\n\n            setRect(\n                f.fridge,\n                fridgeX,\n                fridgeY,\n                fridgeW,\n                fridgeH,\n                1\n            );\n\n            setLine(\n                f.fridgeLine,\n                fridgeX+fridgeW*.5,\n                fridgeY,\n                fridgeX+fridgeW*.5,\n                fridgeY+fridgeH\n            );\n\n            \/*\n             * Sin barra ni taburetes en cocina estrecha.\n             *\/\n            [\n                f.island,\n                f.islandInner,\n                f.stool1,\n                f.stool2,\n                f.stool3\n            ].forEach(el=>{\n                el.style.opacity='0';\n            });\n\n        }else{\n\n            \/*\n             * ================================================\n             * COCINA NORMAL\n             * ================================================\n             *\/\n            const topX=b.left+m+doorStrip;\n            const topY=b.top+m;\n\n            const topW=Math.max(\n                30,\n                sideX-topX+counter\n            );\n\n            setRect(\n                f.counterTop,\n                topX,\n                topY,\n                topW,\n                counter,\n                1\n            );\n\n            setRect(\n                f.counterSide,\n                sideX,\n                topY,\n                counter,\n                Math.max(56,b.height-m*2),\n                1\n            );\n\n            \/*\n             * Fregadero m\u00e1s claro.\n             *\/\n            const sinkW=clamp(topW*.32,34,46);\n            const sinkH=15;\n\n            const sinkX=\n                topX+\n                clamp(\n                    topW*.18,\n                    8,\n                    24\n                );\n\n            setRect(\n                f.sink,\n                sinkX,\n                topY+4,\n                sinkW,\n                sinkH,\n                2\n            );\n\n            setLine(\n                f.sinkDivider,\n                sinkX+sinkW*.5,\n                topY+4,\n                sinkX+sinkW*.5,\n                topY+4+sinkH\n            );\n\n            setLine(\n                f.tap,\n                sinkX+sinkW*.5,\n                topY+1,\n                sinkX+sinkW*.5,\n                topY+6\n            );\n\n            \/*\n             * Fuegos en tramo derecho.\n             *\/\n            const hobSize=34;\n            const hobX=sideX-(hobSize-counter)\/2;\n\n            const hobY=clamp(\n                b.top+m+82,\n                b.top+m+counter+38,\n                b.bottom-m-84\n            );\n\n            setRect(\n                f.hob,\n                hobX,\n                hobY,\n                hobSize,\n                hobSize,\n                2\n            );\n\n            const burners=[\n                [hobX+9,hobY+9],\n                [hobX+25,hobY+9],\n                [hobX+9,hobY+25],\n                [hobX+25,hobY+25]\n            ];\n\n            [f.burner1,f.burner2,f.burner3,f.burner4].forEach((c,i)=>{\n                c.style.opacity='1';\n                setCircle(c,burners[i][0],burners[i][1],4.6);\n            });\n\n            \/*\n             * Frigor\u00edfico abajo a la derecha.\n             *\/\n            const fridgeH=40;\n            const fridgeY=b.bottom-m-fridgeH;\n\n            setRect(\n                f.fridge,\n                sideX,\n                fridgeY,\n                counter,\n                fridgeH,\n                1\n            );\n\n            setLine(\n                f.fridgeLine,\n                sideX,\n                fridgeY+fridgeH\/2,\n                sideX+counter,\n                fridgeY+fridgeH\/2\n            );\n\n            \/*\n             * Barra inferior habitual.\n             *\/\n            const freeLeft=b.left+m+doorStrip+6;\n            const freeRight=sideX-12;\n            const freeW=freeRight-freeLeft;\n\n            const barCanFit=\n                freeW >= 66 &&\n                b.height >= 126;\n\n            if(barCanFit){\n\n                f.island.style.opacity='1';\n                f.islandInner.style.opacity='0';\n\n                const barW=clamp(\n                    freeW*.78,\n                    62,\n                    110\n                );\n\n                const barH=20;\n\n                const barX=\n                    freeLeft+\n                    (freeW-barW)\/2;\n\n                const barY=\n                    b.bottom-m-barH;\n\n                setRect(\n                    f.island,\n                    barX,\n                    barY,\n                    barW,\n                    barH,\n                    2\n                );\n\n                const stoolY=barY-13;\n\n                if(barW>=82){\n                    f.stool1.style.opacity='1';\n                    f.stool2.style.opacity='1';\n                    f.stool3.style.opacity='1';\n\n                    const xs=[\n                        barX+barW*.22,\n                        barX+barW*.50,\n                        barX+barW*.78\n                    ];\n\n                    [f.stool1,f.stool2,f.stool3].forEach((c,i)=>{\n                        setCircle(c,xs[i],stoolY,6);\n                    });\n                }else{\n                    f.stool1.style.opacity='1';\n                    f.stool2.style.opacity='1';\n                    f.stool3.style.opacity='0';\n\n                    setCircle(f.stool1,barX+barW*.34,stoolY,6);\n                    setCircle(f.stool2,barX+barW*.66,stoolY,6);\n                }\n\n            }else{\n\n                [\n                    f.island,\n                    f.islandInner,\n                    f.stool1,\n                    f.stool2,\n                    f.stool3\n                ].forEach(el=>{\n                    el.style.opacity='0';\n                });\n            }\n        }\n    }\n\n    function updateFurniture(activeRoom){\n        rooms.forEach((room,index)=>{\n\n            room.furnitureGroup.classList.toggle(\n                'active',\n                index===activeRoom\n            );\n\n            \/*\n             * Muy importante:\n             * cada frame parte de la geometr\u00eda limpia.\n             * Despu\u00e9s se calcula la distribuci\u00f3n y, al final,\n             * entra el motor de colisiones.\n             *\/\n            Object.values(room.furniture).forEach(el=>{\n                if(!el) return;\n\n                el.removeAttribute('transform');\n\n                \/*\n                 * El motor de colisiones puede ocultar decoraci\u00f3n\n                 * o un bloque imposible. En el frame siguiente\n                 * vuelve a intentarse desde cero.\n                 *\/\n                el.style.opacity='';\n            });\n\n            if(room.type==='bedroom') updateBedroom(room);\n            if(room.type==='bath'){\n                updateBath(room);\n                if(room.furniture.mirror){\n                    room.furniture.mirror.style.opacity='0';\n                }\n            }\n            if(room.type==='study') updateStudy(room);\n            if(room.type==='flex') updateFlexRoom(room);\n            if(room.type==='living') updateLiving(room);\n            if(room.type==='terrace') updateTerrace(room);\n            if(room.type==='kitchen') updateKitchen(room);\n\n            \/*\n             * No permitimos s\u00edmbolos decorativos tipo planta\n             * en las zonas donde estaban generando c\u00edrculos\/l\u00edneas\n             * visualmente confusos.\n             *\/\n            if(\n                room.type==='bedroom' ||\n                room.type==='study' ||\n                room.type==='flex' ||\n                room.type==='living'\n            ){\n                [\n                    room.furniture.plantPot,\n                    room.furniture.plantA,\n                    room.furniture.plantB,\n                    room.furniture.plantC\n                ].forEach(el=>{\n                    if(el) el.style.opacity='0';\n                });\n            }\n        });\n    }\n\n    \/* ========================================================\n       MOTOR DE COLISIONES DEL MOBILIARIO\n\n       REGLAS ABSOLUTAS:\n       1. ning\u00fan mueble puede solaparse con otro;\n       2. ninguna pieza puede invadir una puerta;\n       3. ninguna pieza puede atravesar una pared;\n       4. si la posici\u00f3n original deja de ser v\u00e1lida,\n          se busca otra pared \/ esquina \/ zona libre;\n       5. si todav\u00eda no cabe, se reduce de forma progresiva;\n       6. la decoraci\u00f3n secundaria puede desaparecer antes\n          que invadir una zona prohibida.\n       ======================================================== *\/\n\n    const COLLISION = {\n        gap:8,\n        wallMargin:7,\n        doorRadius:48,\n        minScaleMajor:.54,\n        minScaleMinor:.44\n    };\n\n    function getVisibleElements(list){\n        return list.filter(el=>{\n            if(!el) return false;\n\n            const opacity =\n                el.style &&\n                el.style.opacity;\n\n            return opacity !== '0';\n        });\n    }\n\n    function unionBBox(elements){\n        const els=getVisibleElements(elements);\n\n        if(!els.length) return null;\n\n        let minX=Infinity;\n        let minY=Infinity;\n        let maxX=-Infinity;\n        let maxY=-Infinity;\n\n        els.forEach(el=>{\n            const b=el.getBBox();\n\n            minX=Math.min(minX,b.x);\n            minY=Math.min(minY,b.y);\n            maxX=Math.max(maxX,b.x+b.width);\n            maxY=Math.max(maxY,b.y+b.height);\n        });\n\n        if(\n            !Number.isFinite(minX) ||\n            !Number.isFinite(minY)\n        ){\n            return null;\n        }\n\n        return {\n            x:minX,\n            y:minY,\n            width:maxX-minX,\n            height:maxY-minY,\n            right:maxX,\n            bottom:maxY,\n            cx:(minX+maxX)\/2,\n            cy:(minY+maxY)\/2\n        };\n    }\n\n    function transformedBox(base,dx,dy,scale){\n        const cx=base.cx;\n        const cy=base.cy;\n\n        const x=\n            cx+\n            (base.x-cx)*scale+\n            dx;\n\n        const y=\n            cy+\n            (base.y-cy)*scale+\n            dy;\n\n        const width=\n            base.width*scale;\n\n        const height=\n            base.height*scale;\n\n        return {\n            x,\n            y,\n            width,\n            height,\n            right:x+width,\n            bottom:y+height,\n            cx:x+width\/2,\n            cy:y+height\/2\n        };\n    }\n\n    function boxesOverlap(a,b,gap=COLLISION.gap){\n        return !(\n            a.right+gap <= b.x ||\n            a.x >= b.right+gap ||\n            a.bottom+gap <= b.y ||\n            a.y >= b.bottom+gap\n        );\n    }\n\n    function pointInsideRoom(room,x,y){\n        return puntoDentro(\n            x,\n            y,\n            room.getPoints()\n        );\n    }\n\n    function boxInsideRoom(room,box){\n        \/*\n         * Probamos cuatro esquinas y centro.\n         * Las esquinas se meten unos p\u00edxeles hacia dentro\n         * para no penalizar muebles apoyados visualmente en pared.\n         *\/\n        const m=COLLISION.wallMargin;\n\n        const x1=box.x+m;\n        const x2=box.right-m;\n        const y1=box.y+m;\n        const y2=box.bottom-m;\n\n        if(x2<=x1 || y2<=y1){\n            return false;\n        }\n\n        const points=[\n            [x1,y1],\n            [x2,y1],\n            [x2,y2],\n            [x1,y2],\n            [box.cx,box.cy]\n        ];\n\n        return points.every(\n            p=>pointInsideRoom(\n                room,\n                p[0],\n                p[1]\n            )\n        );\n    }\n\n    function doorSafeZones(room){\n        const roomBox=bbox(room.getPoints());\n\n        \/*\n         * En vez de codificar cada puerta a mano,\n         * usamos las puertas reales del SVG.\n         *\n         * Se crea un cuadrado de seguridad alrededor\n         * de cada bisagra. Si toca esta habitaci\u00f3n,\n         * es zona sagrada para su mobiliario.\n         *\/\n        return doors\n            .map(door=>{\n                const x=door.getX();\n                const y=door.getY();\n                const r=COLLISION.doorRadius;\n\n                return {\n                    x:x-r,\n                    y:y-r,\n                    width:r*2,\n                    height:r*2,\n                    right:x+r,\n                    bottom:y+r,\n                    cx:x,\n                    cy:y\n                };\n            })\n            .filter(zone=>{\n                return !(\n                    zone.right < roomBox.left ||\n                    zone.x > roomBox.right ||\n                    zone.bottom < roomBox.top ||\n                    zone.y > roomBox.bottom\n                );\n            });\n    }\n\n    function clusterDefinitions(room){\n        const f=room.furniture;\n\n        if(room.type==='bedroom'){\n            return [\n                {\n                    id:'bed-system',\n                    major:true,\n                    elements:[\n                        f.bed,\n                        f.headboard,\n                        f.pillow1,\n                        f.pillow2,\n                        f.blanket,\n                        f.duvet,\n                        f.night1,\n                        f.night2,\n                        f.lamp1,\n                        f.lamp2\n                    ]\n                },\n                {\n                    id:'closet',\n                    major:true,\n                    elements:[\n                        f.closet,\n                        f.closetLine1,\n                        f.closetLine2\n                    ]\n                },\n                {\n                    id:'plant',\n                    major:false,\n                    elements:[\n                        f.plantPot,\n                        f.plantA,\n                        f.plantB,\n                        f.plantC\n                    ]\n                }\n            ];\n        }\n\n        if(room.type==='bath'){\n            return [\n                {\n                    id:'shower-tub',\n                    major:true,\n                    elements:[\n                        f.shower,\n                        f.showerDiag1,\n                        f.showerDiag2\n                    ]\n                },\n                {\n                    id:'vanity',\n                    major:true,\n                    elements:[\n                        f.vanity,\n                        f.sinkBowl,\n                        f.tap,\n                        f.mirror\n                    ]\n                },\n                {\n                    id:'wc',\n                    major:true,\n                    elements:[\n                        f.wc,\n                        f.wcInner,\n                        f.wcTank\n                    ]\n                }\n            ];\n        }\n\n        if(room.type==='study'){\n            return [\n                {\n                    id:'desk',\n                    major:true,\n                    elements:[\n                        f.desk,\n                        f.monitor,\n                        f.keyboard,\n                        f.chair,\n                        f.chairBack\n                    ]\n                },\n                {\n                    id:'shelf',\n                    major:true,\n                    elements:[\n                        f.shelf,\n                        f.shelf1,\n                        f.shelf2\n                    ]\n                },\n                {\n                    id:'rug',\n                    major:false,\n                    elements:[f.rug]\n                },\n                {\n                    id:'plant',\n                    major:false,\n                    elements:[\n                        f.plantPot,\n                        f.plantA,\n                        f.plantB\n                    ]\n                }\n            ];\n        }\n\n        if(room.type==='living'){\n            return [\n                {\n                    id:'sofa',\n                    major:true,\n                    elements:[\n                        f.sofaA,\n                        f.sofaB,\n                        f.cushion1,\n                        f.cushion2,\n                        f.cushion3\n                    ]\n                },\n                {\n                    id:'coffee-table',\n                    major:true,\n                    elements:[\n                        f.table,\n                        f.tableInner\n                    ]\n                },\n                {\n                    id:'tv',\n                    major:true,\n                    elements:[\n                        f.tvUnit,\n                        f.tv\n                    ]\n                },\n                {\n                    id:'armchair',\n                    major:true,\n                    elements:[\n                        f.armchair,\n                        f.armchairSeat\n                    ]\n                },\n                {\n                    id:'plant',\n                    major:false,\n                    elements:[\n                        f.plantPot,\n                        f.plantA,\n                        f.plantB,\n                        f.plantC\n                    ]\n                }\n            ];\n        }\n\n        if(room.type==='terrace'){\n            return [\n                {\n                    id:'table-chairs',\n                    major:true,\n                    elements:[\n                        f.table,\n                        f.tableInner,\n                        f.chair1,\n                        f.chair2,\n                        f.chair3,\n                        f.chair4\n                    ]\n                },\n                {\n                    id:'planter-1',\n                    major:false,\n                    elements:[\n                        f.planter1,\n                        f.plant1a,\n                        f.plant1b\n                    ]\n                },\n                {\n                    id:'planter-2',\n                    major:false,\n                    elements:[\n                        f.planter2,\n                        f.plant2a,\n                        f.plant2b\n                    ]\n                }\n            ];\n        }\n\n        if(room.type==='kitchen'){\n            return [\n                {\n                    id:'top-counter',\n                    major:true,\n                    fixed:true,\n                    elements:[\n                        f.counterTop,\n                        f.module1,\n                        f.module2,\n                        f.module3,\n                        f.sink,\n                        f.sinkDivider,\n                        f.tap\n                    ]\n                },\n                {\n                    id:'side-counter',\n                    major:true,\n                    fixed:true,\n                    elements:[\n                        f.counterSide,\n                        f.hob,\n                        f.burner1,\n                        f.burner2,\n                        f.burner3,\n                        f.burner4,\n                        f.fridge,\n                        f.fridgeLine\n                    ]\n                },\n                {\n                    id:'island',\n                    major:true,\n                    elements:[\n                        f.island,\n                        f.islandInner,\n                        f.stool1,\n                        f.stool2,\n                        f.stool3\n                    ]\n                }\n            ];\n        }\n\n        return [];\n    }\n\n    function candidatePlacements(base,roomBox,scale){\n        const scaled=\n            transformedBox(\n                base,\n                0,\n                0,\n                scale\n            );\n\n        const m=12;\n\n        const left=\n            roomBox.left+m;\n\n        const right=\n            roomBox.right-m-scaled.width;\n\n        const top=\n            roomBox.top+m;\n\n        const bottom=\n            roomBox.bottom-m-scaled.height;\n\n        const centerX=\n            roomBox.left+\n            (roomBox.width-scaled.width)\/2;\n\n        const centerY=\n            roomBox.top+\n            (roomBox.height-scaled.height)\/2;\n\n        \/*\n         * Primero intentamos conservar la posici\u00f3n.\n         * Despu\u00e9s probamos paredes, esquinas y centro.\n         *\/\n        const targets=[\n            [scaled.x,scaled.y],\n\n            [left,top],\n            [centerX,top],\n            [right,top],\n\n            [left,centerY],\n            [centerX,centerY],\n            [right,centerY],\n\n            [left,bottom],\n            [centerX,bottom],\n            [right,bottom]\n        ];\n\n        \/*\n         * Tambi\u00e9n hacemos una b\u00fasqueda local progresiva.\n         * Sirve para peque\u00f1os choques sin cambiar toda\n         * la composici\u00f3n de pared.\n         *\/\n        const shifts=[\n            14,28,42,58,74,92\n        ];\n\n        shifts.forEach(s=>{\n            targets.push(\n                [scaled.x+s,scaled.y],\n                [scaled.x-s,scaled.y],\n                [scaled.x,scaled.y+s],\n                [scaled.x,scaled.y-s],\n                [scaled.x+s,scaled.y+s],\n                [scaled.x-s,scaled.y+s],\n                [scaled.x+s,scaled.y-s],\n                [scaled.x-s,scaled.y-s]\n            );\n        });\n\n        \/*\n         * Eliminamos candidatos repetidos.\n         *\/\n        const seen=new Set();\n\n        return targets\n            .map(t=>{\n                const key=\n                    Math.round(t[0])+\n                    ':'+\n                    Math.round(t[1]);\n\n                if(seen.has(key)){\n                    return null;\n                }\n\n                seen.add(key);\n\n                const dx=t[0]-scaled.x;\n                const dy=t[1]-scaled.y;\n\n                return {\n                    dx,\n                    dy,\n                    box:transformedBox(\n                        base,\n                        dx,\n                        dy,\n                        scale\n                    )\n                };\n            })\n            .filter(Boolean);\n    }\n\n    function validPlacement(\n        room,\n        candidateBox,\n        placedBoxes,\n        doorZones\n    ){\n        if(\n            !boxInsideRoom(\n                room,\n                candidateBox\n            )\n        ){\n            return false;\n        }\n\n        if(\n            doorZones.some(\n                zone=>\n                    boxesOverlap(\n                        candidateBox,\n                        zone,\n                        4\n                    )\n            )\n        ){\n            return false;\n        }\n\n        if(\n            placedBoxes.some(\n                placed=>\n                    boxesOverlap(\n                        candidateBox,\n                        placed,\n                        COLLISION.gap\n                    )\n            )\n        ){\n            return false;\n        }\n\n        return true;\n    }\n\n    function applyClusterTransform(\n        cluster,\n        base,\n        dx,\n        dy,\n        scale\n    ){\n        const els=\n            getVisibleElements(\n                cluster.elements\n            );\n\n        els.forEach(el=>{\n            el.setAttribute(\n                'transform',\n                [\n                    `translate(${dx} ${dy})`,\n                    `translate(${base.cx} ${base.cy})`,\n                    `scale(${scale})`,\n                    `translate(${-base.cx} ${-base.cy})`\n                ].join(' ')\n            );\n        });\n    }\n\n    function hideCluster(cluster){\n        cluster.elements.forEach(el=>{\n            if(!el) return;\n            el.style.opacity='0';\n        });\n    }\n\n    function solveRoomCollisions(room){\n        const clusters=\n            clusterDefinitions(room);\n\n        if(!clusters.length){\n            return;\n        }\n\n        const roomBox=\n            bbox(\n                room.getPoints()\n            );\n\n        const doorZones=\n            doorSafeZones(room);\n\n        const placedBoxes=[];\n\n        clusters.forEach(cluster=>{\n\n            const visible=\n                getVisibleElements(\n                    cluster.elements\n                );\n\n            if(!visible.length){\n                return;\n            }\n\n            const base=\n                unionBBox(visible);\n\n            if(!base){\n                return;\n            }\n\n            \/*\n             * Los elementos estructurales de cocina ya se\n             * generan pegados correctamente a sus paredes.\n             * Aun as\u00ed deben respetar puertas y l\u00edmites.\n             *\/\n            const minScale=\n                cluster.major\n                ? COLLISION.minScaleMajor\n                : COLLISION.minScaleMinor;\n\n            const scales=\n                cluster.fixed\n                ? [1,.94,.88,.82,.74,.66,.58]\n                : [1,.94,.88,.82,.76,.70,.64,.58,minScale];\n\n            let solved=null;\n\n            for(const scale of scales){\n\n                const candidates=\n                    candidatePlacements(\n                        base,\n                        roomBox,\n                        scale\n                    );\n\n                for(const candidate of candidates){\n\n                    if(\n                        validPlacement(\n                            room,\n                            candidate.box,\n                            placedBoxes,\n                            doorZones\n                        )\n                    ){\n                        solved={\n                            ...candidate,\n                            scale\n                        };\n\n                        break;\n                    }\n                }\n\n                if(solved){\n                    break;\n                }\n            }\n\n            if(solved){\n\n                applyClusterTransform(\n                    cluster,\n                    base,\n                    solved.dx,\n                    solved.dy,\n                    solved.scale\n                );\n\n                placedBoxes.push(\n                    solved.box\n                );\n\n            }else if(!cluster.major){\n\n                \/*\n                 * Decoraci\u00f3n: si no hay un sitio legal,\n                 * desaparece. Nunca se permite que invada\n                 * puerta, pared o un mueble importante.\n                 *\/\n                hideCluster(cluster);\n\n            }else{\n\n                \/*\n                 * \u00daltimo recurso para mobiliario principal:\n                 * seguimos reduciendo y buscando. La prioridad\n                 * es cumplir las reglas geom\u00e9tricas.\n                 *\/\n                const emergencyScales=[\n                    .50,.46,.42,.38\n                ];\n\n                outer:\n                for(const scale of emergencyScales){\n\n                    const candidates=\n                        candidatePlacements(\n                            base,\n                            roomBox,\n                            scale\n                        );\n\n                    for(const candidate of candidates){\n\n                        if(\n                            validPlacement(\n                                room,\n                                candidate.box,\n                                placedBoxes,\n                                doorZones\n                            )\n                        ){\n                            solved={\n                                ...candidate,\n                                scale\n                            };\n\n                            break outer;\n                        }\n                    }\n                }\n\n                if(solved){\n\n                    applyClusterTransform(\n                        cluster,\n                        base,\n                        solved.dx,\n                        solved.dy,\n                        solved.scale\n                    );\n\n                    placedBoxes.push(\n                        solved.box\n                    );\n\n                }else{\n\n                    \/*\n                     * Si matem\u00e1ticamente no existe hueco,\n                     * ocultamos el bloque antes que romper\n                     * cualquiera de las tres reglas sagradas.\n                     *\/\n                    hideCluster(cluster);\n                }\n            }\n        });\n    }\n\n    function solveAllFurnitureCollisions(){\n        rooms.forEach(room=>{\n            solveRoomCollisions(room);\n        });\n    }\n\n    \/* ========================================================\n       MUROS\n       ======================================================== *\/\n\n    const walls=[];\n\n    function crearMuro(getA,getB,exterior=false){\n        const line=crearSVG(\n            'line',\n            exterior ? 'elp-outer-wall' : 'elp-wall'\n        );\n\n        wallsGroup.appendChild(line);\n\n        walls.push({\n            getA,\n            getB,\n            exterior,\n            element:line\n        });\n    }\n\n    \/* Exterior *\/\n    crearMuro(()=>[BASE.left,BASE.top],()=>[BASE.right,BASE.top],true);\n    crearMuro(()=>[BASE.right,BASE.top],()=>[BASE.right,BASE.bottom],true);\n    crearMuro(()=>[BASE.right,BASE.bottom],()=>[state.x3,BASE.bottom],true);\n    crearMuro(()=>[state.x3,BASE.bottom],()=>[state.x3,690],true);\n    crearMuro(()=>[state.x3,690],()=>[state.x1,690],true);\n    crearMuro(()=>[state.x1,690],()=>[state.x1,BASE.bottom],true);\n    crearMuro(()=>[state.x1,BASE.bottom],()=>[BASE.left,BASE.bottom],true);\n    crearMuro(()=>[BASE.left,BASE.bottom],()=>[BASE.left,BASE.top],true);\n\n    \/* Verticales superiores *\/\n    crearMuro(()=>[state.x1,BASE.top],()=>[state.x1,state.y1]);\n    crearMuro(()=>[state.x2,BASE.top],()=>[state.x2,state.y1]);\n    crearMuro(()=>[state.x3,BASE.top],()=>[state.x3,state.y1]);\n\n    \/*\n     * MURO HORIZONTAL SUPERIOR CON 4 HUECOS REALES DE PUERTA.\n     * Hay 4 estancias arriba: dormitorio, dormitorio, ba\u00f1o,\n     * dormitorio derecho.\n     *\/\n    crearMuro(\n        ()=>[BASE.left,state.y1],\n        ()=>[state.x1-44,state.y1]\n    );\n\n    crearMuro(\n        ()=>[state.x1,state.y1],\n        ()=>[state.x2-44,state.y1]\n    );\n\n    crearMuro(\n        ()=>[state.x2,state.y1],\n        ()=>[state.x3-44,state.y1]\n    );\n\n    \/*\n     * Habitaci\u00f3n derecha:\n     * hueco propio dentro de SU tramo, no en el ba\u00f1o.\n     *\/\n    crearMuro(\n        ()=>[state.x3,state.y1],\n        ()=>[state.x3+24,state.y1]\n    );\n\n    crearMuro(\n        ()=>[state.x3+68,state.y1],\n        ()=>[BASE.right,state.y1]\n    );\n\n    \/* Central *\/\n    crearMuro(()=>[state.x2,state.y1],()=>[state.x2,385]);\n    crearMuro(()=>[state.x2,430],()=>[state.x2,state.y2]);\n\n    \/*\n     * Puerta del ba\u00f1o inferior:\n     * hueco homog\u00e9neo de 50 px en muro y2.\n     *\/\n    crearMuro(()=>[BASE.left,state.y2],()=>[285,state.y2]);\n    crearMuro(()=>[335,state.y2],()=>[state.x1,state.y2]);\n\n    crearMuro(()=>[state.x1,state.y2],()=>[state.x1,BASE.bottom]);\n\n    \/*\n     * Puerta de cocina:\n     * el tabique vertical x3 SE PARTE para que la puerta tenga\n     * un hueco real. Nunca vuelve a pasar una l\u00ednea por encima.\n     *\/\n    crearMuro(\n        ()=>[state.x3,state.y2],\n        ()=>[state.x3,state.y2+44]\n    );\n\n    crearMuro(\n        ()=>[state.x3,state.y2+92],\n        ()=>[state.x3,BASE.bottom]\n    );\n\n    crearMuro(()=>[state.x3,state.y2],()=>[BASE.right,state.y2]);\n\n    \/* ========================================================\n       PUERTAS\n       ======================================================== *\/\n\n    const doors=[];\n\n   function crearPuerta(\n    getX,\n    getY,\n    rotacion,\n    size=40,\n    orientacion='arriba-derecha'\n){\n    const groupDoor=crearSVG('g');\n\n    const hoja=crearSVG('line','elp-door');\n    const arco=crearSVG('path','elp-door');\n\n    let hojaX=0;\n    let hojaY=0;\n    let path='';\n\n    switch(orientacion){\n\n        case 'arriba-derecha':\n            hojaX=0;\n            hojaY=-size;\n\n            path=\n                `M 0 ${-size} A ${size} ${size} 0 0 1 ${size} 0`;\n        break;\n\n       case 'arriba-izquierda':\n\n    \/* Hoja abierta vertical hacia arriba *\/\n    hojaX=0;\n    hojaY=-size;\n\n    \/* Barrido desde la izquierda hasta la hoja vertical *\/\n    path=\n        `M ${-size} 0 A ${size} ${size} 0 0 1 0 ${-size}`;\n\nbreak;\n        case 'abajo-derecha':\n            hojaX=0;\n            hojaY=size;\n\n            path=\n                `M 0 ${size} A ${size} ${size} 0 0 0 ${size} 0`;\n        break;\n\n        case 'abajo-izquierda':\n            hojaX=0;\n            hojaY=size;\n\n            path=\n                `M 0 ${size} A ${size} ${size} 0 0 1 ${-size} 0`;\n        break;\n        \n        case 'izquierda-abajo':\n\n    hojaX=-size;\n    hojaY=0;\n\n    path=\n        `M ${-size} 0 A ${size} ${size} 0 0 0 0 ${size}`;\n\nbreak;\n    }\n\n    hoja.setAttribute('x1',0);\n    hoja.setAttribute('y1',0);\n    hoja.setAttribute('x2',hojaX);\n    hoja.setAttribute('y2',hojaY);\n\n    arco.setAttribute(\n        'd',\n        path\n    );\n\n    groupDoor.append(\n        hoja,\n        arco\n    );\n\n    doorsGroup.appendChild(\n        groupDoor\n    );\n\n    doors.push({\n        group:groupDoor,\n        getX,\n        getY,\n        rotacion\n    });\n}\n   \n    \/* PUERTA 1 \u2014 BISAGRA DERECHA *\/\ncrearPuerta(\n    ()=>state.x1,\n    ()=>state.y1,\n    0,\n    40,\n    'arriba-izquierda'\n);\n\n\n\/* PUERTA 2 \u2014 BISAGRA DERECHA *\/\ncrearPuerta(\n    ()=>state.x2,\n    ()=>state.y1,\n    0,\n    40,\n    'arriba-izquierda'\n);\n\n\n\/* PUERTA 3 \u2014 BISAGRA DERECHA *\/\ncrearPuerta(\n    ()=>state.x3,\n    ()=>state.y1,\n    0,\n    40,\n    'arriba-izquierda'\n);\n\n\n\/* PUERTA 4 \u2014 BISAGRA IZQUIERDA *\/\ncrearPuerta(\n    ()=>state.x3+28,\n    ()=>state.y1,\n    0,\n    40,\n    'arriba-derecha'\n);\n\n    crearPuerta(\n    ()=>state.x2,\n    ()=>385,\n    0,\n    40,\n    'izquierda-abajo'\n);\n\n    crearPuerta(\n    ()=>330,\n    ()=>state.y2,\n    0,\n    40,\n    'abajo-izquierda'\n);\n\n    crearPuerta(\n    ()=>state.x3,\n    ()=>state.y2+48,\n    90,\n    -40,\n    'abajo-izquierda'\n);\n\n    \/* ========================================================\n       VENTANAS\n       ======================================================== *\/\n\n    const windows=[];\n\n    function crearVentana(getX1,getY1,getX2,getY2){\n        const line1=crearSVG('line','elp-window');\n        const line2=crearSVG('line','elp-window');\n\n        windowsGroup.append(line1,line2);\n\n        windows.push({\n            line1,\n            line2,\n            getX1,\n            getY1,\n            getX2,\n            getY2\n        });\n    }\n\n    crearVentana(()=>215,()=>BASE.top+3,()=>310,()=>BASE.top+3);\n    crearVentana(()=>405,()=>BASE.top+3,()=>500,()=>BASE.top+3);\n    crearVentana(()=>790,()=>BASE.top+3,()=>920,()=>BASE.top+3);\n    crearVentana(()=>BASE.left+3,()=>330,()=>BASE.left+3,()=>420);\n    crearVentana(()=>BASE.right-3,()=>330,()=>BASE.right-3,()=>430);\n    crearVentana(()=>BASE.right-3,()=>525,()=>BASE.right-3,()=>605);\n\n    \/* ========================================================\n       NODOS\n       ======================================================== *\/\n\n    const nodeElements=[];\n\n    for(let i=0;i<18;i++){\n        const circle=crearSVG('circle','elp-node');\n        circle.setAttribute('r',2);\n        nodesGroup.appendChild(circle);\n        nodeElements.push(circle);\n    }\n\n    function nodePositions(){\n        return [\n            [BASE.left,BASE.top],\n            [state.x1,BASE.top],\n            [state.x2,BASE.top],\n            [state.x3,BASE.top],\n            [BASE.right,BASE.top],\n\n            [BASE.left,state.y1],\n            [state.x1,state.y1],\n            [state.x2,state.y1],\n            [state.x3,state.y1],\n            [BASE.right,state.y1],\n\n            [BASE.left,state.y2],\n            [state.x1,state.y2],\n            [state.x2,state.y2],\n            [state.x3,state.y2],\n            [BASE.right,state.y2],\n\n            [BASE.left,BASE.bottom],\n            [state.x1,BASE.bottom],\n            [BASE.right,BASE.bottom]\n        ];\n    }\n\n    \/* ========================================================\n       CURSOR LOCAL\n       ======================================================== *\/\n\n    const pointer={\n        x:-1000,\n        y:-1000,\n        targetX:-1000,\n        targetY:-1000,\n        active:false\n    };\n\n    function convertirCursor(clientX,clientY){\n        const point=svg.createSVGPoint();\n        point.x=clientX;\n        point.y=clientY;\n\n        const matrix=group.getScreenCTM();\n        if(!matrix) return;\n\n        const local=point.matrixTransform(matrix.inverse());\n\n        pointer.targetX=local.x;\n        pointer.targetY=local.y;\n\n        pointer.active =\n            local.x>BASE.left-120 &&\n            local.x<BASE.right+120 &&\n            local.y>BASE.top-120 &&\n            local.y<BASE.bottom+150;\n    }\n\n    function getActiveRoom(){\n        if(!pointer.active) return -1;\n\n        for(let i=0;i<rooms.length;i++){\n            if(\n                puntoDentro(\n                    pointer.x,\n                    pointer.y,\n                    rooms[i].getPoints()\n                )\n            ){\n                return i;\n            }\n        }\n\n        return -1;\n    }\n\n    \/* ========================================================\n       EXPANSI\u00d3N DE ESTANCIAS\n       ======================================================== *\/\n\n    function applyRoomExpansion(activeRoom){\n        if(activeRoom===-1) return;\n\n        const amount=CONFIG.roomExpansion;\n\n        \/*\n         * Ahora la habitaci\u00f3n activa cambia de tama\u00f1o de forma\n         * claramente perceptible. Las adyacentes ceden espacio.\n         *\/\n        switch(activeRoom){\n\n            \/* dormitorio superior izquierdo *\/\n            case 0:\n                state.targetX1 += amount*.95;\n                state.targetY1 += amount*.42;\n            break;\n\n            \/* dormitorio superior central *\/\n            case 1:\n                state.targetX1 -= amount*.55;\n                state.targetX2 += amount*.62;\n                state.targetY1 += amount*.42;\n            break;\n\n            \/* ba\u00f1o superior *\/\n            case 2:\n                state.targetX2 -= amount*.52;\n                state.targetX3 += amount*.54;\n                state.targetY1 += amount*.38;\n            break;\n\n            \/* dormitorio superior derecho *\/\n            case 3:\n                state.targetX3 -= amount*.92;\n                state.targetY1 += amount*.42;\n            break;\n\n            \/* estudio *\/\n            case 4:\n                state.targetX2 += amount*.66;\n                state.targetY1 -= amount*.52;\n                state.targetY2 += amount*.52;\n            break;\n\n            \/* sal\u00f3n *\/\n            case 5:\n                state.targetX2 -= amount*.62;\n                state.targetX3 += amount*.48;\n                state.targetY1 -= amount*.58;\n                state.targetY2 += amount*.52;\n            break;\n\n            \/* ba\u00f1o inferior *\/\n            case 6:\n                state.targetX1 += amount*.78;\n                state.targetY2 -= amount*.58;\n            break;\n\n            \/* terraza \/ comedor *\/\n            case 7:\n                state.targetX1 -= amount*.46;\n                state.targetX3 += amount*.46;\n                state.targetY2 -= amount*.62;\n            break;\n\n            \/* cocina *\/\n            case 8:\n                state.targetX3 -= amount*.78;\n                state.targetY2 -= amount*.62;\n            break;\n        }\n    }\n\n    \/* ========================================================\n       REPULSI\u00d3N DE MUROS\n       ======================================================== *\/\n\n    function applyWallRepulsion(){\n        if(!pointer.active) return;\n\n        const props=[\n            ['x1','targetX1','x',CONFIG.wallMovement],\n            ['x2','targetX2','x',CONFIG.wallMovement],\n            ['x3','targetX3','x',CONFIG.wallMovement],\n            ['y1','targetY1','y',CONFIG.wallMovement],\n            ['y2','targetY2','y',CONFIG.wallMovement]\n        ];\n\n        props.forEach(([key,targetKey,axis,movement])=>{\n            const pos=state[key];\n            const mouse=axis==='x' ? pointer.x : pointer.y;\n            const distance=Math.abs(mouse-pos);\n\n            if(distance<CONFIG.wallRadius){\n                let power=1-distance\/CONFIG.wallRadius;\n                power=power*power*(3-2*power);\n\n                const direction=mouse<pos ? 1 : -1;\n\n                state[targetKey] +=\n                    direction *\n                    movement *\n                    power;\n            }\n        });\n    }\n\n    function constrain(){\n        state.targetX1=Math.max(\n            LIMITS.x1min,\n            Math.min(LIMITS.x1max,state.targetX1)\n        );\n\n        state.targetX2=Math.max(\n            state.targetX1+LIMITS.minGapX,\n            Math.min(LIMITS.x2max,state.targetX2)\n        );\n\n        state.targetX3=Math.max(\n            state.targetX2+LIMITS.minGapX,\n            Math.min(LIMITS.x3max,state.targetX3)\n        );\n\n        state.targetY1=Math.max(\n            LIMITS.y1min,\n            Math.min(LIMITS.y1max,state.targetY1)\n        );\n\n        state.targetY2=Math.max(\n            state.targetY1+LIMITS.minGapY,\n            Math.min(LIMITS.y2max,state.targetY2)\n        );\n    }\n\n    \/* ========================================================\n       ACTUALIZACIONES VISUALES\n       ======================================================== *\/\n\n    function updateRooms(activeRoom){\n        rooms.forEach((room,index)=>{\n            const points=room.getPoints();\n\n            room.element.setAttribute(\n                'points',\n                points.map(p=>p[0]+','+p[1]).join(' ')\n            );\n\n            room.element.classList.toggle(\n                'active',\n                index===activeRoom\n            );\n        });\n    }\n\n    function updateWalls(){\n        walls.forEach(wall=>{\n            const a=wall.getA();\n            const b=wall.getB();\n\n            setLine(\n                wall.element,\n                a[0],a[1],\n                b[0],b[1]\n            );\n\n            if(pointer.active){\n                const nearest=distanciaSegmento(\n                    pointer.x,\n                    pointer.y,\n                    a[0],a[1],\n                    b[0],b[1]\n                );\n\n                wall.element.classList.toggle(\n                    'active',\n                    nearest.distance<65\n                );\n            }else{\n                wall.element.classList.remove('active');\n            }\n        });\n    }\n\n    function updateDoors(){\n        doors.forEach(door=>{\n            door.group.setAttribute(\n                'transform',\n                `translate(${door.getX()} ${door.getY()}) rotate(${door.rotacion})`\n            );\n        });\n    }\n\n    function updateWindows(){\n        windows.forEach(w=>{\n            const x1=w.getX1();\n            const y1=w.getY1();\n            const x2=w.getX2();\n            const y2=w.getY2();\n\n            setLine(w.line1,x1,y1,x2,y2);\n\n            const horizontal =\n                Math.abs(x2-x1) >\n                Math.abs(y2-y1);\n\n            setLine(\n                w.line2,\n                horizontal ? x1 : x1+5,\n                horizontal ? y1+5 : y1,\n                horizontal ? x2 : x2+5,\n                horizontal ? y2+5 : y2\n            );\n        });\n    }\n\n    function updateNodes(){\n        const positions=nodePositions();\n\n        nodeElements.forEach((circle,index)=>{\n            const p=positions[index];\n\n            if(!p){\n                circle.style.opacity=0;\n                return;\n            }\n\n            circle.style.opacity=.16;\n            circle.setAttribute('cx',p[0]);\n            circle.setAttribute('cy',p[1]);\n        });\n    }\n\n    function updatePointer(time){\n        if(!pointer.active){\n            pointerCircle.style.opacity=0;\n            pointerRing.style.opacity=0;\n            pointerGlow.style.opacity=0;\n            return;\n        }\n\n        [pointerCircle,pointerRing,pointerGlow].forEach(el=>{\n            el.setAttribute('cx',pointer.x);\n            el.setAttribute('cy',pointer.y);\n        });\n\n        pointerCircle.style.opacity=.92;\n        pointerRing.style.opacity=.4;\n        pointerGlow.style.opacity=.5;\n\n        pointerRing.setAttribute(\n            'r',\n            11+Math.sin(time*.004)*2\n        );\n    }\n\n    \/* ========================================================\n       POINTER GLOBAL\n       ======================================================== *\/\n\n    \/* ========================================================\n   POINTER GLOBAL \u00b7 PLANO + SEGUNDA SECCI\u00d3N\n   ======================================================== *\/\n\nfunction estaEnZonaCursor(event){\n\n    const elemento = document.elementFromPoint(\n        event.clientX,\n        event.clientY\n    );\n\n    if(!elemento){\n        return false;\n    }\n\n    \/*\n     * El cursor funciona:\n     * 1. en la secci\u00f3n original del plano\n     * 2. en cualquier secci\u00f3n con clase cursor-zone\n     *\/\n    return (\n        section.contains(elemento) ||\n        !!elemento.closest('.cursor-zone')\n    );\n}\n\n\nfunction actualizarCursorGlobal(event){\n\n    \/*\n     * Como ahora el cursor es FIXED,\n     * usamos directamente las coordenadas de pantalla.\n     *\/\n    globalTargetX = event.clientX;\n    globalTargetY = event.clientY;\n\n    if(!globalCursorActivo){\n\n        globalCursorX = globalTargetX;\n        globalCursorY = globalTargetY;\n\n        globalCursorActivo = true;\n    }\n\n    const dentro =\n        estaEnZonaCursor(event);\n\n    globalCursor.style.opacity =\n        dentro ? '1' : '0';\n\n\n    \/*\n     * La casa SOLO reacciona cuando estamos\n     * dentro de la primera secci\u00f3n.\n     *\/\n    const elemento = document.elementFromPoint(\n        event.clientX,\n        event.clientY\n    );\n\n    if(\n        elemento &&\n        section.contains(elemento)\n    ){\n\n        convertirCursor(\n            event.clientX,\n            event.clientY\n        );\n\n    }else{\n\n        \/*\n         * En la segunda secci\u00f3n sigue el punto,\n         * pero la casa deja de reaccionar.\n         *\/\n        pointer.active = false;\n    }\n}\n\n\n\/*\n * Escuchamos el rat\u00f3n en toda la p\u00e1gina,\n * no solamente dentro de la casa.\n *\/\ndocument.addEventListener(\n    'pointermove',\n    actualizarCursorGlobal,\n    {passive:true}\n);\n\ndocument.addEventListener(\n    'pointerdown',\n    actualizarCursorGlobal,\n    {passive:true}\n);\n\nlet scrollTimer;\n\nwindow.addEventListener(\n    'scroll',\n    function(){\n\n        globalCursor.style.opacity = '0';\n        pointer.active = false;\n\n        clearTimeout(scrollTimer);\n\n        scrollTimer = setTimeout(function(){\n\n            const fakeEvent = {\n                clientX: globalTargetX,\n                clientY: globalTargetY\n            };\n\n            const elemento = document.elementFromPoint(\n                globalTargetX,\n                globalTargetY\n            );\n\n            const dentro =\n                elemento &&\n                (\n                    section.contains(elemento) ||\n                    !!elemento.closest('.cursor-zone')\n                );\n\n            globalCursor.style.opacity =\n                dentro ? '1' : '0';\n\n        },120);\n\n    },\n    {passive:true}\n);\n    window.addEventListener(\n        'resize',\n        actualizarVistaPlano,\n        {passive:true}\n    );\n\n    \/* ========================================================\n       ANIMACI\u00d3N\n       ======================================================== *\/\n\n    function animate(time){\n\n        if(globalCursorActivo){\n            globalCursorX +=\n                (globalTargetX-globalCursorX)*.20;\n\n            globalCursorY +=\n                (globalTargetY-globalCursorY)*.20;\n\n            globalCursor.style.left=\n                globalCursorX+'px';\n\n            globalCursor.style.top=\n                globalCursorY+'px';\n        }\n\n        pointer.x +=\n            (pointer.targetX-pointer.x)*.18;\n\n        pointer.y +=\n            (pointer.targetY-pointer.y)*.18;\n\n        state.targetX1=ORIGINAL.x1;\n        state.targetX2=ORIGINAL.x2;\n        state.targetX3=ORIGINAL.x3;\n        state.targetY1=ORIGINAL.y1;\n        state.targetY2=ORIGINAL.y2;\n\n        const activeRoom=getActiveRoom();\n\n        applyRoomExpansion(activeRoom);\n        applyWallRepulsion();\n        constrain();\n\n        state.x1 +=\n            (state.targetX1-state.x1)*CONFIG.easing;\n\n        state.x2 +=\n            (state.targetX2-state.x2)*CONFIG.easing;\n\n        state.x3 +=\n            (state.targetX3-state.x3)*CONFIG.easing;\n\n        state.y1 +=\n            (state.targetY1-state.y1)*CONFIG.easing;\n\n        state.y2 +=\n            (state.targetY2-state.y2)*CONFIG.easing;\n\n        updateRooms(activeRoom);\n\n        \/*\n         * IMPORTANTE:\n         * el mobiliario se recalcula DESPU\u00c9S de mover los muros.\n         * Por eso sigue pegado a las paredes y puede girar.\n         *\/\n        updateFurniture(activeRoom);\n\n        \/*\n         * La distribuci\u00f3n ya se resuelve con reglas por estancia:\n         * muebles de pared en paredes, puertas libres y piezas\n         * centrales solo donde tiene sentido.\n         *\/\n        updateWalls();\n        updateDoors();\n        updateWindows();\n        updateNodes();\n        updatePointer(time);\n\n        requestAnimationFrame(animate);\n    }\n\n    requestAnimationFrame(animate);\n}\n\n\/* ============================================================\n   INICIO ELEMENTOR\n   ============================================================ *\/\n\nif(document.readyState==='loading'){\n    document.addEventListener(\n        'DOMContentLoaded',\n        ()=>setTimeout(iniciarPlanoEditeca,300)\n    );\n}else{\n    setTimeout(iniciarPlanoEditeca,300);\n}\n\n})();\n<\/script>\n\n<style>\n.edibim-franja{\n    box-sizing:border-box !important;\n    overflow:hidden !important;\n\n    padding-left:30px !important;\n    padding-right:30px !important;\n\n    border-top:none !important;\n    position:relative !important;\n}\n\n.edibim-franja::before{\n    content:\"\";\n    position:absolute;\n    top:0;\n    left:30px !important;\n    right:30px !important;\n    width:auto !important;\n    max-width:none !important;\n    height:1px;\n    background:rgba(255,255,255,.45);\n    pointer-events:none;\n}\n\n.edibim-franja > *{\n    min-width:0 !important;\n    flex-shrink:1 !important;\n    box-sizing:border-box !important;\n}\n<\/style>\n\n<script>\n(function(){\n\n    function ajustarFranjaEdibim(){\n\n        const franja =\n            document.querySelector('.edibim-franja');\n\n        const exterior =\n            document.querySelector('.editeca-live-plan');\n\n        if(!franja || !exterior) return;\n\n        \/*\n         * Limpiamos cualquier desplazamiento previo\n         *\/\n        franja.style.removeProperty('left');\n        franja.style.removeProperty('margin-left');\n        franja.style.removeProperty('margin-right');\n        franja.style.removeProperty('transform');\n\n        \/*\n         * Medimos el contenedor exterior real.\n         *\/\n        const exteriorRect =\n            exterior.getBoundingClientRect();\n\n        \/*\n         * Forzamos exactamente ese ancho.\n         *\/\n        franja.style.setProperty(\n            'width',\n            exteriorRect.width + 'px',\n            'important'\n        );\n\n        franja.style.setProperty(\n            'max-width',\n            'none',\n            'important'\n        );\n\n        \/*\n         * Medimos d\u00f3nde queda la franja despu\u00e9s\n         * de aplicar el ancho.\n         *\/\n        const franjaRect =\n            franja.getBoundingClientRect();\n\n        \/*\n         * La movemos horizontalmente hasta que\n         * su borde izquierdo coincida exactamente\n         * con el del contenedor exterior.\n         *\/\n        franja.style.setProperty(\n            'position',\n            'relative',\n            'important'\n        );\n\n        franja.style.setProperty(\n            'left',\n            (\n                exteriorRect.left -\n                franjaRect.left\n            ) + 'px',\n            'important'\n        );\n    }\n\n\n    \/*\n     * Ajuste inicial\n     *\/\n    if(document.readyState === 'loading'){\n\n        document.addEventListener(\n            'DOMContentLoaded',\n            function(){\n                setTimeout(\n                    ajustarFranjaEdibim,\n                    300\n                );\n            }\n        );\n\n    }else{\n\n        setTimeout(\n            ajustarFranjaEdibim,\n            300\n        );\n\n    }\n\n\n    \/*\n     * Si cambia el tama\u00f1o de pantalla,\n     * se recalcula autom\u00e1ticamente.\n     *\/\n    window.addEventListener(\n        'resize',\n        function(){\n            requestAnimationFrame(\n                ajustarFranjaEdibim\n            );\n        },\n        {passive:true}\n    );\n\n})();\n\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-d8bb03a elementor-widget elementor-widget-html\" data-id=\"d8bb03a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<style>\r\n\/* ==========================================================\r\n   EDIBIM \u00b7 CONTROL HERO M\u00d3VIL\r\n   SOLO <= 767px\r\n   ========================================================== *\/\r\n\r\n@media (max-width:767px){\r\n\r\n    \/* HERO *\/\r\n    #mcpbim{\r\n        position:relative !important;\r\n        display:flex !important;\r\n        flex-direction:column !important;\r\n\r\n        width:100% !important;\r\n        max-width:100% !important;\r\n        height:auto !important;\r\n        min-height:0 !important;\r\n\r\n        overflow:hidden !important;\r\n    }\r\n\r\n\/* =========================================================\r\n   EDIBIM \u00b7 FRANJA \u00b7 M\u00d3VIL\r\n   ========================================================= *\/\r\n\r\n#mcpbim .edibim-franja{\r\n    display:grid !important;\r\n    grid-template-columns:1fr 1fr !important;\r\n    grid-template-rows:auto auto !important;\r\n\r\n    column-gap:20px !important;\r\n    row-gap:28px !important;\r\n\r\n    width:100% !important;\r\n    max-width:100% !important;\r\n    height:auto !important;\r\n    min-height:0 !important;\r\n\r\n    left:auto !important;\r\n    right:auto !important;\r\n\r\n    padding:30px 20px 28px !important;\r\n    box-sizing:border-box !important;\r\n}\r\n\r\n\r\n\/* TEXTO ARRIBA \u2014 TODO EL ANCHO *\/\r\n\r\n#mcpbim .edibim-franja > :nth-child(2){\r\n    grid-column:1 \/ -1 !important;\r\n    grid-row:1 !important;\r\n\r\n    width:100% !important;\r\n    max-width:100% !important;\r\n    height:auto !important;\r\n    min-height:0 !important;\r\n\r\n    margin:0 !important;\r\n    padding:0 !important;\r\n    transform:none !important;\r\n}\r\n\r\n#mcpbim .edibim-franja > :nth-child(2) p{\r\n    width:100% !important;\r\n    max-width:100% !important;\r\n    margin:0 !important;\r\n\r\n    text-align:left !important;\r\n    white-space:normal !important;\r\n    overflow-wrap:break-word !important;\r\n}\r\n\r\n\r\n\/* BOT\u00d3N ABAJO IZQUIERDA *\/\r\n\r\n#mcpbim .edibim-franja > :nth-child(1){\r\n    grid-column:1 !important;\r\n    grid-row:2 !important;\r\n\r\n    width:100% !important;\r\n    height:auto !important;\r\n    min-height:0 !important;\r\n\r\n    margin:0 !important;\r\n    padding:0 !important;\r\n    transform:none !important;\r\n\r\n    justify-content:flex-start !important;\r\n}\r\n\r\n\r\n\/* LOGO ABAJO DERECHA *\/\r\n\r\n#mcpbim .edibim-franja > :nth-child(3){\r\n    grid-column:2 !important;\r\n    grid-row:2 !important;\r\n\r\n    width:100% !important;\r\n    height:auto !important;\r\n    min-height:0 !important;\r\n\r\n    margin:0 !important;\r\n    padding:0 !important;\r\n    transform:none !important;\r\n\r\n    justify-content:flex-end !important;\r\n    align-items:center !important;\r\n}\r\n\r\n#mcpbim .edibim-franja > :nth-child(3) img{\r\n    width:58px !important;\r\n    max-width:58px !important;\r\n    height:auto !important;\r\n    margin-left:auto !important;\r\n}\r\n\r\n    \/* CONTENEDOR PRINCIPAL *\/\r\n    #mcpbim .edibim-hero-contenido{\r\n        position:relative !important;\r\n\r\n        display:flex !important;\r\n        flex-direction:column !important;\r\n        align-items:stretch !important;\r\n\r\n        width:100% !important;\r\n        max-width:100% !important;\r\n        min-width:0 !important;\r\n\r\n        height:auto !important;\r\n        min-height:0 !important;\r\n\r\n        margin:0 !important;\r\n        padding:0 20px 40px !important;\r\n\r\n        transform:none !important;\r\n        box-sizing:border-box !important;\r\n        overflow:visible !important;\r\n\r\n        z-index:5 !important;\r\n    }\r\n\r\n\r\n    \/* ==============================\r\n       EDIBIM PARA REVIT\r\n       ============================== *\/\r\n\r\n    #mcpbim .edibim-hero-titulo{\r\n        position:absolute !important;\r\n\r\n        top:35px !important;\r\n        left:20px !important;\r\n        right:auto !important;\r\n\r\n        width:210px !important;\r\n        max-width:60vw !important;\r\n\r\n        height:auto !important;\r\n        min-height:0 !important;\r\n\r\n        margin:0 !important;\r\n        padding:0 !important;\r\n\r\n        transform:none !important;\r\n\r\n        text-align:left !important;\r\n\r\n        z-index:20 !important;\r\n    }\r\n\r\n    #mcpbim .edibim-hero-titulo h1{\r\n        position:relative !important;\r\n\r\n        top:auto !important;\r\n        left:auto !important;\r\n        right:auto !important;\r\n        bottom:auto !important;\r\n\r\n        width:100% !important;\r\n        max-width:100% !important;\r\n        height:auto !important;\r\n\r\n        margin:0 !important;\r\n\r\n        transform:none !important;\r\n\r\n        color:#fff !important;\r\n        -webkit-text-fill-color:#fff !important;\r\n\r\n        text-align:left !important;\r\n    }\r\n\r\n\r\n    \/* ==============================\r\n       TU CRITERIO BIM...\r\n       ============================== *\/\r\n\r\n    #mcpbim .edibim-hero-claim{\r\n        position:absolute !important;\r\n\r\n        top:330px !important;\r\n        right:20px !important;\r\n        left:auto !important;\r\n        bottom:auto !important;\r\n\r\n        width:220px !important;\r\n        max-width:68vw !important;\r\n\r\n        height:auto !important;\r\n        min-height:0 !important;\r\n\r\n        margin:0 !important;\r\n        padding:0 !important;\r\n\r\n        transform:none !important;\r\n\r\n        text-align:right !important;\r\n\r\n        z-index:20 !important;\r\n    }\r\n\r\n    #mcpbim .edibim-hero-claim *{\r\n        text-align:right !important;\r\n    }\r\n\r\n\r\n    \/* ==============================\r\n       COPY\r\n       Los dos p\u00e1rrafos\r\n       ============================== *\/\r\n\r\n    #mcpbim .edibim-hero-copy{\r\n        position:relative !important;\r\n\r\n        width:100% !important;\r\n        max-width:100% !important;\r\n        min-width:0 !important;\r\n\r\n        height:auto !important;\r\n        min-height:0 !important;\r\n\r\n        margin:455px 0 0 !important;\r\n        padding:0 !important;\r\n\r\n        transform:none !important;\r\n\r\n        box-sizing:border-box !important;\r\n        overflow:visible !important;\r\n\r\n        z-index:10 !important;\r\n    }\r\n\r\n    #mcpbim .edibim-hero-copy p{\r\n        position:relative !important;\r\n\r\n        width:100% !important;\r\n        max-width:100% !important;\r\n\r\n        height:auto !important;\r\n\r\n        text-align:left !important;\r\n        text-align-last:left !important;\r\n\r\n        white-space:normal !important;\r\n        word-break:normal !important;\r\n        overflow-wrap:break-word !important;\r\n    }\r\n\r\n\r\n    \/* ==============================\r\n       BOTONES\r\n       ============================== *\/\r\n\r\n    #mcpbim .edibim-hero-botones{\r\n        position:relative !important;\r\n\r\n        display:flex !important;\r\n        flex-direction:column !important;\r\n        align-items:stretch !important;\r\n\r\n        width:100% !important;\r\n        max-width:100% !important;\r\n        min-width:0 !important;\r\n\r\n        gap:12px !important;\r\n\r\n        margin:24px 0 0 !important;\r\n        padding:0 !important;\r\n\r\n        transform:none !important;\r\n\r\n        box-sizing:border-box !important;\r\n\r\n        z-index:10 !important;\r\n    }\r\n\r\n    #mcpbim .edibim-hero-botones > *{\r\n        width:100% !important;\r\n        max-width:100% !important;\r\n        min-width:0 !important;\r\n\r\n        margin:0 !important;\r\n    }\r\n\r\n    #mcpbim .edibim-hero-botones a{\r\n        width:100% !important;\r\n        max-width:100% !important;\r\n\r\n        box-sizing:border-box !important;\r\n        text-align:center !important;\r\n    }\r\n\r\n\r\n    \/* ==============================\r\n       CAPA DEL PLANO\r\n       ============================== *\/\r\n\r\n    #mcpbim #editeca-live-plan-svg{\r\n        position:absolute !important;\r\n\r\n        inset:auto !important;\r\n\r\n        left:0 !important;\r\n        top:0 !important;\r\n\r\n        width:100% !important;\r\n        height:100% !important;\r\n\r\n        max-width:none !important;\r\n\r\n        pointer-events:none !important;\r\n\r\n        z-index:1 !important;\r\n    }\r\n\r\n\r\n    \/* FRANJA *\/\r\n    #mcpbim .edibim-franja{\r\n        position:relative !important;\r\n\r\n        left:auto !important;\r\n        right:auto !important;\r\n\r\n        width:100% !important;\r\n        max-width:100% !important;\r\n\r\n        margin:0 !important;\r\n\r\n        transform:none !important;\r\n\r\n        box-sizing:border-box !important;\r\n\r\n        z-index:5 !important;\r\n    }\r\n}\r\n<\/style>\r\n\r\n\r\n<script>\r\n(function(){\r\n\r\n    const mq = window.matchMedia('(max-width:767px)');\r\n\r\n    \/*\r\n     * Valores ORIGINALES del plano de escritorio.\r\n     * Los restauramos cuando dejamos m\u00f3vil.\r\n     *\/\r\n    const DESKTOP = {\r\n        scale:0.80,\r\n        x:100,\r\n        y:-90\r\n    };\r\n\r\n\r\n    function aplicarMovil(){\r\n\r\n        if(!mq.matches) return;\r\n\r\n        const titulo =\r\n            document.querySelector(\r\n                '#mcpbim .edibim-hero-titulo'\r\n            );\r\n\r\n        const claim =\r\n            document.querySelector(\r\n                '#mcpbim .edibim-hero-claim'\r\n            );\r\n\r\n        const center =\r\n            document.querySelector(\r\n                '#mcpbim #PLAN_CENTER'\r\n            );\r\n\r\n        const planA =\r\n            document.querySelector(\r\n                '#mcpbim #PLAN_A'\r\n            );\r\n\r\n        if(\r\n            !titulo ||\r\n            !claim ||\r\n            !center ||\r\n            !planA\r\n        ){\r\n            return false;\r\n        }\r\n\r\n\r\n        \/*\r\n         * TAMA\u00d1O DEL PLANO EN M\u00d3VIL.\r\n         *\r\n         * Lo dejamos algo mayor que en escritorio.\r\n         * M\u00e1s adelante afinaremos este \u00fanico valor.\r\n         *\/\r\n        const escala = 1.20;\r\n\r\n        planA.setAttribute(\r\n            'transform',\r\n            `scale(${escala}) translate(-582.5 -395)`\r\n        );\r\n\r\n\r\n        \/*\r\n         * Primero colocamos PLAN_CENTER en una\r\n         * posici\u00f3n conocida para poder medirlo.\r\n         *\/\r\n        center.style.removeProperty('transform');\r\n\r\n        center.setAttribute(\r\n            'transform',\r\n            'translate(700 360)'\r\n        );\r\n\r\n\r\n        \/*\r\n         * Calculamos autom\u00e1ticamente el espacio\r\n         * disponible entre:\r\n         *\r\n         * EDIBIM PARA REVIT\r\n         *       \u2193\r\n         *     PLANO\r\n         *       \u2193\r\n         * TU CRITERIO BIM...\r\n         *\/\r\n        const rt =\r\n            titulo.getBoundingClientRect();\r\n\r\n        const rc =\r\n            claim.getBoundingClientRect();\r\n\r\n        const rp =\r\n            center.getBoundingClientRect();\r\n\r\n\r\n        const inicioHueco =\r\n            rt.bottom + 20;\r\n\r\n        const finHueco =\r\n            rc.top - 20;\r\n\r\n        const centroDeseado =\r\n            (\r\n                inicioHueco +\r\n                finHueco\r\n            ) \/ 2;\r\n\r\n        const centroActual =\r\n            rp.top +\r\n            rp.height \/ 2;\r\n\r\n\r\n        \/*\r\n         * Averiguamos cu\u00e1ntos p\u00edxeles reales\r\n         * representa una unidad Y del SVG.\r\n         *\/\r\n        center.setAttribute(\r\n            'transform',\r\n            'translate(700 361)'\r\n        );\r\n\r\n        const prueba =\r\n            center.getBoundingClientRect();\r\n\r\n        const pxPorUnidad =\r\n            prueba.top - rp.top;\r\n\r\n\r\n        if(\r\n            Number.isFinite(pxPorUnidad) &&\r\n            Math.abs(pxPorUnidad) > 0.001\r\n        ){\r\n\r\n            const desplazamiento =\r\n                centroDeseado -\r\n                centroActual;\r\n\r\n            const nuevaY =\r\n                360 +\r\n                (\r\n                    desplazamiento \/\r\n                    pxPorUnidad\r\n                );\r\n\r\n            center.setAttribute(\r\n    'transform',\r\n    `translate(611.0550152931356 ${nuevaY + 20})`\r\n);\r\n        }\r\n\r\n        return true;\r\n    }\r\n\r\n\r\n    function restaurarDesktop(){\r\n\r\n        if(mq.matches) return;\r\n\r\n        \/*\r\n         * Utilizamos la API que ya contiene\r\n         * el HTML original.\r\n         *\/\r\n        if(\r\n            window.editecaPlano &&\r\n            typeof window.editecaPlano.setView === 'function'\r\n        ){\r\n            window.editecaPlano.setView(\r\n                DESKTOP.scale,\r\n                DESKTOP.x,\r\n                DESKTOP.y\r\n            );\r\n        }\r\n    }\r\n\r\n\r\n    function iniciar(){\r\n\r\n        let intentos = 0;\r\n\r\n        const timer =\r\n            setInterval(function(){\r\n\r\n                intentos++;\r\n\r\n                if(mq.matches){\r\n\r\n                    if(aplicarMovil()){\r\n                        clearInterval(timer);\r\n                    }\r\n\r\n                }else{\r\n\r\n                    restaurarDesktop();\r\n                    clearInterval(timer);\r\n                }\r\n\r\n\r\n                if(intentos >= 50){\r\n                    clearInterval(timer);\r\n                }\r\n\r\n            },100);\r\n    }\r\n\r\n\r\n    if(document.readyState === 'loading'){\r\n\r\n        document.addEventListener(\r\n            'DOMContentLoaded',\r\n            iniciar\r\n        );\r\n\r\n    }else{\r\n\r\n        iniciar();\r\n    }\r\n\r\n\r\n    \/*\r\n     * Cambio m\u00f3vil \u2194 escritorio.\r\n     *\/\r\n    mq.addEventListener(\r\n        'change',\r\n        function(){\r\n\r\n            if(mq.matches){\r\n\r\n                setTimeout(\r\n                    aplicarMovil,\r\n                    100\r\n                );\r\n\r\n            }else{\r\n\r\n                restaurarDesktop();\r\n            }\r\n\r\n        }\r\n    );\r\n\r\n\r\n    \/*\r\n     * Cambio de anchura\/orientaci\u00f3n m\u00f3vil.\r\n     *\/\r\n    window.addEventListener(\r\n        'resize',\r\n        function(){\r\n\r\n            if(mq.matches){\r\n\r\n                requestAnimationFrame(\r\n                    aplicarMovil\r\n                );\r\n            }\r\n\r\n        },\r\n        {passive:true}\r\n    );\r\n\r\n})();\r\n<\/script>\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-18c78ba e-con e-atomic-element e-flexbox-base e-18c78ba-3bd0275 edibim-franja \" data-id=\"18c78ba\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"18c78ba\">\n    <div class=\"elementor-element elementor-element-46afdc5 e-con e-atomic-element e-flexbox-base \" data-id=\"46afdc5\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"46afdc5\">\n    \t\t\t<button class=\"e-950675f-09bf48a edibim-open-contact e-button-base\" data-interaction-id=\"950675f\">\n\t\tCONT\u00c1CTANOS\n\t<\/button>\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-301146b e-con e-atomic-element e-flexbox-base e-301146b-a06401a \" data-id=\"301146b\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"301146b\">\n    \t\t<div class=\"elementor-element elementor-element-b4fb788 elementor-widget elementor-widget-text-editor\" data-id=\"b4fb788\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p><span style=\"font-weight: 400;\">EdiBIM para Revit operativo \u00b7 Desarrollado por Editeca<\/span><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-ea005fc e-con e-atomic-element e-flexbox-base e-ea005fc-b619555 \" data-id=\"ea005fc\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"ea005fc\">\n    \t\t<div class=\"elementor-element elementor-element-9054428 elementor-widget elementor-widget-image\" data-id=\"9054428\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" width=\"1\" height=\"1\" src=\"https:\/\/mcpbim.com\/wp-content\/uploads\/2026\/09\/logo-en-blanco-edibim.svg\" class=\"attachment-large size-large wp-image-1661\" alt=\"\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n\n<\/div>\n\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-d2372dd e-con e-atomic-element e-flexbox-base e-3e10afe edibim-reveal-v2 \" data-id=\"d2372dd\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"d2372dd\">\n    <div class=\"elementor-element elementor-element-ebaae27 e-con e-atomic-element e-flexbox-base e-d75c939 edibim-accion-textos \" data-id=\"ebaae27\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"ebaae27\" id=\"comofunciona\">\n    <div class=\"elementor-element elementor-element-1fab674 e-con e-atomic-element e-flexbox-base e-1fab674-458a342 \" data-id=\"1fab674\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"1fab674\">\n    \t\t<div class=\"elementor-element elementor-element-35cb09a elementor-widget elementor-widget-text-editor\" data-id=\"35cb09a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>EDIBIM EN ACCI\u00d3N<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-7ff6ffb elementor-widget elementor-widget-text-editor\" data-id=\"7ff6ffb\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h2 class=\"edibim-title\"><strong>IA QUE ACT\u00daA<\/strong><br \/><strong>DENTRO DE REVIT<\/strong><\/h2>\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n\t\t<div class=\"elementor-element elementor-element-7a82878 elementor-widget elementor-widget-text-editor\" data-id=\"7a82878\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>EdiBIM es la familia de herramientas que Editeca desarrolla para llevar la inteligencia artificial al trabajo real dentro de las aplicaciones BIM. EdiBIM para Revit es la primera y ya est\u00e1 operativa.<\/p><p>Es el puente \u2014t\u00e9cnicamente, un MCP\u2014 que permite a un asistente de IA consultar el modelo abierto, crear o modificar elementos y generar documentaci\u00f3n dentro de Revit.<\/p><p>En Revit + IA aprendes a definir la tarea, aportar el contexto, supervisar la ejecuci\u00f3n y validar el resultado. As\u00ed reduces selecciones, comprobaciones y repeticiones manuales sin delegar tu criterio profesional.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-2df61e2 e-con e-atomic-element e-flexbox-base e-a47c99f \" data-id=\"2df61e2\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"2df61e2\">\n    \t\t<div class=\"elementor-element elementor-element-37892e3 elementor-widget__width-initial elementor-widget elementor-widget-html\" data-id=\"37892e3\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- =========================================================\r\n     EDIBIM \u00b7 V\u00cdDEO \u00b7 EDIBIM EN ACCI\u00d3N \u00b7 02\r\n     COMPONENTE COMPLETAMENTE AISLADO\r\n     ========================================================= -->\r\n\r\n<div id=\"edibim-video-accion-02\" class=\"edibim-video-accion-component\">\r\n\r\n    <!-- TARJETA -->\r\n    <div class=\"edibim-showcase\">\r\n\r\n        <article\r\n            class=\"edibim-media-card\"\r\n            data-title=\"VER EL CASO EN ACCI\u00d3N\"\r\n            data-video=\"https:\/\/mcpbim.com\/wp-content\/uploads\/2026\/09\/Mi-video.mp4\"\r\n            tabindex=\"0\"\r\n            role=\"button\"\r\n            aria-label=\"Abrir v\u00eddeo\"\r\n        >\r\n\r\n            <!-- MINIATURA ANIMADA -->\r\n            <video\r\n                class=\"edibim-media-preview\"\r\n                autoplay\r\n                muted\r\n                loop\r\n                playsinline\r\n                preload=\"auto\"\r\n            ><\/video>\r\n\r\n            <!-- SOMBRA HOVER -->\r\n            <div class=\"edibim-card-shade\"><\/div>\r\n\r\n            <!-- T\u00cdTULO -->\r\n            <div class=\"edibim-card-title\">\r\n                VER EL CASO EN ACCI\u00d3N\r\n            <\/div>\r\n\r\n            <!-- ESQUINAS -->\r\n            <span class=\"edibim-corner tl\"><\/span>\r\n            <span class=\"edibim-corner tr\"><\/span>\r\n            <span class=\"edibim-corner bl\"><\/span>\r\n            <span class=\"edibim-corner br\"><\/span>\r\n\r\n        <\/article>\r\n\r\n    <\/div>\r\n\r\n\r\n    <!-- POPUP PROPIO -->\r\n    <div class=\"edibim-modal\" aria-hidden=\"true\">\r\n\r\n        <div class=\"edibim-modal-backdrop\"><\/div>\r\n\r\n        <div class=\"edibim-modal-window\">\r\n\r\n            <button\r\n                class=\"edibim-modal-close\"\r\n                type=\"button\"\r\n                aria-label=\"Cerrar v\u00eddeo\"\r\n            >\r\n                \u00d7\r\n            <\/button>\r\n\r\n            <div class=\"edibim-modal-label\">\r\n                EDIBIM\r\n            <\/div>\r\n\r\n            <div class=\"edibim-video-holder\"><\/div>\r\n\r\n        <\/div>\r\n\r\n    <\/div>\r\n\r\n<\/div>\r\n\r\n\r\n<style>\r\n\r\n\/* =========================================================\r\n   EDIBIM EN ACCI\u00d3N \u00b7 V\u00cdDEO 02 \u00b7 AISLADO\r\n   ========================================================= *\/\r\n\r\n\r\n\/* CONTENEDOR *\/\r\n\r\n#edibim-video-accion-02 .edibim-showcase{\r\n    width:100%;\r\n    display:grid;\r\n    grid-template-columns:repeat(1,minmax(0,1fr));\r\n    gap:28px;\r\n}\r\n\r\n\r\n\/* TARJETA *\/\r\n\r\n#edibim-video-accion-02 .edibim-media-card{\r\n    position:relative;\r\n    width:100%;\r\n    aspect-ratio:16 \/ 8.5;\r\n    background:#171717;\r\n    overflow:hidden;\r\n    cursor:pointer;\r\n    isolation:isolate;\r\n\r\n    border:\r\n        1px solid\r\n        rgba(255,255,255,.035);\r\n\r\n    transition:\r\n        background .45s ease,\r\n        transform .45s cubic-bezier(.2,.75,.25,1);\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   MINIATURA\r\n   ========================================================= *\/\r\n\r\n#edibim-video-accion-02 .edibim-media-preview{\r\n    position:absolute;\r\n\r\n    top:18px;\r\n    left:18px;\r\n\r\n    width:42%;\r\n    height:45%;\r\n\r\n    background-position:center;\r\n    background-size:cover;\r\n    background-repeat:no-repeat;\r\n\r\n    object-fit:cover;\r\n    display:block;\r\n\r\n    filter:\r\n        grayscale(100%)\r\n        contrast(1.05)\r\n        brightness(.78);\r\n\r\n    transform:scale(1);\r\n    transform-origin:left top;\r\n\r\n    transition:\r\n        top .6s cubic-bezier(.2,.8,.2,1),\r\n        left .6s cubic-bezier(.2,.8,.2,1),\r\n        width .6s cubic-bezier(.2,.8,.2,1),\r\n        height .6s cubic-bezier(.2,.8,.2,1),\r\n        filter .6s ease,\r\n        transform .8s cubic-bezier(.2,.8,.2,1);\r\n\r\n    z-index:1;\r\n}\r\n\r\n\r\n\/* HOVER *\/\r\n\r\n#edibim-video-accion-02 .edibim-media-card:hover\r\n.edibim-media-preview,\r\n\r\n#edibim-video-accion-02 .edibim-media-card:focus-visible\r\n.edibim-media-preview{\r\n    top:0;\r\n    left:0;\r\n\r\n    width:100%;\r\n    height:100%;\r\n\r\n    filter:\r\n        grayscale(0%)\r\n        contrast(1.02)\r\n        brightness(.72);\r\n\r\n    transform:scale(1.035);\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   SOMBRA\r\n   ========================================================= *\/\r\n\r\n#edibim-video-accion-02 .edibim-card-shade{\r\n    position:absolute;\r\n    inset:0;\r\n\r\n    background:\r\n        linear-gradient(\r\n            to top,\r\n            rgba(10,10,10,.76) 0%,\r\n            rgba(10,10,10,.20) 48%,\r\n            rgba(10,10,10,.05) 100%\r\n        );\r\n\r\n    opacity:0;\r\n\r\n    transition:opacity .45s ease;\r\n\r\n    z-index:2;\r\n}\r\n\r\n\r\n#edibim-video-accion-02 .edibim-media-card:hover\r\n.edibim-card-shade,\r\n\r\n#edibim-video-accion-02 .edibim-media-card:focus-visible\r\n.edibim-card-shade{\r\n    opacity:1;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   T\u00cdTULO\r\n   ========================================================= *\/\r\n\r\n#edibim-video-accion-02 .edibim-card-title{\r\n    position:absolute;\r\n\r\n    left:18px;\r\n    bottom:18px;\r\n\r\n    font-family:inherit;\r\n    font-size:16px;\r\n    font-weight:500;\r\n    line-height:1;\r\n\r\n    color:#F26B3A;\r\n\r\n    z-index:4;\r\n\r\n    transition:\r\n        transform .45s ease,\r\n        color .45s ease;\r\n}\r\n\r\n\r\n#edibim-video-accion-02 .edibim-media-card:hover\r\n.edibim-card-title{\r\n    transform:translateY(-2px);\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   ESQUINAS\r\n   ========================================================= *\/\r\n\r\n#edibim-video-accion-02 .edibim-corner{\r\n    position:absolute;\r\n\r\n    width:14px;\r\n    height:14px;\r\n\r\n    z-index:5;\r\n\r\n    opacity:.55;\r\n\r\n    transition:\r\n        opacity .4s ease,\r\n        width .4s ease,\r\n        height .4s ease;\r\n}\r\n\r\n\r\n#edibim-video-accion-02 .edibim-corner.tl{\r\n    top:0;\r\n    left:0;\r\n    border-top:1px solid #F26B3A;\r\n    border-left:1px solid #F26B3A;\r\n}\r\n\r\n\r\n#edibim-video-accion-02 .edibim-corner.tr{\r\n    top:0;\r\n    right:0;\r\n    border-top:1px solid #F26B3A;\r\n    border-right:1px solid #F26B3A;\r\n}\r\n\r\n\r\n#edibim-video-accion-02 .edibim-corner.bl{\r\n    bottom:0;\r\n    left:0;\r\n    border-bottom:1px solid #F26B3A;\r\n    border-left:1px solid #F26B3A;\r\n}\r\n\r\n\r\n#edibim-video-accion-02 .edibim-corner.br{\r\n    bottom:0;\r\n    right:0;\r\n    border-bottom:1px solid #F26B3A;\r\n    border-right:1px solid #F26B3A;\r\n}\r\n\r\n\r\n#edibim-video-accion-02 .edibim-media-card:hover\r\n.edibim-corner{\r\n    width:20px;\r\n    height:20px;\r\n    opacity:1;\r\n}\r\n\r\n\r\n\/* ACCESIBILIDAD *\/\r\n\r\n#edibim-video-accion-02 .edibim-media-card:focus{\r\n    outline:none;\r\n}\r\n\r\n\r\n#edibim-video-accion-02 .edibim-media-card:focus-visible{\r\n    outline:\r\n        1px solid\r\n        rgba(242,107,58,.8);\r\n\r\n    outline-offset:4px;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   POPUP\r\n   ========================================================= *\/\r\n\r\n.edibim-modal-accion-02{\r\n    position:fixed !important;\r\n\r\n    inset:0 !important;\r\n\r\n    width:100vw !important;\r\n    height:100vh !important;\r\n    height:100dvh !important;\r\n\r\n    z-index:2147483000 !important;\r\n\r\n    display:flex;\r\n\r\n    align-items:center;\r\n    justify-content:center;\r\n\r\n    padding:40px;\r\n\r\n    box-sizing:border-box;\r\n\r\n    opacity:0;\r\n    visibility:hidden;\r\n    pointer-events:none;\r\n\r\n    transition:\r\n        opacity .35s ease,\r\n        visibility .35s ease;\r\n}\r\n\r\n\r\n.edibim-modal-accion-02.is-open{\r\n    opacity:1;\r\n    visibility:visible;\r\n    pointer-events:auto;\r\n}\r\n\r\n\r\n\/* FONDO *\/\r\n\r\n.edibim-modal-accion-02 .edibim-modal-backdrop{\r\n    position:absolute;\r\n    inset:0;\r\n\r\n    background:rgba(8,8,8,.88);\r\n\r\n    backdrop-filter:blur(8px);\r\n    -webkit-backdrop-filter:blur(8px);\r\n}\r\n\r\n\r\n\/* VENTANA *\/\r\n\r\n.edibim-modal-accion-02 .edibim-modal-window{\r\n    position:relative;\r\n\r\n    width:min(1280px,92vw);\r\n\r\n    background:#101010;\r\n\r\n    z-index:2;\r\n\r\n    transform:\r\n        translateY(25px)\r\n        scale(.97);\r\n\r\n    opacity:0;\r\n\r\n    transition:\r\n        transform .45s cubic-bezier(.2,.8,.2,1),\r\n        opacity .35s ease;\r\n}\r\n\r\n\r\n.edibim-modal-accion-02.is-open\r\n.edibim-modal-window{\r\n    transform:\r\n        translateY(0)\r\n        scale(1);\r\n\r\n    opacity:1;\r\n}\r\n\r\n\r\n\/* T\u00cdTULO POPUP *\/\r\n\r\n.edibim-modal-accion-02 .edibim-modal-label{\r\n    position:absolute;\r\n\r\n    left:24px;\r\n    top:22px;\r\n\r\n    z-index:4;\r\n\r\n    font-family:inherit;\r\n\r\n    color:#F26B3A;\r\n\r\n    font-size:13px;\r\n    font-weight:600;\r\n\r\n    letter-spacing:.04em;\r\n}\r\n\r\n\r\n\/* BOT\u00d3N CERRAR *\/\r\n\r\n.edibim-modal-accion-02 .edibim-modal-close{\r\n    position:absolute;\r\n\r\n    top:15px;\r\n    right:18px;\r\n\r\n    z-index:10;\r\n\r\n    width:42px;\r\n    height:42px;\r\n\r\n    display:flex;\r\n    justify-content:center;\r\n    align-items:center;\r\n\r\n    border:0;\r\n\r\n    background:rgba(15,15,15,.75);\r\n\r\n    color:#fff;\r\n\r\n    font-size:31px;\r\n    font-weight:200;\r\n    line-height:1;\r\n\r\n    cursor:pointer;\r\n\r\n    transition:\r\n        background .25s ease,\r\n        transform .25s ease;\r\n}\r\n\r\n\r\n.edibim-modal-accion-02 .edibim-modal-close:hover{\r\n    background:#F26B3A;\r\n    transform:rotate(4deg);\r\n}\r\n\r\n\r\n\/* REPRODUCTOR *\/\r\n\r\n.edibim-modal-accion-02 .edibim-video-holder{\r\n    width:100%;\r\n    aspect-ratio:16 \/ 9;\r\n    background:#000;\r\n}\r\n\r\n\r\n.edibim-modal-accion-02 .edibim-video-holder iframe,\r\n.edibim-modal-accion-02 .edibim-video-holder video{\r\n    width:100%;\r\n    height:100%;\r\n\r\n    display:block;\r\n\r\n    border:0;\r\n\r\n    object-fit:contain;\r\n}\r\n\r\n\r\n\/* BLOQUEAR SCROLL *\/\r\n\r\nhtml.edibim-modal-active,\r\nbody.edibim-modal-active{\r\n    overflow:hidden !important;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   TABLET\r\n   ========================================================= *\/\r\n\r\n@media(max-width:900px){\r\n\r\n    #edibim-video-accion-02 .edibim-showcase{\r\n        grid-template-columns:1fr;\r\n        gap:22px;\r\n    }\r\n\r\n    #edibim-video-accion-02 .edibim-media-card{\r\n        aspect-ratio:16 \/ 9;\r\n    }\r\n\r\n    #edibim-video-accion-02 .edibim-media-preview{\r\n        width:48%;\r\n        height:48%;\r\n    }\r\n\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   M\u00d3VIL\r\n   ========================================================= *\/\r\n\r\n@media(max-width:600px){\r\n\r\n    .edibim-modal-accion-02{\r\n        padding:12px;\r\n    }\r\n\r\n    #edibim-video-accion-02 .edibim-media-card{\r\n        aspect-ratio:4 \/ 3;\r\n    }\r\n\r\n    #edibim-video-accion-02 .edibim-media-preview{\r\n        width:56%;\r\n        height:45%;\r\n\r\n        top:14px;\r\n        left:14px;\r\n    }\r\n\r\n    #edibim-video-accion-02 .edibim-card-title{\r\n        left:14px;\r\n        bottom:14px;\r\n        font-size:14px;\r\n    }\r\n\r\n    .edibim-modal-accion-02 .edibim-modal-label{\r\n        left:14px;\r\n        top:14px;\r\n    }\r\n\r\n}\r\n\r\n<\/style>\r\n\r\n\r\n<script>\r\n\r\n(function(){\r\n\r\n    \/* SOLO ESTE COMPONENTE *\/\r\n\r\n    const component =\r\n        document.getElementById(\r\n            'edibim-video-accion-02'\r\n        );\r\n\r\n    if(!component){\r\n        return;\r\n    }\r\n\r\n\r\n    const cards =\r\n        component.querySelectorAll(\r\n            '.edibim-media-card'\r\n        );\r\n\r\n\r\n    const modal =\r\n        component.querySelector(\r\n            '.edibim-modal'\r\n        );\r\n\r\n    if(!modal){\r\n        return;\r\n    }\r\n\r\n\r\n    const holder =\r\n        modal.querySelector(\r\n            '.edibim-video-holder'\r\n        );\r\n\r\n\r\n    const modalTitle =\r\n        modal.querySelector(\r\n            '.edibim-modal-label'\r\n        );\r\n\r\n\r\n    const closeButton =\r\n        modal.querySelector(\r\n            '.edibim-modal-close'\r\n        );\r\n\r\n\r\n    const backdrop =\r\n        modal.querySelector(\r\n            '.edibim-modal-backdrop'\r\n        );\r\n\r\n\r\n    \/* =====================================================\r\n       MOVER POPUP AL BODY\r\n       ===================================================== *\/\r\n\r\n    modal.classList.add(\r\n        'edibim-modal-accion-02'\r\n    );\r\n\r\n    document.body.appendChild(\r\n        modal\r\n    );\r\n\r\n\r\n    \/* =====================================================\r\n       T\u00cdTULO\r\n       ===================================================== *\/\r\n\r\n    cards.forEach(function(card){\r\n\r\n        const titulo =\r\n            card.dataset.title;\r\n\r\n        const tituloVisible =\r\n            card.querySelector(\r\n                '.edibim-card-title'\r\n            );\r\n\r\n        if(tituloVisible && titulo){\r\n            tituloVisible.textContent =\r\n                titulo;\r\n        }\r\n\r\n    });\r\n\r\n\r\n    \/* =====================================================\r\n       MINIATURA ANIMADA\r\n       ===================================================== *\/\r\n\r\n    cards.forEach(function(card){\r\n\r\n        const videoUrl =\r\n            card.dataset.video;\r\n\r\n        const preview =\r\n            card.querySelector(\r\n                '.edibim-media-preview'\r\n            );\r\n\r\n        if(!videoUrl || !preview){\r\n            return;\r\n        }\r\n\r\n\r\n        if(\r\n            \/\\.(mp4|webm|ogg)(\\?.*)?$\/i\r\n                .test(videoUrl)\r\n        ){\r\n\r\n            preview.src =\r\n                videoUrl;\r\n\r\n            preview.muted =\r\n                true;\r\n\r\n            preview.defaultMuted =\r\n                true;\r\n\r\n            preview.loop =\r\n                true;\r\n\r\n            preview.autoplay =\r\n                true;\r\n\r\n            preview.playsInline =\r\n                true;\r\n\r\n            preview.setAttribute(\r\n                'muted',\r\n                ''\r\n            );\r\n\r\n            preview.setAttribute(\r\n                'autoplay',\r\n                ''\r\n            );\r\n\r\n            preview.setAttribute(\r\n                'loop',\r\n                ''\r\n            );\r\n\r\n            preview.setAttribute(\r\n                'playsinline',\r\n                ''\r\n            );\r\n\r\n            preview.load();\r\n\r\n\r\n            function playPreview(){\r\n\r\n                const p =\r\n                    preview.play();\r\n\r\n                if(p){\r\n                    p.catch(\r\n                        function(){}\r\n                    );\r\n                }\r\n\r\n            }\r\n\r\n\r\n            preview.addEventListener(\r\n                'canplay',\r\n                playPreview\r\n            );\r\n\r\n            card.addEventListener(\r\n                'mouseenter',\r\n                playPreview\r\n            );\r\n\r\n            setTimeout(\r\n                playPreview,\r\n                300\r\n            );\r\n\r\n        }\r\n\r\n    });\r\n\r\n\r\n    \/* =====================================================\r\n       YOUTUBE\r\n       ===================================================== *\/\r\n\r\n    function youtubeEmbed(url){\r\n\r\n        let id = null;\r\n\r\n        try{\r\n\r\n            const parsed =\r\n                new URL(url);\r\n\r\n            if(\r\n                parsed.hostname.includes(\r\n                    'youtu.be'\r\n                )\r\n            ){\r\n\r\n                id =\r\n                    parsed.pathname\r\n                        .replace('\/','');\r\n\r\n            }\r\n\r\n            else if(\r\n                parsed.hostname.includes(\r\n                    'youtube.com'\r\n                )\r\n            ){\r\n\r\n                id =\r\n                    parsed.searchParams.get('v');\r\n\r\n                if(\r\n                    !id &&\r\n                    parsed.pathname.includes(\r\n                        '\/embed\/'\r\n                    )\r\n                ){\r\n\r\n                    id =\r\n                        parsed.pathname\r\n                            .split('\/embed\/')[1]\r\n                            .split('\/')[0];\r\n\r\n                }\r\n\r\n            }\r\n\r\n        }\r\n        catch(e){}\r\n\r\n\r\n        if(!id){\r\n            return null;\r\n        }\r\n\r\n\r\n        return (\r\n            'https:\/\/www.youtube.com\/embed\/' +\r\n            id +\r\n            '?autoplay=1&rel=0'\r\n        );\r\n\r\n    }\r\n\r\n\r\n    \/* =====================================================\r\n       VIMEO\r\n       ===================================================== *\/\r\n\r\n    function vimeoEmbed(url){\r\n\r\n        const match =\r\n            url.match(\r\n                \/vimeo\\.com\\\/(?:video\\\/)?([0-9]+)\/\r\n            );\r\n\r\n        if(!match){\r\n            return null;\r\n        }\r\n\r\n        return (\r\n            'https:\/\/player.vimeo.com\/video\/' +\r\n            match[1] +\r\n            '?autoplay=1'\r\n        );\r\n\r\n    }\r\n\r\n\r\n    \/* =====================================================\r\n       CREAR REPRODUCTOR\r\n       ===================================================== *\/\r\n\r\n    function crearVideo(url){\r\n\r\n        holder.innerHTML = '';\r\n\r\n        if(!url){\r\n            return;\r\n        }\r\n\r\n\r\n        \/* YOUTUBE *\/\r\n\r\n        const youtube =\r\n            youtubeEmbed(url);\r\n\r\n        if(youtube){\r\n\r\n            const iframe =\r\n                document.createElement(\r\n                    'iframe'\r\n                );\r\n\r\n            iframe.src =\r\n                youtube;\r\n\r\n            iframe.allow =\r\n                'autoplay; fullscreen; picture-in-picture';\r\n\r\n            iframe.allowFullscreen =\r\n                true;\r\n\r\n            holder.appendChild(\r\n                iframe\r\n            );\r\n\r\n            return;\r\n        }\r\n\r\n\r\n        \/* VIMEO *\/\r\n\r\n        const vimeo =\r\n            vimeoEmbed(url);\r\n\r\n        if(vimeo){\r\n\r\n            const iframe =\r\n                document.createElement(\r\n                    'iframe'\r\n                );\r\n\r\n            iframe.src =\r\n                vimeo;\r\n\r\n            iframe.allow =\r\n                'autoplay; fullscreen; picture-in-picture';\r\n\r\n            iframe.allowFullscreen =\r\n                true;\r\n\r\n            holder.appendChild(\r\n                iframe\r\n            );\r\n\r\n            return;\r\n        }\r\n\r\n\r\n        \/* MP4 \/ WEBM \/ OGG *\/\r\n\r\n        if(\r\n            \/\\.(mp4|webm|ogg)(\\?.*)?$\/i\r\n                .test(url)\r\n        ){\r\n\r\n            const video =\r\n                document.createElement(\r\n                    'video'\r\n                );\r\n\r\n            video.src =\r\n                url;\r\n\r\n            video.controls =\r\n                true;\r\n\r\n            video.autoplay =\r\n                true;\r\n\r\n            video.playsInline =\r\n                true;\r\n\r\n            video.muted =\r\n                true;\r\n\r\n            holder.appendChild(\r\n                video\r\n            );\r\n\r\n\r\n            const playPromise =\r\n                video.play();\r\n\r\n            if(playPromise){\r\n\r\n                playPromise.catch(\r\n                    function(){}\r\n                );\r\n\r\n            }\r\n\r\n            return;\r\n        }\r\n\r\n\r\n        \/* URL GEN\u00c9RICA *\/\r\n\r\n        const iframe =\r\n            document.createElement(\r\n                'iframe'\r\n            );\r\n\r\n        iframe.src =\r\n            url;\r\n\r\n        iframe.allow =\r\n            'autoplay; fullscreen; picture-in-picture';\r\n\r\n        iframe.allowFullscreen =\r\n            true;\r\n\r\n        holder.appendChild(\r\n            iframe\r\n        );\r\n\r\n    }\r\n\r\n\r\n    \/* =====================================================\r\n       ABRIR\r\n       ===================================================== *\/\r\n\r\n    function abrir(card){\r\n\r\n        const title =\r\n            card.dataset.title ||\r\n            'EDIBIM';\r\n\r\n        const video =\r\n            card.dataset.video;\r\n\r\n\r\n        modalTitle.textContent =\r\n            title;\r\n\r\n\r\n        crearVideo(\r\n            video\r\n        );\r\n\r\n\r\n        modal.classList.add(\r\n            'is-open'\r\n        );\r\n\r\n        modal.setAttribute(\r\n            'aria-hidden',\r\n            'false'\r\n        );\r\n\r\n\r\n        document\r\n            .documentElement\r\n            .classList\r\n            .add(\r\n                'edibim-modal-active'\r\n            );\r\n\r\n        document\r\n            .body\r\n            .classList\r\n            .add(\r\n                'edibim-modal-active'\r\n            );\r\n\r\n    }\r\n\r\n\r\n    \/* =====================================================\r\n       CERRAR\r\n       ===================================================== *\/\r\n\r\n    function cerrar(){\r\n\r\n        modal.classList.remove(\r\n            'is-open'\r\n        );\r\n\r\n        modal.setAttribute(\r\n            'aria-hidden',\r\n            'true'\r\n        );\r\n\r\n\r\n        document\r\n            .documentElement\r\n            .classList\r\n            .remove(\r\n                'edibim-modal-active'\r\n            );\r\n\r\n        document\r\n            .body\r\n            .classList\r\n            .remove(\r\n                'edibim-modal-active'\r\n            );\r\n\r\n\r\n        setTimeout(\r\n            function(){\r\n\r\n                holder.innerHTML = '';\r\n\r\n            },\r\n            300\r\n        );\r\n\r\n    }\r\n\r\n\r\n    \/* =====================================================\r\n       CLICK EN ESTA TARJETA\r\n       ===================================================== *\/\r\n\r\n    cards.forEach(function(card){\r\n\r\n        card.addEventListener(\r\n            'click',\r\n            function(e){\r\n\r\n                e.preventDefault();\r\n                e.stopPropagation();\r\n\r\n                abrir(card);\r\n\r\n            }\r\n        );\r\n\r\n\r\n        card.addEventListener(\r\n            'keydown',\r\n            function(e){\r\n\r\n                if(\r\n                    e.key === 'Enter' ||\r\n                    e.key === ' '\r\n                ){\r\n\r\n                    e.preventDefault();\r\n                    e.stopPropagation();\r\n\r\n                    abrir(card);\r\n\r\n                }\r\n\r\n            }\r\n        );\r\n\r\n    });\r\n\r\n\r\n    \/* CERRAR CON X *\/\r\n\r\n    closeButton.addEventListener(\r\n        'click',\r\n        function(e){\r\n\r\n            e.preventDefault();\r\n            e.stopPropagation();\r\n\r\n            cerrar();\r\n\r\n        }\r\n    );\r\n\r\n\r\n    \/* CERRAR AL PULSAR FUERA *\/\r\n\r\n    backdrop.addEventListener(\r\n        'click',\r\n        function(e){\r\n\r\n            e.preventDefault();\r\n\r\n            cerrar();\r\n\r\n        }\r\n    );\r\n\r\n\r\n    \/* ESC *\/\r\n\r\n    document.addEventListener(\r\n        'keydown',\r\n        function(e){\r\n\r\n            if(\r\n                e.key === 'Escape' &&\r\n                modal.classList.contains(\r\n                    'is-open'\r\n                )\r\n            ){\r\n\r\n                cerrar();\r\n\r\n            }\r\n\r\n        }\r\n    );\r\n\r\n})();\r\n\r\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-137108a elementor-widget elementor-widget-html\" data-id=\"137108a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<style>\r\n\/* ============================================================\r\n   EDIBIM \u00b7 REVEAL V2\r\n   ============================================================ *\/\r\n\r\n.edibim-reveal-v2 {\r\n    position: relative !important;\r\n    overflow: hidden !important;\r\n    isolation: isolate;\r\n    background: #111111;\r\n\r\n    --edibim-v2-clip: 100%;\r\n}\r\n\r\n\r\n\/* CAPA CLARA *\/\r\n.edibim-reveal-v2::before {\r\n    content: \"\";\r\n\r\n    position: absolute;\r\n    inset: 0;\r\n\r\n    background: #EDEDED;\r\n\r\n    border-radius: 18px 18px 0 0;\r\n\r\n    pointer-events: none;\r\n\r\n    z-index: 0;\r\n\r\n    clip-path: inset(\r\n        0\r\n        var(--edibim-v2-clip)\r\n        0\r\n        0\r\n    );\r\n\r\n    will-change: clip-path;\r\n}\r\n\r\n\r\n\/* CONTENIDO DE ELEMENTOR ENCIMA *\/\r\n.edibim-reveal-v2 > .e-con-inner,\r\n.edibim-reveal-v2 > .elementor-element {\r\n    position: relative;\r\n    z-index: 1;\r\n}\r\n\r\n\r\n\/* ACCESIBILIDAD *\/\r\n@media (prefers-reduced-motion: reduce) {\r\n\r\n    .edibim-reveal-v2::before {\r\n        clip-path: inset(0 0 0 0) !important;\r\n    }\r\n\r\n}\r\n<\/style>\r\n\r\n\r\n<script>\r\n(function () {\r\n\r\n    const sections =\r\n        document.querySelectorAll('.edibim-reveal-v2');\r\n\r\n    if (!sections.length) return;\r\n\r\n\r\n    function actualizarRevealV2() {\r\n\r\n        const vh = window.innerHeight;\r\n\r\n\r\n        sections.forEach(function(section) {\r\n\r\n            const rect =\r\n                section.getBoundingClientRect();\r\n\r\n\r\n            \/*\r\n             * EMPIEZA ANTES DE QUE APAREZCA.\r\n             *\r\n             * Cuando el borde superior del bloque\r\n             * est\u00e1 al 130% de la altura de pantalla.\r\n             *\/\r\n            const inicio =\r\n                vh * 1.00;\r\n\r\n\r\n            \/*\r\n             * TERMINA MUY PRONTO.\r\n             *\r\n             * Cuando el borde superior llega al\r\n             * 75% de la pantalla = 100% blanco.\r\n             *\/\r\n            const fin =\r\n                vh * 0.25;\r\n\r\n\r\n            let progress =\r\n                (inicio - rect.top) \/\r\n                (inicio - fin);\r\n\r\n\r\n            progress =\r\n                Math.max(\r\n                    0,\r\n                    Math.min(1, progress)\r\n                );\r\n\r\n\r\n            \/*\r\n             * Un poco m\u00e1s r\u00e1pido al principio,\r\n             * pero sigue dependiendo del scroll.\r\n             *\/\r\n            progress =\r\n                1 - Math.pow(\r\n                    1 - progress,\r\n                    1.6\r\n                );\r\n\r\n\r\n            const clip =\r\n                100 - (progress * 100);\r\n\r\n\r\n            section.style.setProperty(\r\n                '--edibim-v2-clip',\r\n                clip + '%'\r\n            );\r\n\r\n        });\r\n\r\n    }\r\n\r\n\r\n    actualizarRevealV2();\r\n\r\n\r\n    window.addEventListener(\r\n        'scroll',\r\n        actualizarRevealV2,\r\n        { passive: true }\r\n    );\r\n\r\n\r\n    window.addEventListener(\r\n        'resize',\r\n        actualizarRevealV2\r\n    );\r\n\r\n})();\r\n<\/script>\t\t\t\t<\/div>\n\t\t\n<\/div>\n\t\t<div class=\"elementor-element elementor-element-563b6b8 elementor-widget elementor-widget-html\" data-id=\"563b6b8\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<style>\r\n\/* =========================================================\r\n   EDIBIM EN ACCI\u00d3N \u00b7 ADAPTACI\u00d3N M\u00d3VIL\r\n   SOLO <= 767px\r\n   ========================================================= *\/\r\n\r\n@media (max-width:767px){\r\n\r\n    \/* BLOQUE DE TEXTOS\r\n       20px EXACTOS A IZQUIERDA Y DERECHA *\/\r\n    .edibim-accion-textos{\r\n        width:calc(100vw - 40px) !important;\r\n        max-width:calc(100vw - 40px) !important;\r\n        min-width:0 !important;\r\n\r\n        position:relative !important;\r\n        left:50% !important;\r\n        right:auto !important;\r\n\r\n        transform:translateX(-50%) !important;\r\n\r\n        margin-left:0 !important;\r\n        margin-right:0 !important;\r\n\r\n        box-sizing:border-box !important;\r\n    }\r\n\r\n\r\n    \/* TEXTO ALINEADO A LA IZQUIERDA\r\n       NO JUSTIFICADO *\/\r\n    .edibim-accion-textos .elementor-widget-text-editor,\r\n    .edibim-accion-textos .elementor-widget-container,\r\n    .edibim-accion-textos p{\r\n        text-align:left !important;\r\n        text-align-last:left !important;\r\n        word-spacing:normal !important;\r\n    }\r\n\r\n}\r\n<\/style>\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-a80b83e e-con e-atomic-element e-flexbox-base e-38ad541 edibim-seccion2 \" data-id=\"a80b83e\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"a80b83e\">\n    <div class=\"elementor-element elementor-element-5d2c60d e-con e-atomic-element e-flexbox-base e-b8a18c0 \" data-id=\"5d2c60d\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"5d2c60d\">\n    \t\t<div class=\"elementor-element elementor-element-06afb22 elementor-position-inline-end elementor-widget__width-initial elementor-view-default elementor-mobile-position-block-start elementor-widget elementor-widget-icon-box\" data-id=\"06afb22\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon-box.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-box-wrapper\">\n\n\t\t\t\t\t\t<div class=\"elementor-icon-box-icon\">\n\t\t\t\t<span  class=\"elementor-icon\">\n\t\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" id=\"Layer_1\" data-name=\"Layer 1\" viewBox=\"0 0 24 24\"><path d=\"m10,12.058c-.276,0-.5-.224-.5-.5v-6c0-.276.223-.5.5-.5s.5.224.5.5v6c0,.276-.223.5-.5.5Zm0,.939c-.552,0-1,.448-1,1s.448,1,1,1,1-.448,1-1-.448-1-1-1Zm6.756-2.996c.238-.139.319-.445.18-.684L12.21,1.214c-.443-.76-1.234-1.214-2.114-1.214s-1.671.454-2.114,1.214L.337,14.319c-.447.767-.45,1.684-.008,2.453.442.77,1.235,1.229,2.122,1.229h6.55c.276,0,.5-.224.5-.5s-.224-.5-.5-.5H2.451c-.524,0-.993-.271-1.255-.727s-.26-.998.004-1.451L8.845,1.718c.263-.45.73-.718,1.251-.718s.988.268,1.251.718l4.726,8.103c.139.239.445.318.684.18Zm7.099,13.853c-.098.098-.226.146-.354.146s-.256-.049-.354-.146l-3-3c-.862.705-1.948,1.146-3.146,1.146-2.757,0-5-2.243-5-5s2.243-5,5-5,5,2.243,5,5c0,1.198-.441,2.284-1.146,3.146l3,3c.195.195.195.512,0,.707Zm-2.854-6.854c0-2.206-1.794-4-4-4s-4,1.794-4,4,1.794,4,4,4,4-1.794,4-4Z\"><\/path><\/svg>\t\t\t\t<\/span>\n\t\t\t<\/div>\n\t\t\t\n\t\t\t\t\t\t<div class=\"elementor-icon-box-content\">\n\n\t\t\t\t\t\t\t\t\t<h3 class=\"elementor-icon-box-title\">\n\t\t\t\t\t\t<span  >\n\t\t\t\t\t\t\tPROBLEMA REAL\t\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/h3>\n\t\t\t\t\n\t\t\t\t\t\t\t\t\t<p class=\"elementor-icon-box-description\">\n\t\t\t\t\t\t01<br>\n\u2500\u2500\u2500\n<br><br>Partimos de una tarea BIM concreta, no de una funci\u00f3n aislada\t\t\t\t\t<\/p>\n\t\t\t\t\n\t\t\t<\/div>\n\t\t\t\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-f57333b elementor-position-inline-end elementor-widget__width-initial elementor-view-default elementor-mobile-position-block-start elementor-widget elementor-widget-icon-box\" data-id=\"f57333b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon-box.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-box-wrapper\">\n\n\t\t\t\t\t\t<div class=\"elementor-icon-box-icon\">\n\t\t\t\t<span  class=\"elementor-icon\">\n\t\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" id=\"Layer_1\" data-name=\"Layer 1\" viewBox=\"0 0 24 24\"><path d=\"m17.662,11l-5.183-3.749.016-1.293c1.42-.237,2.505-1.473,2.505-2.959,0-1.654-1.346-3-3-3s-3,1.346-3,3c0,1.482,1.08,2.715,2.494,2.957l-.016,1.283-5.142,3.76H0v4h1v-3h5.337l5.632,4.118,5.693-4.118h5.338v3h1v-4h-6.338ZM10,3c0-1.103.897-2,2-2s2,.897,2,2-.897,2-2,2-2-.897-2-2Zm-2.68,8.603l4.652-3.485,4.676,3.382-4.676,3.382-4.652-3.278Zm.385,6.063l.707.707-5.135,5.135L.031,20.264l.707-.707,2.538,2.537,4.428-4.428Zm16.265.072l-2.762,2.762,2.762,2.762-.707.707-2.762-2.762-2.762,2.762-.707-.707,2.762-2.762-2.762-2.762.707-.707,2.762,2.762,2.762-2.762.707.707Z\"><\/path><\/svg>\t\t\t\t<\/span>\n\t\t\t<\/div>\n\t\t\t\n\t\t\t\t\t\t<div class=\"elementor-icon-box-content\">\n\n\t\t\t\t\t\t\t\t\t<h3 class=\"elementor-icon-box-title\">\n\t\t\t\t\t\t<span  >\n\t\t\t\t\t\t\tDECISI\u00d3N DEL ALUMNO\t\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/h3>\n\t\t\t\t\n\t\t\t\t\t\t\t\t\t<p class=\"elementor-icon-box-description\">\n\t\t\t\t\t\t02<br>\n\u2500\u2500\u2500\n<br><br>Defines qu\u00e9 quieres conseguir, aportas el contexto y marcas el criterio.\t\t\t\t\t<\/p>\n\t\t\t\t\n\t\t\t<\/div>\n\t\t\t\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e738a6d elementor-position-inline-end elementor-widget__width-initial elementor-view-default elementor-mobile-position-block-start elementor-widget elementor-widget-icon-box\" data-id=\"e738a6d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon-box.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-box-wrapper\">\n\n\t\t\t\t\t\t<div class=\"elementor-icon-box-icon\">\n\t\t\t\t<span  class=\"elementor-icon\">\n\t\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" id=\"Layer_1\" viewBox=\"0 0 24 24\" data-name=\"Layer 1\"><path d=\"m10.708 10.955.63 2.045h-1.274zm11.94 7.654s0 0 0 0c-.577.929-1.763 1.375-3.333 1.375-.434 0-.907-.047-1.396-.114.097-.306.188-.619.272-.943 1.819.233 3.124-.067 3.607-.845.888-1.431-.884-4.624-4.85-7.747.032.546.061 1.095.061 1.666 0 6.729-2.196 12-5 12-1.221 0-2.327-1.002-3.187-2.687.319-.092.645-.203.971-.313.675 1.273 1.462 2 2.216 2 1.498 0 3.128-2.84 3.743-7.239-.321.224-.651.445-.997.661-2.779 1.727-5.664 2.928-8.122 3.383-.704.13-1.354.194-1.943.194-1.597 0-2.748-.473-3.318-1.388 0 0-.001 0-.002-.002-.768-1.237-.29-3.063 1.123-5.043.225.237.459.473.704.708-1.122 1.612-1.482 2.995-.978 3.807.54.867 2.079 1.138 4.231.74 1.368-.253 2.882-.759 4.438-1.464-.54-.291-1.082-.598-1.627-.936-5.96-3.705-9.427-8.548-7.891-11.028 0 0 0-.002.001-.003.735-1.185 2.467-1.576 4.719-1.266-.096.307-.185.622-.269.948-1.815-.232-3.117.068-3.6.845-.889 1.432.886 4.63 4.859 7.756-.031-.552-.08-1.098-.08-1.674 0-6.728 2.206-11.999 5.009-12 1.22 0 2.325 1 3.184 2.682-.32.093-.644.196-.973.309-.674-1.267-1.459-1.99-2.211-1.99-1.499 0-3.13 2.843-3.744 7.246.33-.223.649-.451.997-.667 5.963-3.705 11.844-4.667 13.386-2.188s0 0 0 0c.769 1.237.289 3.064-1.124 5.044-.225-.237-.458-.473-.703-.709 1.122-1.613 1.482-2.995.978-3.807-.862-1.387-4.321-1.269-8.689.723.546.29 1.094.593 1.647.937 5.966 3.706 9.432 8.551 7.892 11.03zm-9.648-3.609-1.847-6h-.878l-1.888 6h1.049l.315-1h1.896l.308 1h1.046zm2-6h-1v6h1z\"><\/path><\/svg>\t\t\t\t<\/span>\n\t\t\t<\/div>\n\t\t\t\n\t\t\t\t\t\t<div class=\"elementor-icon-box-content\">\n\n\t\t\t\t\t\t\t\t\t<h3 class=\"elementor-icon-box-title\">\n\t\t\t\t\t\t<span  >\n\t\t\t\t\t\t\tEJECUCI\u00d3N DE EDIBIM\t\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/h3>\n\t\t\t\t\n\t\t\t\t\t\t\t\t\t<p class=\"elementor-icon-box-description\">\n\t\t\t\t\t\t03<br>\n\u2500\u2500\u2500\n<br><br>EdiBIM convierte tus instrucciones en acciones reales dentro de Revit.\t\t\t\t\t<\/p>\n\t\t\t\t\n\t\t\t<\/div>\n\t\t\t\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-8520cc6 elementor-position-inline-end elementor-widget__width-initial elementor-view-default elementor-mobile-position-block-start elementor-widget elementor-widget-icon-box\" data-id=\"8520cc6\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon-box.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-box-wrapper\">\n\n\t\t\t\t\t\t<div class=\"elementor-icon-box-icon\">\n\t\t\t\t<span  class=\"elementor-icon\">\n\t\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" id=\"Layer_1\" data-name=\"Layer 1\" viewBox=\"0 0 24 24\" width=\"512\" height=\"512\"><path d=\"m23.354,7.691l-13.577,13.577c-.473.473-1.1.732-1.768.732h-.005c-.669,0-1.297-.264-1.769-.738L.666,15.65l.71-.705,5.569,5.611c.283.285.659.442,1.061.443h.003c.401,0,.777-.156,1.061-.439l13.577-13.577.707.707Zm-15.359,3.31h.014c.668,0,1.295-.26,1.768-.732L17.976,2.068l-.707-.707-8.2,8.2c-.284.284-.66.439-1.061.439h-.008c-.404-.002-.781-.162-1.063-.451L2.858,5.385l-.715.699,4.078,4.165c.471.481,1.1.748,1.773.751Z\"><\/path><\/svg>\t\t\t\t<\/span>\n\t\t\t<\/div>\n\t\t\t\n\t\t\t\t\t\t<div class=\"elementor-icon-box-content\">\n\n\t\t\t\t\t\t\t\t\t<h3 class=\"elementor-icon-box-title\">\n\t\t\t\t\t\t<span  >\n\t\t\t\t\t\t\tCOMPROBAR EL RESULTADO\t\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/h3>\n\t\t\t\t\n\t\t\t\t\t\t\t\t\t<p class=\"elementor-icon-box-description\">\n\t\t\t\t\t\t04<br>\n\u2500\u2500\u2500\n<br><br>Revisas lo ejecutado, detectas desviaciones y decides si el resultado es v\u00e1lido.\t\t\t\t\t<\/p>\n\t\t\t\t\n\t\t\t<\/div>\n\t\t\t\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-6ded732 e-con e-atomic-element e-flexbox-base e-ae23802 edibim-sticky \" data-id=\"6ded732\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"6ded732\">\n    \t\t<div class=\"elementor-element elementor-element-a774f8e elementor-widget elementor-widget-text-editor\" data-id=\"a774f8e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>POR QU\u00c9 EDIBIM<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-fda62ca elementor-widget elementor-widget-text-editor\" data-id=\"fda62ca\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h3 class=\"PDq2pG_selectionAnchorContainer\" data-section-id=\"fcrq78\" data-start=\"106\" data-end=\"132\"><strong>NO TE AHORRA PENSAR<\/strong><\/h3><h3 class=\"PDq2pG_selectionAnchorContainer\" data-section-id=\"fcrq78\" data-start=\"106\" data-end=\"132\"><strong>TE DEVUELVE TIEMPO PARA PENSAR<\/strong><\/h3>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9a267d0 elementor-widget elementor-widget-text-editor\" data-id=\"9a267d0\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p class=\"PDq2pG_selectionAnchorContainer\" data-start=\"170\" data-end=\"367\">Tu conocimiento BIM tiene m\u00e1s valor cuando lo utilizas para <strong data-start=\"230\" data-end=\"266\">interpretar, coordinar y decidir<\/strong>, no cuando se consume trasladando datos, repitiendo selecciones o comprobando elementos uno por uno.<\/p><p data-start=\"369\" data-end=\"494\">Por eso desarrollamos EdiBIM: para convertir tareas BIM recurrentes en <strong data-start=\"440\" data-end=\"493\">acciones, reglas y comprobaciones dentro de Revit<\/strong>.<\/p><p data-start=\"496\" data-end=\"583\"><strong data-start=\"496\" data-end=\"583\">La herramienta aporta ejecuci\u00f3n. La formaci\u00f3n desarrolla la capacidad de dirigirla.<\/strong><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<a href=\"#edibim-acesso\" target=\"_self\" class=\"e-ae7d66b-0250a2f e-button-base\" data-interaction-id=\"ae7d66b\">\n\t\tACCEDER A EDIBIM\n\t<\/a>\t\t\n<\/div>\n\t\t<div class=\"elementor-element elementor-element-6fec63c elementor-widget elementor-widget-html\" data-id=\"6fec63c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<style>\r\n.edibim-sticky{\r\n    position: sticky !important;\r\n    top: 80px !important;\r\n    align-self: flex-start !important;\r\n    z-index: 5 !important;\r\n}\r\n<\/style>\r\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-060e8c2 elementor-widget elementor-widget-html\" data-id=\"060e8c2\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"edibim-fondo-sticky\">\r\n    <canvas id=\"edibimPointsCanvas\"><\/canvas>\r\n<\/div>\r\n\r\n<style>\r\n\r\n\/* =========================================================\r\n   SECCI\u00d3N\r\n   ========================================================= *\/\r\n\r\n.edibim-seccion2{\r\n    position:relative !important;\r\n    overflow:visible !important;\r\n    isolation:isolate;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   WIDGET HTML = CAPA DE FONDO\r\n\r\n   Sigue siendo ABSOLUTO.\r\n   NO ocupa espacio.\r\n   NO crea ninguna columna.\r\n   ========================================================= *\/\r\n\r\n.edibim-seccion2 .elementor-widget-html:has(#edibimPointsCanvas){\r\n\r\n    position:absolute !important;\r\n\r\n    top:0 !important;\r\n    left:0 !important;\r\n\r\n    width:100% !important;\r\n    height:100% !important;\r\n\r\n    margin:0 !important;\r\n    padding:0 !important;\r\n\r\n    pointer-events:none !important;\r\n\r\n    z-index:0 !important;\r\n}\r\n\r\n\r\n\/* Wrapper que mete Elementor *\/\r\n\r\n.edibim-seccion2\r\n.elementor-widget-html:has(#edibimPointsCanvas)\r\n> .elementor-widget-container{\r\n\r\n    position:relative !important;\r\n\r\n    width:100% !important;\r\n    height:100% !important;\r\n\r\n    margin:0 !important;\r\n    padding:0 !important;\r\n\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   ESTO ES LO QUE SE QUEDA STICKY\r\n   ========================================================= *\/\r\n\r\n.edibim-fondo-sticky{\r\n    position:relative !important;\r\n    top:0 !important;\r\n\r\n    width:100% !important;\r\n\r\n    \/* IMPORTANTE:\r\n       ya no obligamos al canvas a medir 100vh *\/\r\n    height:100% !important;\r\n\r\n    overflow:hidden !important;\r\n    pointer-events:none !important;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   CANVAS\r\n   ========================================================= *\/\r\n\r\n#edibimPointsCanvas{\r\n\r\n    position:absolute !important;\r\n\r\n    inset:0 !important;\r\n\r\n    width:100% !important;\r\n    height:100% !important;\r\n\r\n    display:block !important;\r\n\r\n    pointer-events:none !important;\r\n\r\n    background:transparent !important;\r\n\r\n    \/* Conservamos exactamente tu desplazamiento actual *\/\r\n    transform:translateX(350px) !important;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   TODO TU CONTENIDO SIGUE ENCIMA\r\n   ========================================================= *\/\r\n\r\n.edibim-seccion2 > *{\r\n    position:relative;\r\n    z-index:2;\r\n}\r\n\r\n\r\n\/* El widget HTML siempre al fondo *\/\r\n\r\n.edibim-seccion2 .elementor-widget-html:has(#edibimPointsCanvas){\r\n    z-index:0 !important;\r\n}\r\n\r\n\r\n\/* Tu columna derecha sigue sticky *\/\r\n\r\n.edibim-sticky{\r\n    position:sticky !important;\r\n    top:80px !important;\r\n    align-self:flex-start !important;\r\n    z-index:5 !important;\r\n}\r\n\r\n<\/style>\r\n\r\n\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/three.js\/r128\/three.min.js\"><\/script>\r\n\r\n<script>\r\n(function(){\r\n\r\n    let iniciado = false;\r\n\r\n    function initEdiBim(){\r\n\r\n        if(iniciado) return;\r\n\r\n        const section =\r\n            document.querySelector('.edibim-seccion2');\r\n\r\n        const canvas =\r\n            document.getElementById('edibimPointsCanvas');\r\n\r\n        if(!section || !canvas) return;\r\n\r\n        if(typeof THREE === 'undefined'){\r\n            setTimeout(initEdiBim, 100);\r\n            return;\r\n        }\r\n\r\n        iniciado = true;\r\n\r\n\r\n        \/* =====================================================\r\n           TAMA\u00d1O REAL DE LA SECCI\u00d3N\r\n           ===================================================== *\/\r\n\r\n        function obtenerTamano(){\r\n\r\n            const r =\r\n                section.getBoundingClientRect();\r\n\r\n            return {\r\n                width: Math.max(1, r.width),\r\n                height: Math.max(1, r.height)\r\n            };\r\n        }\r\n\r\n        const inicial =\r\n            obtenerTamano();\r\n\r\n\r\n        \/* =====================================================\r\n           ESCENA\r\n           ===================================================== *\/\r\n\r\n        const scene =\r\n            new THREE.Scene();\r\n\r\n\r\n        \/* =====================================================\r\n           C\u00c1MARA ORIGINAL\r\n           ===================================================== *\/\r\n\r\n        const camera =\r\n            new THREE.PerspectiveCamera(\r\n                60,\r\n                inicial.width \/ inicial.height,\r\n                0.1,\r\n                1000\r\n            );\r\n\r\n        camera.position.set(\r\n            0,\r\n            0.4,\r\n            49.6\r\n        );\r\n\r\n\r\n        \/* =====================================================\r\n           RENDERER\r\n           ===================================================== *\/\r\n\r\n        const renderer =\r\n            new THREE.WebGLRenderer({\r\n                canvas:canvas,\r\n                alpha:true,\r\n                antialias:false,\r\n                powerPreference:'high-performance'\r\n            });\r\n\r\n        renderer.setPixelRatio(\r\n            Math.min(\r\n                window.devicePixelRatio || 1,\r\n                2\r\n            )\r\n        );\r\n\r\n        renderer.setSize(\r\n            inicial.width,\r\n            inicial.height,\r\n            false\r\n        );\r\n\r\n        \/* Transparente *\/\r\n        renderer.setClearColor(\r\n            0x000000,\r\n            0\r\n        );\r\n\r\n\r\n        \/* =====================================================\r\n           NIEBLA\r\n\r\n           El original la usa para dar profundidad.\r\n           ===================================================== *\/\r\n\r\n        scene.fog =\r\n            new THREE.Fog(\r\n                0xD1D1D1,\r\n                0,\r\n                15\r\n            );\r\n\r\n\r\n        \/* =====================================================\r\n           GEOMETR\u00cdA ORIGINAL\r\n           ===================================================== *\/\r\n\r\n        const curve =\r\n            new THREE.QuadraticBezierCurve3(\r\n\r\n                new THREE.Vector3(\r\n                    -11,\r\n                    0,\r\n                    0\r\n                ),\r\n\r\n                new THREE.Vector3(\r\n                    0,\r\n                    0,\r\n                    0\r\n                ),\r\n\r\n                new THREE.Vector3(\r\n                    11,\r\n                    0,\r\n                    0\r\n                )\r\n            );\r\n\r\n\r\n        const geometry =\r\n            new THREE.TubeGeometry(\r\n                curve,\r\n\r\n                60,     \/\/ tubularSegments ORIGINAL\r\n\r\n                1.5,    \/\/ radio ORIGINAL\r\n\r\n                60,     \/\/ radialSegments ORIGINAL\r\n\r\n                false\r\n            );\r\n\r\n\r\n        \/* Guardamos la geometr\u00eda original para el movimiento *\/\r\n        const originalPositions =\r\n            new Float32Array(\r\n                geometry.attributes.position.array\r\n            );\r\n\r\n\r\n        \/* =====================================================\r\n           SHADER DE PUNTOS CIRCULARES\r\n           ===================================================== *\/\r\n\r\n        const material =\r\n            new THREE.ShaderMaterial({\r\n\r\n                uniforms:{\r\n\r\n                    color:{\r\n                        value:\r\n                            new THREE.Color(\r\n                                0x777777\r\n                            )\r\n                    },\r\n\r\n                    size:{\r\n                        value:0.20\r\n                    },\r\n\r\n                    sizeAttenuation:{\r\n                        value:170\r\n                    },\r\n\r\n                    opacity:{\r\n                        value:0.38\r\n                    }\r\n                },\r\n\r\n                vertexShader:`\r\n\r\n                    uniform float size;\r\n                    uniform float sizeAttenuation;\r\n\r\n                    void main(){\r\n\r\n                        vec4 mvPosition =\r\n                            modelViewMatrix *\r\n                            vec4(position, 1.0);\r\n\r\n                        gl_PointSize =\r\n                            size *\r\n                            (\r\n                                sizeAttenuation \/\r\n                                -mvPosition.z\r\n                            );\r\n\r\n                        gl_Position =\r\n                            projectionMatrix *\r\n                            mvPosition;\r\n                    }\r\n\r\n                `,\r\n\r\n                fragmentShader:`\r\n\r\n                    uniform vec3 color;\r\n                    uniform float opacity;\r\n\r\n                    void main(){\r\n\r\n                        vec2 center =\r\n                            gl_PointCoord -\r\n                            vec2(0.5);\r\n\r\n                        float dist =\r\n                            dot(center,center);\r\n\r\n                        if(dist > 0.25){\r\n                            discard;\r\n                        }\r\n\r\n                        float edge =\r\n                            1.0 -\r\n                            smoothstep(\r\n                                0.18,\r\n                                0.25,\r\n                                dist\r\n                            );\r\n\r\n                        gl_FragColor =\r\n                            vec4(\r\n                                color,\r\n                                opacity * edge\r\n                            );\r\n                    }\r\n\r\n                `,\r\n\r\n                transparent:true,\r\n                depthWrite:false,\r\n                fog:false\r\n            });\r\n\r\n\r\n        const points =\r\n            new THREE.Points(\r\n                geometry,\r\n                material\r\n            );\r\n\r\n\r\n        \/* =====================================================\r\n           POSICI\u00d3N ORIGINAL\r\n           ===================================================== *\/\r\n\r\n        points.position.set(\r\n            -4.2,\r\n            -2.5,\r\n            36\r\n        );\r\n\r\n\r\n        \/* =====================================================\r\n           ROTACI\u00d3N ORIGINAL\r\n\r\n           ESTA es la que crea:\r\n\r\n           GRANDE IZQUIERDA  ======>  PEQUE\u00d1O DERECHA\r\n           ===================================================== *\/\r\n\r\n        points.rotation.set(\r\n            THREE.MathUtils.degToRad(0),\r\n            THREE.MathUtils.degToRad(-65),\r\n            THREE.MathUtils.degToRad(16)\r\n        );\r\n\r\n\r\n        \/*\r\n         * Un poco m\u00e1s peque\u00f1o que el original\r\n         * para nuestra composici\u00f3n con tarjetas.\r\n         *\/\r\n        points.scale.set(\r\n             0.8,\r\n             0.8,\r\n             0.8\r\n        );\r\n\r\n\r\n        scene.add(points);\r\n\r\n\r\n        \/* Necesario para proyectar correctamente *\/\r\n        scene.updateMatrixWorld(true);\r\n\r\n\r\n        \/* =====================================================\r\n           RAT\u00d3N\r\n           ===================================================== *\/\r\n\r\n        const mouse = {\r\n            x:-9999,\r\n            y:-9999\r\n        };\r\n\r\n\r\n        const targets =\r\n            new Float32Array(\r\n                originalPositions.length\r\n            );\r\n\r\n        const offsets =\r\n            new Float32Array(\r\n                originalPositions.length\r\n            );\r\n\r\n\r\n        const world =\r\n            new THREE.Vector3();\r\n\r\n        const projected =\r\n            new THREE.Vector3();\r\n\r\n\r\n        section.addEventListener(\r\n            'pointermove',\r\n            function(e){\r\n\r\n                const r =\r\n                    canvas.getBoundingClientRect();\r\n\r\n                mouse.x =\r\n                    e.clientX -\r\n                    r.left;\r\n\r\n                mouse.y =\r\n                    e.clientY -\r\n                    r.top;\r\n            },\r\n            {\r\n                passive:true\r\n            }\r\n        );\r\n\r\n\r\n        section.addEventListener(\r\n            'pointerleave',\r\n            function(){\r\n\r\n                mouse.x = -9999;\r\n                mouse.y = -9999;\r\n            }\r\n        );\r\n\r\n\r\n        \/* =====================================================\r\n           ONDA ORIGINAL\r\n           ===================================================== *\/\r\n\r\n        function wave(\r\n            time,\r\n            coordinate,\r\n            speed,\r\n            useCos\r\n        ){\r\n\r\n            const trig =\r\n                useCos ?\r\n                    Math.cos :\r\n                    Math.sin;\r\n\r\n            return (\r\n                0.5 *\r\n                Math.sin(\r\n                    trig(\r\n                        time +\r\n                        coordinate *\r\n                        speed\r\n                    ) *\r\n                    Math.PI\r\n                )\r\n            );\r\n        }\r\n\r\n\r\n        \/* =====================================================\r\n           ANIMACI\u00d3N\r\n           ===================================================== *\/\r\n\r\n        let time = 0;\r\n\r\n\r\n        function animate(){\r\n\r\n            requestAnimationFrame(\r\n                animate\r\n            );\r\n\r\n\r\n            time += 0.01;\r\n\r\n\r\n            const positions =\r\n                geometry.attributes.position.array;\r\n\r\n\r\n            const rect =\r\n                canvas.getBoundingClientRect();\r\n\r\n\r\n            points.updateMatrixWorld(true);\r\n\r\n\r\n            for(\r\n                let i = 0;\r\n                i < positions.length;\r\n                i += 3\r\n            ){\r\n\r\n                const ox =\r\n                    originalPositions[i];\r\n\r\n                const oy =\r\n                    originalPositions[i + 1];\r\n\r\n                const oz =\r\n                    originalPositions[i + 2];\r\n\r\n\r\n                \/* =============================================\r\n                   POSICI\u00d3N DEL PUNTO EN PANTALLA\r\n                   ============================================= *\/\r\n\r\n                world.set(\r\n                    ox,\r\n                    oy,\r\n                    oz\r\n                );\r\n\r\n                world.applyMatrix4(\r\n                    points.matrixWorld\r\n                );\r\n\r\n                projected.copy(\r\n                    world\r\n                );\r\n\r\n                projected.project(\r\n                    camera\r\n                );\r\n\r\n\r\n                const px =\r\n                    (\r\n                        projected.x *\r\n                        0.5 +\r\n                        0.5\r\n                    ) *\r\n                    rect.width;\r\n\r\n\r\n                const py =\r\n                    (\r\n                        -projected.y *\r\n                        0.5 +\r\n                        0.5\r\n                    ) *\r\n                    rect.height;\r\n\r\n\r\n                const dx =\r\n                    px -\r\n                    mouse.x;\r\n\r\n                const dy =\r\n                    py -\r\n                    mouse.y;\r\n\r\n\r\n                const distance =\r\n                    Math.sqrt(\r\n                        dx * dx +\r\n                        dy * dy\r\n                    );\r\n\r\n\r\n                \/* =============================================\r\n                   REPULSI\u00d3N\r\n\r\n                   Valores del comportamiento original.\r\n                   ============================================= *\/\r\n\r\n                if(distance < 125){\r\n\r\n                    const strength =\r\n                        1 -\r\n                        distance \/ 125;\r\n\r\n                    const angle =\r\n                        Math.atan2(\r\n                            dy,\r\n                            dx\r\n                        );\r\n\r\n\r\n                    targets[i] =\r\n                        -Math.cos(angle) *\r\n                        strength *\r\n                        1.5;\r\n\r\n\r\n                    targets[i + 1] =\r\n                        -Math.sin(angle) *\r\n                        strength *\r\n                        1.5;\r\n\r\n\r\n                    targets[i + 2] =\r\n                        strength *\r\n                        0.75;\r\n\r\n                }else{\r\n\r\n                    targets[i] = 0;\r\n                    targets[i + 1] = 0;\r\n                    targets[i + 2] = 0;\r\n                }\r\n\r\n\r\n                \/* =============================================\r\n                   RETORNO SUAVE\r\n                   ============================================= *\/\r\n\r\n                offsets[i] +=\r\n                    (\r\n                        targets[i] -\r\n                        offsets[i]\r\n                    ) *\r\n                    0.10;\r\n\r\n\r\n                offsets[i + 1] +=\r\n                    (\r\n                        targets[i + 1] -\r\n                        offsets[i + 1]\r\n                    ) *\r\n                    0.10;\r\n\r\n\r\n                offsets[i + 2] +=\r\n                    (\r\n                        targets[i + 2] -\r\n                        offsets[i + 2]\r\n                    ) *\r\n                    0.10;\r\n\r\n\r\n                \/* =============================================\r\n                   ONDA MUY SUAVE\r\n                   ============================================= *\/\r\n\r\n                const wx =\r\n                    wave(\r\n                        time,\r\n                        ox,\r\n                        0.01,\r\n                        false\r\n                    ) *\r\n                    0.02;\r\n\r\n\r\n                const wy =\r\n                    wave(\r\n                        time,\r\n                        oy,\r\n                        0.015,\r\n                        true\r\n                    ) *\r\n                    0.02;\r\n\r\n\r\n                const wz =\r\n                    wave(\r\n                        time,\r\n                        oz,\r\n                        0.02,\r\n                        false\r\n                    ) *\r\n                    0.02;\r\n\r\n\r\n                positions[i] =\r\n                    ox +\r\n                    wx +\r\n                    offsets[i];\r\n\r\n\r\n                positions[i + 1] =\r\n                    oy +\r\n                    wy +\r\n                    offsets[i + 1];\r\n\r\n\r\n                positions[i + 2] =\r\n                    oz +\r\n                    wz +\r\n                    offsets[i + 2];\r\n\r\n            }\r\n\r\n\r\n            geometry\r\n                .attributes\r\n                .position\r\n                .needsUpdate = true;\r\n\r\n\r\n            renderer.render(\r\n                scene,\r\n                camera\r\n            );\r\n        }\r\n\r\n\r\n        \/* =====================================================\r\n           RESIZE \u2014 SIN SCROLL INFINITO\r\n           ===================================================== *\/\r\n\r\n        let resizeTimer = null;\r\n\r\n\r\n        window.addEventListener(\r\n            'resize',\r\n            function(){\r\n\r\n                clearTimeout(\r\n                    resizeTimer\r\n                );\r\n\r\n\r\n                resizeTimer =\r\n                    setTimeout(\r\n                        function(){\r\n\r\n                            const size =\r\n                                obtenerTamano();\r\n\r\n                            camera.aspect =\r\n                                size.width \/\r\n                                size.height;\r\n\r\n                            camera.updateProjectionMatrix();\r\n\r\n\r\n                            renderer.setSize(\r\n                                size.width,\r\n                                size.height,\r\n                                false\r\n                            );\r\n\r\n                        },\r\n                        150\r\n                    );\r\n\r\n            },\r\n            {\r\n                passive:true\r\n            }\r\n        );\r\n\r\n\/* =====================================================\r\n   STICKY VISUAL DEL FONDO\r\n   SIN CAMBIAR TAMA\u00d1O NI PERSPECTIVA\r\n   ===================================================== *\/\r\n\r\nfunction actualizarStickyFondo(){\r\n\r\n    const rect =\r\n        section.getBoundingClientRect();\r\n\r\n    \/*\r\n     * Cu\u00e1nto hemos avanzado dentro de la secci\u00f3n.\r\n     *\/\r\n    let desplazamiento =\r\n        -rect.top;\r\n\r\n    \/*\r\n     * Antes de llegar a la secci\u00f3n:\r\n     * no mover.\r\n     *\/\r\n    if(desplazamiento < 0){\r\n        desplazamiento = 0;\r\n    }\r\n\r\n    \/*\r\n     * Evitar que siga movi\u00e9ndose\r\n     * cuando llegamos al final de la secci\u00f3n.\r\n     *\/\r\n    const maximo =\r\n        Math.max(\r\n            0,\r\n            rect.height -\r\n            window.innerHeight\r\n        );\r\n\r\n    if(desplazamiento > maximo){\r\n        desplazamiento = maximo;\r\n    }\r\n\r\n    \/*\r\n     * IMPORTANTE:\r\n     * mantenemos tus 350px horizontales\r\n     * y solo a\u00f1adimos movimiento vertical.\r\n     *\/\r\n    canvas.style.transform =\r\n        `translateX(350px) translateY(${desplazamiento}px)`;\r\n}\r\n\r\n\r\nwindow.addEventListener(\r\n    'scroll',\r\n    actualizarStickyFondo,\r\n    {passive:true}\r\n);\r\n\r\n\r\nactualizarStickyFondo();\r\n\r\n        \/* =====================================================\r\n           INICIO\r\n           ===================================================== *\/\r\n\r\n        animate();\r\n\r\n    }\r\n\r\n\r\n    function esperar(){\r\n\r\n        if(typeof THREE === 'undefined'){\r\n\r\n            setTimeout(\r\n                esperar,\r\n                100\r\n            );\r\n\r\n            return;\r\n        }\r\n\r\n        initEdiBim();\r\n    }\r\n\r\n\r\n    if(document.readyState === 'loading'){\r\n\r\n        document.addEventListener(\r\n            'DOMContentLoaded',\r\n            esperar\r\n        );\r\n\r\n    }else{\r\n\r\n        esperar();\r\n    }\r\n\r\n})();\r\n\r\n\r\n<\/script>\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-dd256e4 e-con e-atomic-element e-flexbox-base e-dd256e4-7f3cc52 edibim-investors-points \" data-id=\"dd256e4\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"dd256e4\">\n    <div class=\"elementor-element elementor-element-6afbbd6 e-con e-atomic-element e-flexbox-base e-6afbbd6-3373ec5 edibim-sticky \" data-id=\"6afbbd6\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"6afbbd6\" id=\"casoreal\">\n    \t\t<div class=\"elementor-element elementor-element-5d487d5 elementor-widget elementor-widget-text-editor\" data-id=\"5d487d5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>CASO REAL<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-845ce35 elementor-widget elementor-widget-text-editor\" data-id=\"845ce35\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h3 class=\"PDq2pG_selectionAnchorContainer\" data-section-id=\"1lv1spf\" data-start=\"349\" data-end=\"370\"><strong>11.214 ELEMENTOS<\/strong><\/h3><h3 data-section-id=\"16x7i48\" data-start=\"371\" data-end=\"395\"><strong>\u00bfPOR D\u00d3NDE EMPEZAR\u00cdAS?<\/strong><\/h3>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-d0dd634 elementor-widget elementor-widget-text-editor\" data-id=\"d0dd634\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p class=\"PDq2pG_selectionAnchorContainer\" data-start=\"327\" data-end=\"507\">En el ejercicio grabado, el alumno convirti\u00f3 requisitos del Plan de Ejecuci\u00f3n BIM (BEP) en controles medibles, delimit\u00f3 qu\u00e9 pod\u00eda corregirse y dirigi\u00f3 la ejecuci\u00f3n sobre el modelo. Despu\u00e9s de corregir, volvi\u00f3 a medir el resultado.<\/p><h4 data-start=\"327\" data-end=\"507\"><strong>MEDIDO. REVISABLE. \u00daTIL PARA DECIDIR.<\/strong><\/h4>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-f05ce21 elementor-widget elementor-widget-html\" data-id=\"f05ce21\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- =========================================================\r\n     EDIBIM \u00b7 V\u00cdDEO \u00b7 EDIBIM EN ACCI\u00d3N\r\n     COMPONENTE COMPLETAMENTE AISLADO\r\n     ========================================================= -->\r\n\r\n<div id=\"edibim-video-accion-01\" class=\"edibim-video-accion-component\">\r\n\r\n    <!-- =====================================================\r\n         TARJETA\r\n         ===================================================== -->\r\n\r\n    <div class=\"edibim-showcase\">\r\n\r\n        <article\r\n            class=\"edibim-media-card\"\r\n            data-title=\"VER EL CASO EN ACCI\u00d3N\"\r\n            data-video=\"https:\/\/mcpbim.com\/wp-content\/uploads\/2026\/09\/Mi-video-1.mp4\"\r\n            tabindex=\"0\"\r\n            role=\"button\"\r\n            aria-label=\"Abrir v\u00eddeo\"\r\n        >\r\n\r\n            <!-- MINIATURA ANIMADA -->\r\n            <video\r\n                class=\"edibim-media-preview\"\r\n                autoplay\r\n                muted\r\n                loop\r\n                playsinline\r\n                preload=\"auto\"\r\n            ><\/video>\r\n\r\n            <!-- SOMBRA HOVER -->\r\n            <div class=\"edibim-card-shade\"><\/div>\r\n\r\n            <!-- T\u00cdTULO -->\r\n            <div class=\"edibim-card-title\">\r\n                VER EL CASO EN ACCI\u00d3N\r\n            <\/div>\r\n\r\n            <!-- ESQUINAS -->\r\n            <span class=\"edibim-corner tl\"><\/span>\r\n            <span class=\"edibim-corner tr\"><\/span>\r\n            <span class=\"edibim-corner bl\"><\/span>\r\n            <span class=\"edibim-corner br\"><\/span>\r\n\r\n        <\/article>\r\n\r\n    <\/div>\r\n\r\n\r\n    <!-- =====================================================\r\n         POPUP PROPIO\r\n         ===================================================== -->\r\n\r\n    <div class=\"edibim-modal\" aria-hidden=\"true\">\r\n\r\n        <div class=\"edibim-modal-backdrop\"><\/div>\r\n\r\n        <div class=\"edibim-modal-window\">\r\n\r\n            <button\r\n                class=\"edibim-modal-close\"\r\n                type=\"button\"\r\n                aria-label=\"Cerrar v\u00eddeo\"\r\n            >\r\n                \u00d7\r\n            <\/button>\r\n\r\n            <div class=\"edibim-modal-label\">\r\n                EDIBIM\r\n            <\/div>\r\n\r\n            <div class=\"edibim-video-holder\"><\/div>\r\n\r\n        <\/div>\r\n\r\n    <\/div>\r\n\r\n<\/div>\r\n\r\n\r\n<style>\r\n\r\n\/* =========================================================\r\n   EDIBIM EN ACCI\u00d3N \u00b7 COMPONENTE AISLADO\r\n   ========================================================= *\/\r\n\r\n\r\n\/* =========================================================\r\n   CONTENEDOR\r\n   ========================================================= *\/\r\n\r\n#edibim-video-accion-01 .edibim-showcase{\r\n\r\n    width:100%;\r\n\r\n    display:grid;\r\n\r\n    grid-template-columns:\r\n        repeat(1,minmax(0,1fr));\r\n\r\n    gap:28px;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   TARJETA\r\n   ========================================================= *\/\r\n\r\n#edibim-video-accion-01 .edibim-media-card{\r\n\r\n    position:relative;\r\n\r\n    width:100%;\r\n\r\n    aspect-ratio:16 \/ 8.5;\r\n\r\n    background:#171717;\r\n\r\n    overflow:hidden;\r\n\r\n    cursor:pointer;\r\n\r\n    isolation:isolate;\r\n\r\n    border:\r\n        1px solid\r\n        rgba(255,255,255,.035);\r\n\r\n    transition:\r\n        background .45s ease,\r\n        transform .45s cubic-bezier(.2,.75,.25,1);\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   MINIATURA\r\n   MISMO SISTEMA QUE B4-01\r\n   ========================================================= *\/\r\n\r\n#edibim-video-accion-01 .edibim-media-preview{\r\n\r\n    position:absolute;\r\n\r\n    top:18px;\r\n    left:18px;\r\n\r\n    width:42%;\r\n    height:45%;\r\n\r\n    background-position:center;\r\n    background-size:cover;\r\n    background-repeat:no-repeat;\r\n\r\n    object-fit:cover;\r\n\r\n    display:block;\r\n\r\n    filter:\r\n        grayscale(100%)\r\n        contrast(1.05)\r\n        brightness(.78);\r\n\r\n    transform:scale(1);\r\n\r\n    transform-origin:left top;\r\n\r\n    transition:\r\n        top .6s cubic-bezier(.2,.8,.2,1),\r\n        left .6s cubic-bezier(.2,.8,.2,1),\r\n        width .6s cubic-bezier(.2,.8,.2,1),\r\n        height .6s cubic-bezier(.2,.8,.2,1),\r\n        filter .6s ease,\r\n        transform .8s cubic-bezier(.2,.8,.2,1);\r\n\r\n    z-index:1;\r\n}\r\n\r\n\r\n\/* HOVER *\/\r\n\r\n#edibim-video-accion-01 .edibim-media-card:hover\r\n.edibim-media-preview,\r\n\r\n#edibim-video-accion-01 .edibim-media-card:focus-visible\r\n.edibim-media-preview{\r\n\r\n    top:0;\r\n    left:0;\r\n\r\n    width:100%;\r\n    height:100%;\r\n\r\n    filter:\r\n        grayscale(0%)\r\n        contrast(1.02)\r\n        brightness(.72);\r\n\r\n    transform:scale(1.035);\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   CAPA OSCURA\r\n   ========================================================= *\/\r\n\r\n#edibim-video-accion-01 .edibim-card-shade{\r\n\r\n    position:absolute;\r\n\r\n    inset:0;\r\n\r\n    background:\r\n        linear-gradient(\r\n            to top,\r\n            rgba(10,10,10,.76) 0%,\r\n            rgba(10,10,10,.20) 48%,\r\n            rgba(10,10,10,.05) 100%\r\n        );\r\n\r\n    opacity:0;\r\n\r\n    transition:\r\n        opacity .45s ease;\r\n\r\n    z-index:2;\r\n}\r\n\r\n\r\n#edibim-video-accion-01 .edibim-media-card:hover\r\n.edibim-card-shade,\r\n\r\n#edibim-video-accion-01 .edibim-media-card:focus-visible\r\n.edibim-card-shade{\r\n\r\n    opacity:1;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   T\u00cdTULO\r\n   ========================================================= *\/\r\n\r\n#edibim-video-accion-01 .edibim-card-title{\r\n\r\n    position:absolute;\r\n\r\n    left:18px;\r\n    bottom:18px;\r\n\r\n    font-family:inherit;\r\n\r\n    font-size:16px;\r\n\r\n    font-weight:500;\r\n\r\n    line-height:1;\r\n\r\n    color:#F26B3A;\r\n\r\n    z-index:4;\r\n\r\n    transition:\r\n        transform .45s ease,\r\n        color .45s ease;\r\n}\r\n\r\n\r\n#edibim-video-accion-01 .edibim-media-card:hover\r\n.edibim-card-title{\r\n\r\n    transform:translateY(-2px);\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   ESQUINAS\r\n   ========================================================= *\/\r\n\r\n#edibim-video-accion-01 .edibim-corner{\r\n\r\n    position:absolute;\r\n\r\n    width:14px;\r\n    height:14px;\r\n\r\n    z-index:5;\r\n\r\n    opacity:.55;\r\n\r\n    transition:\r\n        opacity .4s ease,\r\n        width .4s ease,\r\n        height .4s ease;\r\n}\r\n\r\n\r\n#edibim-video-accion-01 .edibim-corner.tl{\r\n\r\n    top:0;\r\n    left:0;\r\n\r\n    border-top:1px solid #F26B3A;\r\n    border-left:1px solid #F26B3A;\r\n}\r\n\r\n\r\n#edibim-video-accion-01 .edibim-corner.tr{\r\n\r\n    top:0;\r\n    right:0;\r\n\r\n    border-top:1px solid #F26B3A;\r\n    border-right:1px solid #F26B3A;\r\n}\r\n\r\n\r\n#edibim-video-accion-01 .edibim-corner.bl{\r\n\r\n    bottom:0;\r\n    left:0;\r\n\r\n    border-bottom:1px solid #F26B3A;\r\n    border-left:1px solid #F26B3A;\r\n}\r\n\r\n\r\n#edibim-video-accion-01 .edibim-corner.br{\r\n\r\n    bottom:0;\r\n    right:0;\r\n\r\n    border-bottom:1px solid #F26B3A;\r\n    border-right:1px solid #F26B3A;\r\n}\r\n\r\n\r\n#edibim-video-accion-01 .edibim-media-card:hover\r\n.edibim-corner{\r\n\r\n    width:20px;\r\n    height:20px;\r\n\r\n    opacity:1;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   ACCESIBILIDAD\r\n   ========================================================= *\/\r\n\r\n#edibim-video-accion-01 .edibim-media-card:focus{\r\n\r\n    outline:none;\r\n}\r\n\r\n\r\n#edibim-video-accion-01 .edibim-media-card:focus-visible{\r\n\r\n    outline:\r\n        1px solid\r\n        rgba(242,107,58,.8);\r\n\r\n    outline-offset:4px;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   POPUP\r\n   JS LO MUEVE AL BODY\r\n   ========================================================= *\/\r\n\r\n.edibim-modal-accion-01{\r\n\r\n    position:fixed !important;\r\n\r\n    inset:0 !important;\r\n\r\n    width:100vw !important;\r\n    height:100vh !important;\r\n    height:100dvh !important;\r\n\r\n    z-index:2147483000 !important;\r\n\r\n    display:flex;\r\n\r\n    align-items:center;\r\n    justify-content:center;\r\n\r\n    padding:40px;\r\n\r\n    box-sizing:border-box;\r\n\r\n    opacity:0;\r\n\r\n    visibility:hidden;\r\n\r\n    pointer-events:none;\r\n\r\n    transition:\r\n        opacity .35s ease,\r\n        visibility .35s ease;\r\n}\r\n\r\n\r\n.edibim-modal-accion-01.is-open{\r\n\r\n    opacity:1;\r\n\r\n    visibility:visible;\r\n\r\n    pointer-events:auto;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   FONDO POPUP\r\n   ========================================================= *\/\r\n\r\n.edibim-modal-accion-01 .edibim-modal-backdrop{\r\n\r\n    position:absolute;\r\n\r\n    inset:0;\r\n\r\n    background:rgba(8,8,8,.88);\r\n\r\n    backdrop-filter:blur(8px);\r\n\r\n    -webkit-backdrop-filter:blur(8px);\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   VENTANA\r\n   ========================================================= *\/\r\n\r\n.edibim-modal-accion-01 .edibim-modal-window{\r\n\r\n    position:relative;\r\n\r\n    width:min(1280px,92vw);\r\n\r\n    background:#101010;\r\n\r\n    z-index:2;\r\n\r\n    transform:\r\n        translateY(25px)\r\n        scale(.97);\r\n\r\n    opacity:0;\r\n\r\n    transition:\r\n        transform .45s cubic-bezier(.2,.8,.2,1),\r\n        opacity .35s ease;\r\n}\r\n\r\n\r\n.edibim-modal-accion-01.is-open\r\n.edibim-modal-window{\r\n\r\n    transform:\r\n        translateY(0)\r\n        scale(1);\r\n\r\n    opacity:1;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   T\u00cdTULO POPUP\r\n   ========================================================= *\/\r\n\r\n.edibim-modal-accion-01 .edibim-modal-label{\r\n\r\n    position:absolute;\r\n\r\n    left:24px;\r\n    top:22px;\r\n\r\n    z-index:4;\r\n\r\n    font-family:inherit;\r\n\r\n    color:#F26B3A;\r\n\r\n    font-size:13px;\r\n\r\n    font-weight:600;\r\n\r\n    letter-spacing:.04em;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   BOT\u00d3N CERRAR\r\n   ========================================================= *\/\r\n\r\n.edibim-modal-accion-01 .edibim-modal-close{\r\n\r\n    position:absolute;\r\n\r\n    top:15px;\r\n    right:18px;\r\n\r\n    z-index:10;\r\n\r\n    width:42px;\r\n    height:42px;\r\n\r\n    display:flex;\r\n\r\n    justify-content:center;\r\n    align-items:center;\r\n\r\n    border:0;\r\n\r\n    background:rgba(15,15,15,.75);\r\n\r\n    color:#fff;\r\n\r\n    font-size:31px;\r\n\r\n    font-weight:200;\r\n\r\n    line-height:1;\r\n\r\n    cursor:pointer;\r\n\r\n    transition:\r\n        background .25s ease,\r\n        transform .25s ease;\r\n}\r\n\r\n\r\n.edibim-modal-accion-01 .edibim-modal-close:hover{\r\n\r\n    background:#F26B3A;\r\n\r\n    transform:rotate(4deg);\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   REPRODUCTOR\r\n   ========================================================= *\/\r\n\r\n.edibim-modal-accion-01 .edibim-video-holder{\r\n\r\n    width:100%;\r\n\r\n    aspect-ratio:16 \/ 9;\r\n\r\n    background:#000;\r\n}\r\n\r\n\r\n.edibim-modal-accion-01 .edibim-video-holder iframe,\r\n.edibim-modal-accion-01 .edibim-video-holder video{\r\n\r\n    width:100%;\r\n    height:100%;\r\n\r\n    display:block;\r\n\r\n    border:0;\r\n\r\n    object-fit:contain;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   BLOQUEAR SCROLL\r\n   ========================================================= *\/\r\n\r\nhtml.edibim-modal-active,\r\nbody.edibim-modal-active{\r\n\r\n    overflow:hidden !important;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   TABLET\r\n   ========================================================= *\/\r\n\r\n@media(max-width:900px){\r\n\r\n    #edibim-video-accion-01 .edibim-showcase{\r\n\r\n        grid-template-columns:1fr;\r\n\r\n        gap:22px;\r\n    }\r\n\r\n\r\n    #edibim-video-accion-01 .edibim-media-card{\r\n\r\n        aspect-ratio:16 \/ 9;\r\n    }\r\n\r\n\r\n    #edibim-video-accion-01 .edibim-media-preview{\r\n\r\n        width:48%;\r\n        height:48%;\r\n    }\r\n\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   M\u00d3VIL\r\n   ========================================================= *\/\r\n\r\n@media(max-width:600px){\r\n\r\n    .edibim-modal-accion-01{\r\n\r\n        padding:12px;\r\n    }\r\n\r\n\r\n    #edibim-video-accion-01 .edibim-media-card{\r\n\r\n        aspect-ratio:4 \/ 3;\r\n    }\r\n\r\n\r\n    #edibim-video-accion-01 .edibim-media-preview{\r\n\r\n        width:56%;\r\n        height:45%;\r\n\r\n        top:14px;\r\n        left:14px;\r\n    }\r\n\r\n\r\n    #edibim-video-accion-01 .edibim-card-title{\r\n\r\n        left:14px;\r\n        bottom:14px;\r\n\r\n        font-size:14px;\r\n    }\r\n\r\n\r\n    .edibim-modal-accion-01 .edibim-modal-label{\r\n\r\n        left:14px;\r\n        top:14px;\r\n    }\r\n\r\n}\r\n\r\n<\/style>\r\n\r\n\r\n<script>\r\n\r\n(function(){\r\n\r\n    \/* =====================================================\r\n       SOLO ESTE COMPONENTE\r\n       ===================================================== *\/\r\n\r\n    const component =\r\n        document.getElementById(\r\n            'edibim-video-accion-01'\r\n        );\r\n\r\n\r\n    if(!component){\r\n        return;\r\n    }\r\n\r\n\r\n    const cards =\r\n        component.querySelectorAll(\r\n            '.edibim-media-card'\r\n        );\r\n\r\n\r\n    const modal =\r\n        component.querySelector(\r\n            '.edibim-modal'\r\n        );\r\n\r\n\r\n    if(!modal){\r\n        return;\r\n    }\r\n\r\n\r\n    const holder =\r\n        modal.querySelector(\r\n            '.edibim-video-holder'\r\n        );\r\n\r\n\r\n    const modalTitle =\r\n        modal.querySelector(\r\n            '.edibim-modal-label'\r\n        );\r\n\r\n\r\n    const closeButton =\r\n        modal.querySelector(\r\n            '.edibim-modal-close'\r\n        );\r\n\r\n\r\n    const backdrop =\r\n        modal.querySelector(\r\n            '.edibim-modal-backdrop'\r\n        );\r\n\r\n\r\n    \/* =====================================================\r\n       AISLAR POPUP\r\n       ===================================================== *\/\r\n\r\n    modal.classList.add(\r\n        'edibim-modal-accion-01'\r\n    );\r\n\r\n\r\n    document.body.appendChild(\r\n        modal\r\n    );\r\n\r\n\r\n    \/* =====================================================\r\n       T\u00cdTULO\r\n       ===================================================== *\/\r\n\r\n    cards.forEach(function(card){\r\n\r\n        const titulo =\r\n            card.dataset.title;\r\n\r\n\r\n        const tituloVisible =\r\n            card.querySelector(\r\n                '.edibim-card-title'\r\n            );\r\n\r\n\r\n        if(tituloVisible && titulo){\r\n\r\n            tituloVisible.textContent =\r\n                titulo;\r\n\r\n        }\r\n\r\n    });\r\n\r\n\r\n    \/* =====================================================\r\n       MINIATURA ANIMADA\r\n       ===================================================== *\/\r\n\r\n    cards.forEach(function(card){\r\n\r\n        const videoUrl =\r\n            card.dataset.video;\r\n\r\n\r\n        const preview =\r\n            card.querySelector(\r\n                '.edibim-media-preview'\r\n            );\r\n\r\n\r\n        if(\r\n            !videoUrl ||\r\n            !preview\r\n        ){\r\n            return;\r\n        }\r\n\r\n\r\n        if(\r\n            \/\\.(mp4|webm|ogg)(\\?.*)?$\/i\r\n                .test(videoUrl)\r\n        ){\r\n\r\n            preview.src =\r\n                videoUrl;\r\n\r\n\r\n            preview.muted =\r\n                true;\r\n\r\n\r\n            preview.defaultMuted =\r\n                true;\r\n\r\n\r\n            preview.loop =\r\n                true;\r\n\r\n\r\n            preview.autoplay =\r\n                true;\r\n\r\n\r\n            preview.playsInline =\r\n                true;\r\n\r\n\r\n            preview.setAttribute(\r\n                'muted',\r\n                ''\r\n            );\r\n\r\n\r\n            preview.setAttribute(\r\n                'autoplay',\r\n                ''\r\n            );\r\n\r\n\r\n            preview.setAttribute(\r\n                'loop',\r\n                ''\r\n            );\r\n\r\n\r\n            preview.setAttribute(\r\n                'playsinline',\r\n                ''\r\n            );\r\n\r\n\r\n            preview.load();\r\n\r\n\r\n            function playPreview(){\r\n\r\n                const p =\r\n                    preview.play();\r\n\r\n\r\n                if(p){\r\n\r\n                    p.catch(\r\n                        function(){}\r\n                    );\r\n\r\n                }\r\n\r\n            }\r\n\r\n\r\n            preview.addEventListener(\r\n                'canplay',\r\n                playPreview\r\n            );\r\n\r\n\r\n            card.addEventListener(\r\n                'mouseenter',\r\n                playPreview\r\n            );\r\n\r\n\r\n            setTimeout(\r\n                playPreview,\r\n                300\r\n            );\r\n\r\n        }\r\n\r\n    });\r\n\r\n\r\n    \/* =====================================================\r\n       YOUTUBE\r\n       ===================================================== *\/\r\n\r\n    function youtubeEmbed(url){\r\n\r\n        let id = null;\r\n\r\n\r\n        try{\r\n\r\n            const parsed =\r\n                new URL(url);\r\n\r\n\r\n            if(\r\n                parsed.hostname.includes(\r\n                    'youtu.be'\r\n                )\r\n            ){\r\n\r\n                id =\r\n                    parsed.pathname\r\n                        .replace('\/','');\r\n\r\n            }\r\n\r\n\r\n            else if(\r\n                parsed.hostname.includes(\r\n                    'youtube.com'\r\n                )\r\n            ){\r\n\r\n                id =\r\n                    parsed.searchParams.get('v');\r\n\r\n\r\n                if(\r\n                    !id &&\r\n                    parsed.pathname.includes(\r\n                        '\/embed\/'\r\n                    )\r\n                ){\r\n\r\n                    id =\r\n                        parsed.pathname\r\n                            .split('\/embed\/')[1]\r\n                            .split('\/')[0];\r\n\r\n                }\r\n\r\n            }\r\n\r\n        }\r\n        catch(e){}\r\n\r\n\r\n        if(!id){\r\n            return null;\r\n        }\r\n\r\n\r\n        return (\r\n            'https:\/\/www.youtube.com\/embed\/' +\r\n            id +\r\n            '?autoplay=1&rel=0'\r\n        );\r\n\r\n    }\r\n\r\n\r\n    \/* =====================================================\r\n       VIMEO\r\n       ===================================================== *\/\r\n\r\n    function vimeoEmbed(url){\r\n\r\n        const match =\r\n            url.match(\r\n                \/vimeo\\.com\\\/(?:video\\\/)?([0-9]+)\/\r\n            );\r\n\r\n\r\n        if(!match){\r\n            return null;\r\n        }\r\n\r\n\r\n        return (\r\n            'https:\/\/player.vimeo.com\/video\/' +\r\n            match[1] +\r\n            '?autoplay=1'\r\n        );\r\n\r\n    }\r\n\r\n\r\n    \/* =====================================================\r\n       CREAR REPRODUCTOR\r\n       ===================================================== *\/\r\n\r\n    function crearVideo(url){\r\n\r\n        holder.innerHTML = '';\r\n\r\n\r\n        if(!url){\r\n            return;\r\n        }\r\n\r\n\r\n        \/* YOUTUBE *\/\r\n\r\n        const youtube =\r\n            youtubeEmbed(url);\r\n\r\n\r\n        if(youtube){\r\n\r\n            const iframe =\r\n                document.createElement(\r\n                    'iframe'\r\n                );\r\n\r\n\r\n            iframe.src =\r\n                youtube;\r\n\r\n\r\n            iframe.allow =\r\n                'autoplay; fullscreen; picture-in-picture';\r\n\r\n\r\n            iframe.allowFullscreen =\r\n                true;\r\n\r\n\r\n            holder.appendChild(\r\n                iframe\r\n            );\r\n\r\n\r\n            return;\r\n\r\n        }\r\n\r\n\r\n        \/* VIMEO *\/\r\n\r\n        const vimeo =\r\n            vimeoEmbed(url);\r\n\r\n\r\n        if(vimeo){\r\n\r\n            const iframe =\r\n                document.createElement(\r\n                    'iframe'\r\n                );\r\n\r\n\r\n            iframe.src =\r\n                vimeo;\r\n\r\n\r\n            iframe.allow =\r\n                'autoplay; fullscreen; picture-in-picture';\r\n\r\n\r\n            iframe.allowFullscreen =\r\n                true;\r\n\r\n\r\n            holder.appendChild(\r\n                iframe\r\n            );\r\n\r\n\r\n            return;\r\n\r\n        }\r\n\r\n\r\n        \/* MP4 \/ WEBM \/ OGG *\/\r\n\r\n        if(\r\n            \/\\.(mp4|webm|ogg)(\\?.*)?$\/i\r\n                .test(url)\r\n        ){\r\n\r\n            const video =\r\n                document.createElement(\r\n                    'video'\r\n                );\r\n\r\n\r\n            video.src =\r\n                url;\r\n\r\n\r\n            video.controls =\r\n                true;\r\n\r\n\r\n            video.autoplay =\r\n                true;\r\n\r\n\r\n            video.playsInline =\r\n                true;\r\n\r\n\r\n            video.muted =\r\n                true;\r\n\r\n\r\n            holder.appendChild(\r\n                video\r\n            );\r\n\r\n\r\n            const playPromise =\r\n                video.play();\r\n\r\n\r\n            if(playPromise){\r\n\r\n                playPromise.catch(\r\n                    function(){}\r\n                );\r\n\r\n            }\r\n\r\n\r\n            return;\r\n\r\n        }\r\n\r\n\r\n        \/* URL GEN\u00c9RICA *\/\r\n\r\n        const iframe =\r\n            document.createElement(\r\n                'iframe'\r\n            );\r\n\r\n\r\n        iframe.src =\r\n            url;\r\n\r\n\r\n        iframe.allow =\r\n            'autoplay; fullscreen; picture-in-picture';\r\n\r\n\r\n        iframe.allowFullscreen =\r\n            true;\r\n\r\n\r\n        holder.appendChild(\r\n            iframe\r\n        );\r\n\r\n    }\r\n\r\n\r\n    \/* =====================================================\r\n       ABRIR\r\n       ===================================================== *\/\r\n\r\n    function abrir(card){\r\n\r\n        const title =\r\n            card.dataset.title ||\r\n            'EDIBIM';\r\n\r\n\r\n        const video =\r\n            card.dataset.video;\r\n\r\n\r\n        modalTitle.textContent =\r\n            title;\r\n\r\n\r\n        crearVideo(\r\n            video\r\n        );\r\n\r\n\r\n        modal.classList.add(\r\n            'is-open'\r\n        );\r\n\r\n\r\n        modal.setAttribute(\r\n            'aria-hidden',\r\n            'false'\r\n        );\r\n\r\n\r\n        document\r\n            .documentElement\r\n            .classList\r\n            .add(\r\n                'edibim-modal-active'\r\n            );\r\n\r\n\r\n        document\r\n            .body\r\n            .classList\r\n            .add(\r\n                'edibim-modal-active'\r\n            );\r\n\r\n    }\r\n\r\n\r\n    \/* =====================================================\r\n       CERRAR\r\n       ===================================================== *\/\r\n\r\n    function cerrar(){\r\n\r\n        modal.classList.remove(\r\n            'is-open'\r\n        );\r\n\r\n\r\n        modal.setAttribute(\r\n            'aria-hidden',\r\n            'true'\r\n        );\r\n\r\n\r\n        document\r\n            .documentElement\r\n            .classList\r\n            .remove(\r\n                'edibim-modal-active'\r\n            );\r\n\r\n\r\n        document\r\n            .body\r\n            .classList\r\n            .remove(\r\n                'edibim-modal-active'\r\n            );\r\n\r\n\r\n        setTimeout(\r\n            function(){\r\n\r\n                holder.innerHTML = '';\r\n\r\n            },\r\n            300\r\n        );\r\n\r\n    }\r\n\r\n\r\n    \/* =====================================================\r\n       CLICK\r\n       ===================================================== *\/\r\n\r\n    cards.forEach(function(card){\r\n\r\n        card.addEventListener(\r\n            'click',\r\n            function(e){\r\n\r\n                e.preventDefault();\r\n\r\n                e.stopPropagation();\r\n\r\n                abrir(card);\r\n\r\n            }\r\n        );\r\n\r\n\r\n        card.addEventListener(\r\n            'keydown',\r\n            function(e){\r\n\r\n                if(\r\n                    e.key === 'Enter' ||\r\n                    e.key === ' '\r\n                ){\r\n\r\n                    e.preventDefault();\r\n\r\n                    e.stopPropagation();\r\n\r\n                    abrir(card);\r\n\r\n                }\r\n\r\n            }\r\n        );\r\n\r\n    });\r\n\r\n\r\n    \/* CERRAR CON X *\/\r\n\r\n    closeButton.addEventListener(\r\n        'click',\r\n        function(e){\r\n\r\n            e.preventDefault();\r\n\r\n            e.stopPropagation();\r\n\r\n            cerrar();\r\n\r\n        }\r\n    );\r\n\r\n\r\n    \/* CERRAR FUERA *\/\r\n\r\n    backdrop.addEventListener(\r\n        'click',\r\n        function(e){\r\n\r\n            e.preventDefault();\r\n\r\n            cerrar();\r\n\r\n        }\r\n    );\r\n\r\n\r\n    \/* ESC *\/\r\n\r\n    document.addEventListener(\r\n        'keydown',\r\n        function(e){\r\n\r\n            if(\r\n                e.key === 'Escape' &&\r\n                modal.classList.contains(\r\n                    'is-open'\r\n                )\r\n            ){\r\n\r\n                cerrar();\r\n\r\n            }\r\n\r\n        }\r\n    );\r\n\r\n})();\r\n\r\n<\/script>\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-bc75af5 e-con e-atomic-element e-flexbox-base e-bc75af5-e7275a5 \" data-id=\"bc75af5\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"bc75af5\">\n    \t\t<div class=\"elementor-element elementor-element-0587322 elementor-widget__width-initial elementor-widget elementor-widget-icon-box\" data-id=\"0587322\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon-box.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-box-wrapper\">\n\n\t\t\t\n\t\t\t\t\t\t<div class=\"elementor-icon-box-content\">\n\n\t\t\t\t\t\t\t\t\t<h3 class=\"elementor-icon-box-title\">\n\t\t\t\t\t\t<span  >\n\t\t\t\t\t\t\t11.214  <br>ELEMENTOS\t\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/h3>\n\t\t\t\t\n\t\t\t\t\t\t\t\t\t<p class=\"elementor-icon-box-description\">\n\t\t\t\t\t\tMODELO ANALIZADO<br>\n\u2500\u2500\u2500\n<br><br>El punto de partida: un modelo que necesitaba ser comprobado de forma sistem\u00e1tica.\t\t\t\t\t<\/p>\n\t\t\t\t\n\t\t\t<\/div>\n\t\t\t\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-fa081b1 elementor-widget__width-initial elementor-widget elementor-widget-icon-box\" data-id=\"fa081b1\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon-box.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-box-wrapper\">\n\n\t\t\t\n\t\t\t\t\t\t<div class=\"elementor-icon-box-content\">\n\n\t\t\t\t\t\t\t\t\t<h3 class=\"elementor-icon-box-title\">\n\t\t\t\t\t\t<span  >\n\t\t\t\t\t\t\t13 \u2192 75  <br>SOBRE 100\t\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/h3>\n\t\t\t\t\n\t\t\t\t\t\t\t\t\t<p class=\"elementor-icon-box-description\">\n\t\t\t\t\t\tPUNTUACI\u00d3N<br>\n\u2500\u2500\u2500\n<br><br>Las comprobaciones part\u00edan de 13\/100. Despu\u00e9s de intervenir y volver a medir, alcanzaron 75\/100.\t\t\t\t\t<\/p>\n\t\t\t\t\n\t\t\t<\/div>\n\t\t\t\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-202a5bb elementor-widget__width-initial elementor-widget elementor-widget-icon-box\" data-id=\"202a5bb\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon-box.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-box-wrapper\">\n\n\t\t\t\n\t\t\t\t\t\t<div class=\"elementor-icon-box-content\">\n\n\t\t\t\t\t\t\t\t\t<h3 class=\"elementor-icon-box-title\">\n\t\t\t\t\t\t<span  >\n\t\t\t\t\t\t\t44 \u2192 15<br>EN REVIT\t\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/h3>\n\t\t\t\t\n\t\t\t\t\t\t\t\t\t<p class=\"elementor-icon-box-description\">\n\t\t\t\t\t\tADVERTENCIAS<br>\n\u2500\u2500\u2500\n<br><br>Las advertencias se redujeron tras la ejecuci\u00f3n y una nueva comprobaci\u00f3n del modelo.\t\t\t\t\t<\/p>\n\t\t\t\t\n\t\t\t<\/div>\n\t\t\t\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-8bc88b7 elementor-widget__width-initial elementor-widget elementor-widget-icon-box\" data-id=\"8bc88b7\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon-box.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-box-wrapper\">\n\n\t\t\t\n\t\t\t\t\t\t<div class=\"elementor-icon-box-content\">\n\n\t\t\t\t\t\t\t\t\t<h3 class=\"elementor-icon-box-title\">\n\t\t\t\t\t\t<span  >\n\t\t\t\t\t\t\t75\/100<br>NO ERA EL FINAL\t\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/h3>\n\t\t\t\t\n\t\t\t\t\t\t\t\t\t<p class=\"elementor-icon-box-description\">\n\t\t\t\t\t\tRESULTADO<br>\n\u2500\u2500\u2500\n<br><br>La meta era 85\/100. El panel mantuvo visibles los requisitos pendientes y los que necesitaban revisi\u00f3n adicional.\t\t\t\t\t<\/p>\n\t\t\t\t\n\t\t\t<\/div>\n\t\t\t\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n\t\t<div class=\"elementor-element elementor-element-aa12a8f elementor-widget elementor-widget-html\" data-id=\"aa12a8f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<style>\r\n.edibim-sticky{\r\n    position: sticky !important;\r\n    top: 80px !important;\r\n    align-self: flex-start !important;\r\n    z-index: 5 !important;\r\n}\r\n<\/style>\r\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-545ce3e elementor-widget elementor-widget-html\" data-id=\"545ce3e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<canvas id=\"edibimInvestorsCanvas\"><\/canvas>\r\n\r\n<style>\r\n.edibim-investors-points{\r\n    position:relative !important;\r\n    overflow:visible !important;\r\n    isolation:isolate;\r\n}\r\n\r\n#edibimInvestorsCanvas{\r\n    position:absolute !important;\r\n    inset:0 !important;\r\n    width:100% !important;\r\n    height:100% !important;\r\n    display:block !important;\r\n    pointer-events:none !important;\r\n    z-index:0 !important;\r\n}\r\n\r\n.edibim-investors-points > *:not(#edibimInvestorsCanvas){\r\n    position:relative;\r\n    z-index:2;\r\n}\r\n<\/style>\r\n\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/three.js\/r128\/three.min.js\"><\/script>\r\n\r\n<script>\r\n(function(){\r\n\r\n    function init(){\r\n\r\n        const section =\r\n            document.querySelector('.edibim-investors-points');\r\n\r\n        const canvas =\r\n            document.getElementById('edibimInvestorsCanvas');\r\n\r\n        if(!section || !canvas){\r\n            console.log('EdiBIM: no se encuentra secci\u00f3n o canvas');\r\n            return;\r\n        }\r\n\r\n        if(typeof THREE === 'undefined'){\r\n            setTimeout(init,100);\r\n            return;\r\n        }\r\n\r\n        section.prepend(canvas);\r\n\r\n\r\n        \/* =====================================================\r\n           VALORES TOMADOS DEL JS ORIGINAL\r\n           ===================================================== *\/\r\n\r\n        const CAMERA_FOV = 60;\r\n        const CAMERA_Z = 16;\r\n\r\n        const PLANE_WIDTH = 8;\r\n        const PLANE_HEIGHT = 10;\r\n\r\n        const SEGMENTS_X = 20;\r\n        const SEGMENTS_Y = 20;\r\n\r\n        const LAYERS = 5;\r\n        const LAYER_AXIS = 'z';\r\n\r\n        const POINT_COLOR = 13750737;   \/\/ original\r\n        const POINT_SIZE = 0.35;\r\n        const POINT_ATTENUATION = 170;\r\n\r\n        const FOG_COLOR = 1118481;\r\n        const FOG_NEAR = 0;\r\n        const FOG_FAR = 25;\r\n\r\n        const ROTATION_SPEED = 0.03;\r\n\r\n\r\n        \/* =====================================================\r\n           TAMA\u00d1O DEL CONTENEDOR\r\n           ===================================================== *\/\r\n\r\n        function getSize(){\r\n\r\n            return {\r\n                width:\r\n                    Math.max(\r\n                        1,\r\n                        section.clientWidth\r\n                    ),\r\n\r\n                height:\r\n                    Math.max(\r\n                        1,\r\n                        section.clientHeight\r\n                    )\r\n            };\r\n\r\n        }\r\n\r\n\r\n        const size =\r\n            getSize();\r\n\r\n\r\n        \/* =====================================================\r\n           ESCENA\r\n           ===================================================== *\/\r\n\r\n        const scene =\r\n            new THREE.Scene();\r\n\r\n        scene.fog =\r\n            new THREE.Fog(\r\n                FOG_COLOR,\r\n                FOG_NEAR,\r\n                FOG_FAR\r\n            );\r\n\r\n\r\n        \/* =====================================================\r\n           C\u00c1MARA\r\n           ===================================================== *\/\r\n\r\n        const camera =\r\n            new THREE.PerspectiveCamera(\r\n                CAMERA_FOV,\r\n                size.width \/ size.height,\r\n                0.1,\r\n                1000\r\n            );\r\n\r\n        camera.position.set(\r\n            0,\r\n            0,\r\n            CAMERA_Z\r\n        );\r\n\r\n\r\n        \/* =====================================================\r\n           RENDERER\r\n           ===================================================== *\/\r\n\r\n        const renderer =\r\n            new THREE.WebGLRenderer({\r\n                canvas:canvas,\r\n                powerPreference:'high-performance',\r\n                antialias:true,\r\n                alpha:true\r\n            });\r\n\r\n        renderer.setSize(\r\n            size.width,\r\n            size.height,\r\n            false\r\n        );\r\n\r\n        renderer.setPixelRatio(\r\n            Math.min(\r\n                window.devicePixelRatio || 1,\r\n                1.5\r\n            )\r\n        );\r\n\r\n        renderer.setClearColor(\r\n            0x000000,\r\n            0\r\n        );\r\n\r\n\r\n        \/* =====================================================\r\n           GEOMETR\u00cdA ORIGINAL\r\n           5 PLANOS PARALELOS DE PUNTOS\r\n           ===================================================== *\/\r\n\r\n        const positions = [];\r\n\r\n        const layerStep =\r\n            PLANE_WIDTH \/\r\n            (LAYERS - 1);\r\n\r\n        const layerStart =\r\n            -PLANE_WIDTH \/ 2;\r\n\r\n\r\n        for(\r\n            let layer = 0;\r\n            layer < LAYERS;\r\n            layer++\r\n        ){\r\n\r\n            const plane =\r\n                new THREE.PlaneGeometry(\r\n                    PLANE_WIDTH,\r\n                    PLANE_HEIGHT,\r\n                    SEGMENTS_X,\r\n                    SEGMENTS_Y\r\n                );\r\n\r\n            const offset =\r\n                layerStart +\r\n                layer * layerStep;\r\n\r\n            const arr =\r\n                plane.attributes.position.array;\r\n\r\n\r\n            for(\r\n                let i = 0;\r\n                i < arr.length;\r\n                i += 3\r\n            ){\r\n\r\n                let x =\r\n                    arr[i];\r\n\r\n                let y =\r\n                    arr[i + 1];\r\n\r\n                let z =\r\n                    arr[i + 2];\r\n\r\n\r\n                if(LAYER_AXIS === 'x'){\r\n                    x += offset;\r\n                }\r\n\r\n                if(LAYER_AXIS === 'y'){\r\n                    y += offset;\r\n                }\r\n\r\n                if(LAYER_AXIS === 'z'){\r\n                    z += offset;\r\n                }\r\n\r\n\r\n                positions.push(\r\n                    x,\r\n                    y,\r\n                    z\r\n                );\r\n\r\n            }\r\n\r\n            plane.dispose();\r\n\r\n        }\r\n\r\n\r\n        const geometry =\r\n            new THREE.BufferGeometry();\r\n\r\n        geometry.setAttribute(\r\n            'position',\r\n            new THREE.BufferAttribute(\r\n                new Float32Array(\r\n                    positions\r\n                ),\r\n                3\r\n            )\r\n        );\r\n\r\n\r\n        \/* =====================================================\r\n           SHADER ORIGINAL DE PUNTOS\r\n           ===================================================== *\/\r\n\r\n        const material =\r\n            new THREE.ShaderMaterial({\r\n\r\n                uniforms:\r\n                    THREE.UniformsUtils.merge([\r\n\r\n                        THREE.UniformsLib.fog,\r\n\r\n                        {\r\n                            color:{\r\n                                value:\r\n                                    new THREE.Color(\r\n                                        POINT_COLOR\r\n                                    )\r\n                            },\r\n\r\n                            size:{\r\n                                value:\r\n                                    POINT_SIZE\r\n                            },\r\n\r\n                            sizeAttenuation:{\r\n                                value:\r\n                                    POINT_ATTENUATION\r\n                            }\r\n                        }\r\n\r\n                    ]),\r\n\r\n                vertexShader:`\r\n\r\n                    uniform float size;\r\n                    uniform float sizeAttenuation;\r\n\r\n                    #include <fog_pars_vertex>\r\n\r\n                    void main(){\r\n\r\n                        vec4 mvPosition =\r\n                            modelViewMatrix *\r\n                            vec4(\r\n                                position,\r\n                                1.0\r\n                            );\r\n\r\n                        gl_PointSize =\r\n                            size *\r\n                            (\r\n                                sizeAttenuation \/\r\n                                -mvPosition.z\r\n                            );\r\n\r\n                        gl_Position =\r\n                            projectionMatrix *\r\n                            mvPosition;\r\n\r\n                        #include <fog_vertex>\r\n\r\n                    }\r\n\r\n                `,\r\n\r\n                fragmentShader:`\r\n\r\n                    uniform vec3 color;\r\n\r\n                    #include <fog_pars_fragment>\r\n\r\n                    void main(){\r\n\r\n                        vec2 center =\r\n                            gl_PointCoord -\r\n                            vec2(0.5);\r\n\r\n                        if(\r\n                            dot(\r\n                                center,\r\n                                center\r\n                            ) >\r\n                            0.25\r\n                        ){\r\n                            discard;\r\n                        }\r\n\r\n                        gl_FragColor =\r\n                            vec4(\r\n                                color,\r\n                                1.0\r\n                            );\r\n\r\n                        #include <fog_fragment>\r\n\r\n                    }\r\n\r\n                `,\r\n\r\n                fog:true,\r\n                transparent:false\r\n\r\n            });\r\n\r\n\r\n        \/* =====================================================\r\n           NUBE DE PUNTOS\r\n           ===================================================== *\/\r\n\r\n        const points =\r\n            new THREE.Points(\r\n                geometry,\r\n                material\r\n            );\r\n\r\n        scene.add(points);\r\n\r\n\r\n        \/* =====================================================\r\n           ROTACI\u00d3N CONTINUA DEL ORIGINAL\r\n           ===================================================== *\/\r\n\r\n        let time = 0;\r\n\r\n\r\n        function animate(){\r\n\r\n            requestAnimationFrame(\r\n                animate\r\n            );\r\n\r\n\r\n            time += 0.01;\r\n\r\n\r\n            const rotation =\r\n                ROTATION_SPEED *\r\n                time *\r\n                360 %\r\n                360;\r\n\r\n\r\n            const r =\r\n                THREE.MathUtils.degToRad(\r\n                    rotation\r\n                );\r\n\r\n\r\n            points.rotation.set(\r\n                r,\r\n                r,\r\n                r\r\n            );\r\n\r\n\r\n            renderer.render(\r\n                scene,\r\n                camera\r\n            );\r\n\r\n        }\r\n\r\n\r\n        animate();\r\n\r\n\r\n        \/* =====================================================\r\n           RESPONSIVE\r\n           ===================================================== *\/\r\n\r\n        const observer =\r\n            new ResizeObserver(\r\n                function(){\r\n\r\n                    const newSize =\r\n                        getSize();\r\n\r\n\r\n                    camera.aspect =\r\n                        newSize.width \/\r\n                        newSize.height;\r\n\r\n\r\n                    camera.updateProjectionMatrix();\r\n\r\n\r\n                    renderer.setSize(\r\n                        newSize.width,\r\n                        newSize.height,\r\n                        false\r\n                    );\r\n\r\n                }\r\n            );\r\n\r\n\r\n        observer.observe(\r\n            section\r\n        );\r\n\r\n    }\r\n\r\n\r\n    if(\r\n        document.readyState === 'loading'\r\n    ){\r\n\r\n        document.addEventListener(\r\n            'DOMContentLoaded',\r\n            init\r\n        );\r\n\r\n    }else{\r\n\r\n        init();\r\n\r\n    }\r\n\r\n})();\r\n<\/script>\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-f8cbcfe e-con e-atomic-element e-flexbox-base e-ec04571 \" data-id=\"f8cbcfe\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"f8cbcfe\" id=\"accion-edibim\">\n    <div class=\"elementor-element elementor-element-1b21025 e-con e-atomic-element e-flexbox-base e-1b21025-395fa5c \" data-id=\"1b21025\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"1b21025\" id=\"quepuedehacer\">\n    <div class=\"elementor-element elementor-element-a9391a3 e-con e-atomic-element e-flexbox-base e-a9391a3-bcdeccf \" data-id=\"a9391a3\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"a9391a3\">\n    \t\t<div class=\"elementor-element elementor-element-8f6fde6 elementor-widget elementor-widget-text-editor\" data-id=\"8f6fde6\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>CAPACIDADES REALES<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-068302d elementor-widget elementor-widget-text-editor\" data-id=\"068302d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h2 class=\"edibim-title\"><strong>LO EJECUTAS<br \/><\/strong><strong>LO CONTROLAS<\/strong><\/h2>\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n\t\t<div class=\"elementor-element elementor-element-310ad9b elementor-widget elementor-widget-text-editor\" data-id=\"310ad9b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>EdiBIM no se presenta como una lista de herramientas. Cada capacidad parte de un problema BIM concreto, ejecuta trabajo dentro de Revit y exige que el alumno sepa preparar, dirigir y comprobar el resultado.<\/p><p class=\"PDq2pG_selectionAnchorContainer\" data-start=\"770\" data-end=\"847\"><strong data-start=\"770\" data-end=\"847\">La herramienta ejecuta. T\u00fa defines el criterio y compruebas el resultado.<\/strong><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-fcba23e elementor-widget elementor-widget-html\" data-id=\"fcba23e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<style>\r\n\r\n\/* =========================================================\r\n   CONTENEDOR DE LAS 4 TARJETAS\r\n   ========================================================= *\/\r\n\r\n.edibim-capacidades-cards{\r\n    display:flex !important;\r\n    flex-direction:row !important;\r\n    flex-wrap:nowrap !important;\r\n\r\n    gap:0 !important;\r\n\r\n    width:100% !important;\r\n\r\n    overflow:hidden !important;\r\n\r\n    align-items:stretch !important;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   TARJETAS\r\n   ========================================================= *\/\r\n\r\n.edibim-capacidad-card{\r\n\r\n    position:relative !important;\r\n\r\n    width:25% !important;\r\n    flex:0 0 25% !important;\r\n\r\n    min-width:0 !important;\r\n\r\n    overflow:hidden !important;\r\n\r\n    display:flex !important;\r\n    flex-direction:column !important;\r\n    justify-content:flex-start !important;\r\n\r\n    transition:\r\n        width .55s cubic-bezier(.65,0,.35,1),\r\n        flex-basis .55s cubic-bezier(.65,0,.35,1);\r\n\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   CONTENIDO INTERIOR\r\n\r\n   Mantiene un ancho estable para que\r\n   los textos no cambien de l\u00ednea durante el hover.\r\n   ========================================================= *\/\r\n\r\n.edibim-capacidad-card > .e-con-inner{\r\n\r\n    width:var(--edibim-content-width, 25%) !important;\r\n\r\n    min-width:var(--edibim-content-width, 25%) !important;\r\n    max-width:var(--edibim-content-width, 25%) !important;\r\n\r\n    flex:none !important;\r\n\r\n}\r\n\r\n\r\n\/* Si Elementor no utiliza .e-con-inner *\/\r\n.edibim-capacidad-card > .elementor-element{\r\n\r\n    flex-shrink:0 !important;\r\n\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   TEXTOS\r\n\r\n   Sin transiciones ni cambios raros.\r\n   ========================================================= *\/\r\n\r\n.edibim-capacidad-card .elementor-heading-title,\r\n.edibim-capacidad-card .elementor-widget-text-editor,\r\n.edibim-capacidad-card .elementor-widget-text-editor p{\r\n\r\n    transition:none !important;\r\n\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   M\u00d3VIL\r\n   ========================================================= *\/\r\n\r\n@media (max-width:767px){\r\n\r\n    .edibim-capacidades-cards{\r\n\r\n        flex-direction:column !important;\r\n\r\n        overflow:visible !important;\r\n\r\n    }\r\n\r\n\r\n    .edibim-capacidad-card{\r\n\r\n        width:100% !important;\r\n        flex-basis:100% !important;\r\n\r\n    }\r\n\r\n\r\n    .edibim-capacidad-card > .e-con-inner{\r\n\r\n        width:100% !important;\r\n\r\n        min-width:0 !important;\r\n        max-width:none !important;\r\n\r\n    }\r\n\r\n}\r\n\r\n<\/style>\r\n\r\n\r\n<script>\r\n\r\ndocument.addEventListener('DOMContentLoaded', function(){\r\n\r\n    const grupo = document.querySelector(\r\n        '.edibim-capacidades-cards'\r\n    );\r\n\r\n    if(!grupo){\r\n        console.log('No encuentro .edibim-capacidades-cards');\r\n        return;\r\n    }\r\n\r\n\r\n    const tarjetas = Array.from(\r\n        grupo.querySelectorAll(\r\n            '.edibim-capacidad-card'\r\n        )\r\n    );\r\n\r\n\r\n    console.log(\r\n        'Tarjetas encontradas:',\r\n        tarjetas.length\r\n    );\r\n\r\n\r\n    if(tarjetas.length < 4){\r\n        return;\r\n    }\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       MEDIR EL ANCHO ORIGINAL DE UNA TARJETA\r\n\r\n       En vez de usar 25vw, calculamos el\r\n       25% REAL del contenedor.\r\n       ===================================================== *\/\r\n\r\n    function medirContenido(){\r\n\r\n        if(window.innerWidth <= 767){\r\n            return;\r\n        }\r\n\r\n\r\n        const anchoGrupo =\r\n            grupo.getBoundingClientRect().width;\r\n\r\n\r\n        const anchoBase =\r\n            anchoGrupo \/ 4;\r\n\r\n\r\n        tarjetas.forEach(function(card){\r\n\r\n            card.style.setProperty(\r\n                '--edibim-content-width',\r\n                anchoBase + 'px'\r\n            );\r\n\r\n        });\r\n\r\n    }\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       ESTADO NORMAL\r\n       ===================================================== *\/\r\n\r\n    function normal(){\r\n\r\n        if(window.innerWidth <= 767){\r\n            return;\r\n        }\r\n\r\n\r\n        tarjetas.forEach(function(card){\r\n\r\n            card.style.setProperty(\r\n                'width',\r\n                '25%',\r\n                'important'\r\n            );\r\n\r\n\r\n            card.style.setProperty(\r\n                'flex-basis',\r\n                '25%',\r\n                'important'\r\n            );\r\n\r\n        });\r\n\r\n    }\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       ACTIVAR TARJETA\r\n\r\n       ACTIVA = 40%\r\n       RESTO  = 20%\r\n       ===================================================== *\/\r\n\r\n    function activar(activa){\r\n\r\n        if(window.innerWidth <= 767){\r\n            return;\r\n        }\r\n\r\n\r\n        tarjetas.forEach(function(card){\r\n\r\n            const valor =\r\n                card === activa\r\n                    ? '40%'\r\n                    : '20%';\r\n\r\n\r\n            card.style.setProperty(\r\n                'width',\r\n                valor,\r\n                'important'\r\n            );\r\n\r\n\r\n            card.style.setProperty(\r\n                'flex-basis',\r\n                valor,\r\n                'important'\r\n            );\r\n\r\n        });\r\n\r\n    }\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       HOVER\r\n       ===================================================== *\/\r\n\r\n    tarjetas.forEach(function(card){\r\n\r\n        card.addEventListener(\r\n            'mouseenter',\r\n            function(){\r\n\r\n                activar(card);\r\n\r\n            }\r\n        );\r\n\r\n    });\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       AL SALIR DEL GRUPO\r\n       ===================================================== *\/\r\n\r\n    grupo.addEventListener(\r\n        'mouseleave',\r\n        function(){\r\n\r\n            normal();\r\n\r\n        }\r\n    );\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       INICIO\r\n       ===================================================== *\/\r\n\r\n    medirContenido();\r\n    normal();\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       REDIMENSIONAR\r\n       ===================================================== *\/\r\n\r\n    let resizeTimer;\r\n\r\n\r\n    window.addEventListener(\r\n        'resize',\r\n        function(){\r\n\r\n            clearTimeout(\r\n                resizeTimer\r\n            );\r\n\r\n\r\n            resizeTimer =\r\n                setTimeout(\r\n                    function(){\r\n\r\n                        medirContenido();\r\n                        normal();\r\n\r\n                    },\r\n                    150\r\n                );\r\n\r\n        }\r\n    );\r\n\r\n});\r\n\r\n<\/script>\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-66d71ce e-con e-atomic-element e-flexbox-base e-1731373 edibim-capacidades-cards \" data-id=\"66d71ce\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"66d71ce\">\n    <div class=\"elementor-element elementor-element-4a1c7d1 e-con e-atomic-element e-flexbox-base e-6f574f2 edibim-capacidad-card card-01 \" data-id=\"4a1c7d1\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"4a1c7d1\">\n    \t\t<div class=\"elementor-element elementor-element-4ee4fa0 elementor-widget elementor-widget-text-editor\" data-id=\"4ee4fa0\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p><strong>AUDITAR<\/strong><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-1ad8ff8 elementor-widget elementor-widget-text-editor\" data-id=\"1ad8ff8\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>01 \/ 04<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-c55ef6f elementor-widget elementor-widget-html\" data-id=\"c55ef6f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- =========================================================\r\n     EDIBIM \u00b7 TARJETA DE V\u00cdDEO \u00b7 BLOQUE 4 \u00b7 TARJETA 01\r\n     AISLADA DEL RESTO DE V\u00cdDEOS DE LA P\u00c1GINA\r\n\r\n     PARA CAMBIAR EL CONTENIDO:\r\n     1. data-title\r\n     2. data-video\r\n     ========================================================= -->\r\n\r\n\r\n<div id=\"edibim-video-b4-01\" class=\"edibim-video-b4-component\">\r\n\r\n\r\n    <!-- =====================================================\r\n         TARJETA\r\n         ===================================================== -->\r\n\r\n    <div class=\"edibim-showcase\">\r\n\r\n        <article\r\n            class=\"edibim-media-card\"\r\n            data-title=\"VER C\u00d3MO AUDITA\"\r\n            data-video=\"https:\/\/mcpbim.com\/wp-content\/uploads\/2026\/09\/Del-CAD-al-modelo-arquitectonico.mp4\"\r\n            tabindex=\"0\"\r\n            role=\"button\"\r\n            aria-label=\"Abrir v\u00eddeo\"\r\n        >\r\n\r\n            <!-- MINIATURA ANIMADA -->\r\n            <video\r\n                class=\"edibim-media-preview\"\r\n                autoplay\r\n                muted\r\n                loop\r\n                playsinline\r\n                preload=\"auto\"\r\n            ><\/video>\r\n\r\n\r\n            <!-- SOMBRA HOVER -->\r\n            <div class=\"edibim-card-shade\"><\/div>\r\n\r\n\r\n            <!-- T\u00cdTULO -->\r\n            <div class=\"edibim-card-title\">\r\n                VER EL CASO EN ACCI\u00d3N\r\n            <\/div>\r\n\r\n\r\n            <!-- ESQUINAS -->\r\n            <span class=\"edibim-corner tl\"><\/span>\r\n            <span class=\"edibim-corner tr\"><\/span>\r\n            <span class=\"edibim-corner bl\"><\/span>\r\n            <span class=\"edibim-corner br\"><\/span>\r\n\r\n        <\/article>\r\n\r\n    <\/div>\r\n\r\n\r\n\r\n    <!-- =====================================================\r\n         POPUP PROPIO DE ESTA TARJETA\r\n         ===================================================== -->\r\n\r\n    <div\r\n        class=\"edibim-modal\"\r\n        aria-hidden=\"true\"\r\n    >\r\n\r\n        <div class=\"edibim-modal-backdrop\"><\/div>\r\n\r\n\r\n        <div class=\"edibim-modal-window\">\r\n\r\n            <button\r\n                class=\"edibim-modal-close\"\r\n                type=\"button\"\r\n                aria-label=\"Cerrar v\u00eddeo\"\r\n            >\r\n                \u00d7\r\n            <\/button>\r\n\r\n\r\n            <div class=\"edibim-modal-label\">\r\n                EDIBIM\r\n            <\/div>\r\n\r\n\r\n            <div class=\"edibim-video-holder\"><\/div>\r\n\r\n        <\/div>\r\n\r\n    <\/div>\r\n\r\n\r\n<\/div>\r\n\r\n\r\n\r\n<style>\r\n\r\n\/* =========================================================\r\n   SOLO BLOQUE 4 \u00b7 TARJETA 01\r\n   ========================================================= *\/\r\n\r\n\r\n\/* =========================================================\r\n   CONTENEDOR\r\n   ========================================================= *\/\r\n\r\n#edibim-video-b4-01 .edibim-showcase{\r\n\r\n    width:100%;\r\n\r\n    display:grid;\r\n\r\n    grid-template-columns:\r\n        repeat(1,minmax(0,1fr));\r\n\r\n    gap:28px;\r\n}\r\n\r\n\r\n\r\n\/* =========================================================\r\n   TARJETA\r\n   ========================================================= *\/\r\n\r\n#edibim-video-b4-01 .edibim-media-card{\r\n\r\n    position:relative;\r\n\r\n    width:100%;\r\n\r\n    aspect-ratio:16 \/ 8.5;\r\n\r\n    background:#171717;\r\n\r\n    overflow:hidden;\r\n\r\n    cursor:pointer;\r\n\r\n    isolation:isolate;\r\n\r\n    border:\r\n        1px solid\r\n        rgba(255,255,255,.035);\r\n\r\n    transition:\r\n        background .45s ease,\r\n        transform .45s cubic-bezier(.2,.75,.25,1);\r\n}\r\n\r\n\r\n\r\n\/* =========================================================\r\n   MINIATURA\r\n   ========================================================= *\/\r\n\r\n#edibim-video-b4-01 .edibim-media-preview{\r\n\r\n    position:absolute;\r\n\r\n    top:18px;\r\n    left:18px;\r\n\r\n    width:42%;\r\n    height:45%;\r\n\r\n    background-position:center;\r\n    background-size:cover;\r\n    background-repeat:no-repeat;\r\n\r\n    object-fit:cover;\r\n\r\n    display:block;\r\n\r\n    filter:\r\n        grayscale(100%)\r\n        contrast(1.05)\r\n        brightness(.78);\r\n\r\n    transform:scale(1);\r\n\r\n    transform-origin:left top;\r\n\r\n    transition:\r\n        top .6s cubic-bezier(.2,.8,.2,1),\r\n        left .6s cubic-bezier(.2,.8,.2,1),\r\n        width .6s cubic-bezier(.2,.8,.2,1),\r\n        height .6s cubic-bezier(.2,.8,.2,1),\r\n        filter .6s ease,\r\n        transform .8s cubic-bezier(.2,.8,.2,1);\r\n\r\n    z-index:1;\r\n}\r\n\r\n\r\n\r\n#edibim-video-b4-01 .edibim-media-card:hover\r\n.edibim-media-preview,\r\n\r\n#edibim-video-b4-01 .edibim-media-card:focus-visible\r\n.edibim-media-preview{\r\n\r\n    top:0;\r\n    left:0;\r\n\r\n    width:100%;\r\n    height:100%;\r\n\r\n    filter:\r\n        grayscale(0%)\r\n        contrast(1.02)\r\n        brightness(.72);\r\n\r\n    transform:scale(1.035);\r\n}\r\n\r\n\r\n\r\n\/* =========================================================\r\n   CAPA OSCURA\r\n   ========================================================= *\/\r\n\r\n#edibim-video-b4-01 .edibim-card-shade{\r\n\r\n    position:absolute;\r\n\r\n    inset:0;\r\n\r\n    background:\r\n        linear-gradient(\r\n            to top,\r\n            rgba(10,10,10,.76) 0%,\r\n            rgba(10,10,10,.20) 48%,\r\n            rgba(10,10,10,.05) 100%\r\n        );\r\n\r\n    opacity:0;\r\n\r\n    transition:\r\n        opacity .45s ease;\r\n\r\n    z-index:2;\r\n}\r\n\r\n\r\n\r\n#edibim-video-b4-01 .edibim-media-card:hover\r\n.edibim-card-shade,\r\n\r\n#edibim-video-b4-01 .edibim-media-card:focus-visible\r\n.edibim-card-shade{\r\n\r\n    opacity:1;\r\n}\r\n\r\n\r\n\r\n\/* =========================================================\r\n   T\u00cdTULO\r\n   ========================================================= *\/\r\n\r\n#edibim-video-b4-01 .edibim-card-title{\r\n\r\n    position:absolute;\r\n\r\n    left:18px;\r\n    bottom:18px;\r\n\r\n    font-family:inherit;\r\n\r\n    font-size:16px;\r\n\r\n    font-weight:500;\r\n\r\n    line-height:1;\r\n\r\n    color:#F26B3A;\r\n\r\n    z-index:4;\r\n\r\n    transition:\r\n        transform .45s ease,\r\n        color .45s ease;\r\n}\r\n\r\n\r\n\r\n#edibim-video-b4-01 .edibim-media-card:hover\r\n.edibim-card-title{\r\n\r\n    transform:translateY(-2px);\r\n}\r\n\r\n\r\n\r\n\/* =========================================================\r\n   ESQUINAS\r\n   ========================================================= *\/\r\n\r\n#edibim-video-b4-01 .edibim-corner{\r\n\r\n    position:absolute;\r\n\r\n    width:14px;\r\n    height:14px;\r\n\r\n    z-index:5;\r\n\r\n    opacity:.55;\r\n\r\n    transition:\r\n        opacity .4s ease,\r\n        width .4s ease,\r\n        height .4s ease;\r\n}\r\n\r\n\r\n\r\n#edibim-video-b4-01 .edibim-corner.tl{\r\n\r\n    top:0;\r\n    left:0;\r\n\r\n    border-top:1px solid #F26B3A;\r\n    border-left:1px solid #F26B3A;\r\n}\r\n\r\n\r\n\r\n#edibim-video-b4-01 .edibim-corner.tr{\r\n\r\n    top:0;\r\n    right:0;\r\n\r\n    border-top:1px solid #F26B3A;\r\n    border-right:1px solid #F26B3A;\r\n}\r\n\r\n\r\n\r\n#edibim-video-b4-01 .edibim-corner.bl{\r\n\r\n    bottom:0;\r\n    left:0;\r\n\r\n    border-bottom:1px solid #F26B3A;\r\n    border-left:1px solid #F26B3A;\r\n}\r\n\r\n\r\n\r\n#edibim-video-b4-01 .edibim-corner.br{\r\n\r\n    bottom:0;\r\n    right:0;\r\n\r\n    border-bottom:1px solid #F26B3A;\r\n    border-right:1px solid #F26B3A;\r\n}\r\n\r\n\r\n\r\n#edibim-video-b4-01 .edibim-media-card:hover\r\n.edibim-corner{\r\n\r\n    width:20px;\r\n    height:20px;\r\n\r\n    opacity:1;\r\n}\r\n\r\n\r\n\r\n\/* =========================================================\r\n   ACCESIBILIDAD\r\n   ========================================================= *\/\r\n\r\n#edibim-video-b4-01 .edibim-media-card:focus{\r\n\r\n    outline:none;\r\n}\r\n\r\n\r\n\r\n#edibim-video-b4-01 .edibim-media-card:focus-visible{\r\n\r\n    outline:\r\n        1px solid\r\n        rgba(242,107,58,.8);\r\n\r\n    outline-offset:4px;\r\n}\r\n\r\n\r\n\r\n\/* =========================================================\r\n   POPUP\r\n\r\n   IMPORTANTE:\r\n   JS LO MUEVE AL BODY AL CARGAR.\r\n   POR ESO NO LO CORTA EL OVERFLOW DE LAS TARJETAS.\r\n   ========================================================= *\/\r\n\r\n.edibim-modal-b4-01{\r\n\r\n    position:fixed !important;\r\n\r\n    inset:0 !important;\r\n\r\n    width:100vw !important;\r\n    height:100vh !important;\r\n\r\n    z-index:2147483000 !important;\r\n\r\n    display:flex;\r\n\r\n    align-items:center;\r\n    justify-content:center;\r\n\r\n    padding:40px;\r\n\r\n    box-sizing:border-box;\r\n\r\n    opacity:0;\r\n\r\n    visibility:hidden;\r\n\r\n    pointer-events:none;\r\n\r\n    transition:\r\n        opacity .35s ease,\r\n        visibility .35s ease;\r\n}\r\n\r\n\r\n\r\n\/* POPUP ABIERTO *\/\r\n\r\n.edibim-modal-b4-01.is-open{\r\n\r\n    opacity:1;\r\n\r\n    visibility:visible;\r\n\r\n    pointer-events:auto;\r\n}\r\n\r\n\r\n\r\n\/* =========================================================\r\n   FONDO POPUP\r\n   ========================================================= *\/\r\n\r\n.edibim-modal-b4-01 .edibim-modal-backdrop{\r\n\r\n    position:absolute;\r\n\r\n    inset:0;\r\n\r\n    background:rgba(8,8,8,.88);\r\n\r\n    backdrop-filter:blur(8px);\r\n\r\n    -webkit-backdrop-filter:blur(8px);\r\n}\r\n\r\n\r\n\r\n\/* =========================================================\r\n   VENTANA\r\n   ========================================================= *\/\r\n\r\n.edibim-modal-b4-01 .edibim-modal-window{\r\n\r\n    position:relative;\r\n\r\n    width:min(1280px,92vw);\r\n\r\n    background:#101010;\r\n\r\n    z-index:2;\r\n\r\n    transform:\r\n        translateY(25px)\r\n        scale(.97);\r\n\r\n    opacity:0;\r\n\r\n    transition:\r\n        transform .45s cubic-bezier(.2,.8,.2,1),\r\n        opacity .35s ease;\r\n}\r\n\r\n\r\n\r\n.edibim-modal-b4-01.is-open\r\n.edibim-modal-window{\r\n\r\n    transform:\r\n        translateY(0)\r\n        scale(1);\r\n\r\n    opacity:1;\r\n}\r\n\r\n\r\n\r\n\/* =========================================================\r\n   T\u00cdTULO POPUP\r\n   ========================================================= *\/\r\n\r\n.edibim-modal-b4-01 .edibim-modal-label{\r\n\r\n    position:absolute;\r\n\r\n    left:24px;\r\n    top:22px;\r\n\r\n    z-index:4;\r\n\r\n    font-family:inherit;\r\n\r\n    color:#F26B3A;\r\n\r\n    font-size:13px;\r\n\r\n    font-weight:600;\r\n\r\n    letter-spacing:.04em;\r\n}\r\n\r\n\r\n\r\n\/* =========================================================\r\n   BOT\u00d3N CERRAR\r\n   ========================================================= *\/\r\n\r\n.edibim-modal-b4-01 .edibim-modal-close{\r\n\r\n    position:absolute;\r\n\r\n    top:15px;\r\n    right:18px;\r\n\r\n    z-index:10;\r\n\r\n    width:42px;\r\n    height:42px;\r\n\r\n    display:flex;\r\n\r\n    justify-content:center;\r\n    align-items:center;\r\n\r\n    border:0;\r\n\r\n    background:rgba(15,15,15,.75);\r\n\r\n    color:#fff;\r\n\r\n    font-size:31px;\r\n\r\n    font-weight:200;\r\n\r\n    line-height:1;\r\n\r\n    cursor:pointer;\r\n\r\n    transition:\r\n        background .25s ease,\r\n        transform .25s ease;\r\n}\r\n\r\n\r\n\r\n.edibim-modal-b4-01 .edibim-modal-close:hover{\r\n\r\n    background:#F26B3A;\r\n\r\n    transform:rotate(4deg);\r\n}\r\n\r\n\r\n\r\n\/* =========================================================\r\n   REPRODUCTOR\r\n   ========================================================= *\/\r\n\r\n.edibim-modal-b4-01 .edibim-video-holder{\r\n\r\n    width:100%;\r\n\r\n    aspect-ratio:16 \/ 9;\r\n\r\n    background:#000;\r\n}\r\n\r\n\r\n\r\n.edibim-modal-b4-01 .edibim-video-holder iframe,\r\n.edibim-modal-b4-01 .edibim-video-holder video{\r\n\r\n    width:100%;\r\n    height:100%;\r\n\r\n    display:block;\r\n\r\n    border:0;\r\n\r\n    object-fit:contain;\r\n}\r\n\r\n\r\n\r\n\/* =========================================================\r\n   BLOQUEO DE SCROLL\r\n   ========================================================= *\/\r\n\r\nhtml.edibim-modal-active,\r\nbody.edibim-modal-active{\r\n\r\n    overflow:hidden !important;\r\n}\r\n\r\n\r\n\r\n\/* =========================================================\r\n   TABLET\r\n   ========================================================= *\/\r\n\r\n@media(max-width:900px){\r\n\r\n    #edibim-video-b4-01 .edibim-showcase{\r\n\r\n        grid-template-columns:1fr;\r\n\r\n        gap:22px;\r\n    }\r\n\r\n\r\n    #edibim-video-b4-01 .edibim-media-card{\r\n\r\n        aspect-ratio:16 \/ 9;\r\n    }\r\n\r\n\r\n    #edibim-video-b4-01 .edibim-media-preview{\r\n\r\n        width:48%;\r\n        height:48%;\r\n    }\r\n\r\n}\r\n\r\n\r\n\r\n\/* =========================================================\r\n   M\u00d3VIL\r\n   ========================================================= *\/\r\n\r\n@media(max-width:600px){\r\n\r\n    .edibim-modal-b4-01{\r\n\r\n        padding:12px;\r\n    }\r\n\r\n\r\n    #edibim-video-b4-01 .edibim-media-card{\r\n\r\n        aspect-ratio:4 \/ 3;\r\n    }\r\n\r\n\r\n    #edibim-video-b4-01 .edibim-media-preview{\r\n\r\n        width:56%;\r\n        height:45%;\r\n\r\n        top:14px;\r\n        left:14px;\r\n    }\r\n\r\n\r\n    #edibim-video-b4-01 .edibim-card-title{\r\n\r\n        left:14px;\r\n        bottom:14px;\r\n\r\n        font-size:14px;\r\n    }\r\n\r\n\r\n    .edibim-modal-b4-01 .edibim-modal-label{\r\n\r\n        left:14px;\r\n        top:14px;\r\n    }\r\n\r\n}\r\n\r\n<\/style>\r\n\r\n\r\n\r\n<script>\r\n\r\n(function(){\r\n\r\n    \/* =====================================================\r\n       SOLO ESTE COMPONENTE\r\n       ===================================================== *\/\r\n\r\n    const component =\r\n        document.getElementById(\r\n            'edibim-video-b4-01'\r\n        );\r\n\r\n\r\n    if(!component){\r\n        return;\r\n    }\r\n\r\n\r\n\r\n    const cards =\r\n        component.querySelectorAll(\r\n            '.edibim-media-card'\r\n        );\r\n\r\n\r\n    const modal =\r\n        component.querySelector(\r\n            '.edibim-modal'\r\n        );\r\n\r\n\r\n    if(!modal){\r\n        return;\r\n    }\r\n\r\n\r\n\r\n    const holder =\r\n        modal.querySelector(\r\n            '.edibim-video-holder'\r\n        );\r\n\r\n\r\n    const modalTitle =\r\n        modal.querySelector(\r\n            '.edibim-modal-label'\r\n        );\r\n\r\n\r\n    const closeButton =\r\n        modal.querySelector(\r\n            '.edibim-modal-close'\r\n        );\r\n\r\n\r\n    const backdrop =\r\n        modal.querySelector(\r\n            '.edibim-modal-backdrop'\r\n        );\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       AISLAR EL POPUP\r\n\r\n       Lo movemos directamente al BODY.\r\n       As\u00ed ning\u00fan overflow:hidden de Elementor\r\n       puede cortarlo.\r\n       ===================================================== *\/\r\n\r\n    modal.classList.add(\r\n        'edibim-modal-b4-01'\r\n    );\r\n\r\n\r\n    document.body.appendChild(\r\n        modal\r\n    );\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       T\u00cdTULO DE LA TARJETA\r\n       ===================================================== *\/\r\n\r\n    cards.forEach(function(card){\r\n\r\n        const titulo =\r\n            card.dataset.title;\r\n\r\n\r\n        const tituloVisible =\r\n            card.querySelector(\r\n                '.edibim-card-title'\r\n            );\r\n\r\n\r\n        if(tituloVisible && titulo){\r\n\r\n            tituloVisible.textContent =\r\n                titulo;\r\n\r\n        }\r\n\r\n    });\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       MINIATURA ANIMADA\r\n       ===================================================== *\/\r\n\r\n    cards.forEach(function(card){\r\n\r\n        const videoUrl =\r\n            card.dataset.video;\r\n\r\n\r\n        const preview =\r\n            card.querySelector(\r\n                '.edibim-media-preview'\r\n            );\r\n\r\n\r\n        if(\r\n            !videoUrl ||\r\n            !preview\r\n        ){\r\n            return;\r\n        }\r\n\r\n\r\n\r\n        if(\r\n            \/\\.(mp4|webm|ogg)(\\?.*)?$\/i\r\n                .test(videoUrl)\r\n        ){\r\n\r\n            preview.src =\r\n                videoUrl;\r\n\r\n\r\n            preview.muted =\r\n                true;\r\n\r\n\r\n            preview.defaultMuted =\r\n                true;\r\n\r\n\r\n            preview.loop =\r\n                true;\r\n\r\n\r\n            preview.autoplay =\r\n                true;\r\n\r\n\r\n            preview.playsInline =\r\n                true;\r\n\r\n\r\n            preview.setAttribute(\r\n                'muted',\r\n                ''\r\n            );\r\n\r\n\r\n            preview.setAttribute(\r\n                'autoplay',\r\n                ''\r\n            );\r\n\r\n\r\n            preview.setAttribute(\r\n                'loop',\r\n                ''\r\n            );\r\n\r\n\r\n            preview.setAttribute(\r\n                'playsinline',\r\n                ''\r\n            );\r\n\r\n\r\n            preview.load();\r\n\r\n\r\n\r\n            function playPreview(){\r\n\r\n                const p =\r\n                    preview.play();\r\n\r\n\r\n                if(p){\r\n\r\n                    p.catch(\r\n                        function(){}\r\n                    );\r\n\r\n                }\r\n\r\n            }\r\n\r\n\r\n\r\n            preview.addEventListener(\r\n                'canplay',\r\n                playPreview\r\n            );\r\n\r\n\r\n            card.addEventListener(\r\n                'mouseenter',\r\n                playPreview\r\n            );\r\n\r\n\r\n            setTimeout(\r\n                playPreview,\r\n                300\r\n            );\r\n\r\n        }\r\n\r\n    });\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       YOUTUBE\r\n       ===================================================== *\/\r\n\r\n    function youtubeEmbed(url){\r\n\r\n        let id = null;\r\n\r\n\r\n        try{\r\n\r\n            const parsed =\r\n                new URL(url);\r\n\r\n\r\n\r\n            if(\r\n                parsed.hostname.includes(\r\n                    'youtu.be'\r\n                )\r\n            ){\r\n\r\n                id =\r\n                    parsed.pathname\r\n                        .replace(\r\n                            '\/',\r\n                            ''\r\n                        );\r\n\r\n            }\r\n\r\n\r\n\r\n            else if(\r\n                parsed.hostname.includes(\r\n                    'youtube.com'\r\n                )\r\n            ){\r\n\r\n                id =\r\n                    parsed\r\n                        .searchParams\r\n                        .get('v');\r\n\r\n\r\n                if(\r\n                    !id &&\r\n                    parsed.pathname.includes(\r\n                        '\/embed\/'\r\n                    )\r\n                ){\r\n\r\n                    id =\r\n                        parsed.pathname\r\n                            .split(\r\n                                '\/embed\/'\r\n                            )[1]\r\n                            .split('\/')[0];\r\n\r\n                }\r\n\r\n            }\r\n\r\n        }\r\n        catch(e){}\r\n\r\n\r\n\r\n        if(!id){\r\n\r\n            return null;\r\n\r\n        }\r\n\r\n\r\n\r\n        return (\r\n            'https:\/\/www.youtube.com\/embed\/' +\r\n            id +\r\n            '?autoplay=1&rel=0'\r\n        );\r\n\r\n    }\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       VIMEO\r\n       ===================================================== *\/\r\n\r\n    function vimeoEmbed(url){\r\n\r\n        const match =\r\n            url.match(\r\n                \/vimeo\\.com\\\/(?:video\\\/)?([0-9]+)\/\r\n            );\r\n\r\n\r\n        if(!match){\r\n\r\n            return null;\r\n\r\n        }\r\n\r\n\r\n\r\n        return (\r\n            'https:\/\/player.vimeo.com\/video\/' +\r\n            match[1] +\r\n            '?autoplay=1'\r\n        );\r\n\r\n    }\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       CREAR REPRODUCTOR\r\n       ===================================================== *\/\r\n\r\n    function crearVideo(url){\r\n\r\n        holder.innerHTML = '';\r\n\r\n\r\n        if(!url){\r\n\r\n            return;\r\n\r\n        }\r\n\r\n\r\n\r\n        \/* YOUTUBE *\/\r\n\r\n        const youtube =\r\n            youtubeEmbed(url);\r\n\r\n\r\n        if(youtube){\r\n\r\n            const iframe =\r\n                document.createElement(\r\n                    'iframe'\r\n                );\r\n\r\n\r\n            iframe.src =\r\n                youtube;\r\n\r\n\r\n            iframe.allow =\r\n                'autoplay; fullscreen; picture-in-picture';\r\n\r\n\r\n            iframe.allowFullscreen =\r\n                true;\r\n\r\n\r\n            holder.appendChild(\r\n                iframe\r\n            );\r\n\r\n\r\n            return;\r\n\r\n        }\r\n\r\n\r\n\r\n        \/* VIMEO *\/\r\n\r\n        const vimeo =\r\n            vimeoEmbed(url);\r\n\r\n\r\n        if(vimeo){\r\n\r\n            const iframe =\r\n                document.createElement(\r\n                    'iframe'\r\n                );\r\n\r\n\r\n            iframe.src =\r\n                vimeo;\r\n\r\n\r\n            iframe.allow =\r\n                'autoplay; fullscreen; picture-in-picture';\r\n\r\n\r\n            iframe.allowFullscreen =\r\n                true;\r\n\r\n\r\n            holder.appendChild(\r\n                iframe\r\n            );\r\n\r\n\r\n            return;\r\n\r\n        }\r\n\r\n\r\n\r\n        \/* MP4 \/ WEBM \/ OGG *\/\r\n\r\n        if(\r\n            \/\\.(mp4|webm|ogg)(\\?.*)?$\/i\r\n                .test(url)\r\n        ){\r\n\r\n            const video =\r\n                document.createElement(\r\n                    'video'\r\n                );\r\n\r\n\r\n            video.src =\r\n                url;\r\n\r\n\r\n            video.controls =\r\n                true;\r\n\r\n\r\n            video.autoplay =\r\n                true;\r\n\r\n\r\n            video.playsInline =\r\n                true;\r\n\r\n\r\n            \/*\r\n             * Muted permite que autoplay\r\n             * funcione de forma consistente.\r\n             * El usuario puede activar sonido\r\n             * desde los controles.\r\n             *\/\r\n\r\n            video.muted =\r\n                true;\r\n\r\n\r\n            holder.appendChild(\r\n                video\r\n            );\r\n\r\n\r\n            const playPromise =\r\n                video.play();\r\n\r\n\r\n            if(playPromise){\r\n\r\n                playPromise.catch(\r\n                    function(){}\r\n                );\r\n\r\n            }\r\n\r\n\r\n            return;\r\n\r\n        }\r\n\r\n\r\n\r\n        \/* URL GEN\u00c9RICA *\/\r\n\r\n        const iframe =\r\n            document.createElement(\r\n                'iframe'\r\n            );\r\n\r\n\r\n        iframe.src =\r\n            url;\r\n\r\n\r\n        iframe.allow =\r\n            'autoplay; fullscreen; picture-in-picture';\r\n\r\n\r\n        iframe.allowFullscreen =\r\n            true;\r\n\r\n\r\n        holder.appendChild(\r\n            iframe\r\n        );\r\n\r\n    }\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       ABRIR POPUP\r\n       ===================================================== *\/\r\n\r\n    function abrir(card){\r\n\r\n        const title =\r\n            card.dataset.title ||\r\n            'EDIBIM';\r\n\r\n\r\n        const video =\r\n            card.dataset.video;\r\n\r\n\r\n\r\n        modalTitle.textContent =\r\n            title;\r\n\r\n\r\n\r\n        crearVideo(\r\n            video\r\n        );\r\n\r\n\r\n\r\n        modal.classList.add(\r\n            'is-open'\r\n        );\r\n\r\n\r\n        modal.setAttribute(\r\n            'aria-hidden',\r\n            'false'\r\n        );\r\n\r\n\r\n\r\n        document\r\n            .documentElement\r\n            .classList\r\n            .add(\r\n                'edibim-modal-active'\r\n            );\r\n\r\n\r\n        document\r\n            .body\r\n            .classList\r\n            .add(\r\n                'edibim-modal-active'\r\n            );\r\n\r\n    }\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       CERRAR POPUP\r\n       ===================================================== *\/\r\n\r\n    function cerrar(){\r\n\r\n        modal.classList.remove(\r\n            'is-open'\r\n        );\r\n\r\n\r\n        modal.setAttribute(\r\n            'aria-hidden',\r\n            'true'\r\n        );\r\n\r\n\r\n\r\n        document\r\n            .documentElement\r\n            .classList\r\n            .remove(\r\n                'edibim-modal-active'\r\n            );\r\n\r\n\r\n        document\r\n            .body\r\n            .classList\r\n            .remove(\r\n                'edibim-modal-active'\r\n            );\r\n\r\n\r\n\r\n        setTimeout(\r\n            function(){\r\n\r\n                holder.innerHTML = '';\r\n\r\n            },\r\n            300\r\n        );\r\n\r\n    }\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       CLICK SOLO EN ESTA TARJETA\r\n       ===================================================== *\/\r\n\r\n    cards.forEach(function(card){\r\n\r\n        card.addEventListener(\r\n            'click',\r\n            function(e){\r\n\r\n                e.preventDefault();\r\n\r\n                e.stopPropagation();\r\n\r\n                abrir(\r\n                    card\r\n                );\r\n\r\n            }\r\n        );\r\n\r\n\r\n\r\n        card.addEventListener(\r\n            'keydown',\r\n            function(e){\r\n\r\n                if(\r\n                    e.key === 'Enter' ||\r\n                    e.key === ' '\r\n                ){\r\n\r\n                    e.preventDefault();\r\n\r\n                    e.stopPropagation();\r\n\r\n                    abrir(\r\n                        card\r\n                    );\r\n\r\n                }\r\n\r\n            }\r\n        );\r\n\r\n    });\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       CERRAR CON X\r\n       ===================================================== *\/\r\n\r\n    closeButton.addEventListener(\r\n        'click',\r\n        function(e){\r\n\r\n            e.preventDefault();\r\n\r\n            e.stopPropagation();\r\n\r\n            cerrar();\r\n\r\n        }\r\n    );\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       CERRAR AL PULSAR FUERA\r\n       ===================================================== *\/\r\n\r\n    backdrop.addEventListener(\r\n        'click',\r\n        function(e){\r\n\r\n            e.preventDefault();\r\n\r\n            cerrar();\r\n\r\n        }\r\n    );\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       ESC\r\n       ===================================================== *\/\r\n\r\n    document.addEventListener(\r\n        'keydown',\r\n        function(e){\r\n\r\n            if(\r\n                e.key === 'Escape' &&\r\n                modal.classList.contains(\r\n                    'is-open'\r\n                )\r\n            ){\r\n\r\n                cerrar();\r\n\r\n            }\r\n\r\n        }\r\n    );\r\n\r\n})();\r\n\r\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e83743c elementor-widget elementor-widget-text-editor\" data-id=\"e83743c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p class=\"PDq2pG_selectionAnchorContainer\" data-start=\"378\" data-end=\"454\"><strong data-start=\"378\" data-end=\"393\">EL PROBLEMA<\/strong><br data-start=\"393\" data-end=\"396\" \/>Un BEP extenso y un modelo que no sabes por d\u00f3nde revisar.<\/p><p data-start=\"456\" data-end=\"537\"><strong data-start=\"456\" data-end=\"474\">LO QUE EJECUTA<\/strong><br data-start=\"474\" data-end=\"477\" \/>Consulta, comprueba, corrige lo delimitado y vuelve a medir.<\/p><p data-start=\"539\" data-end=\"636\"><strong data-start=\"539\" data-end=\"558\">LO QUE APRENDES<\/strong><br data-start=\"558\" data-end=\"561\" \/>Convertir requisitos en reglas y reconocer qu\u00e9 necesita revisi\u00f3n adicional.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-bb8c947 e-con e-atomic-element e-flexbox-base e-3ba4209 edibim-capacidad-card card-02 \" data-id=\"bb8c947\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"bb8c947\">\n    \t\t<div class=\"elementor-element elementor-element-bac263f elementor-widget elementor-widget-text-editor\" data-id=\"bac263f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p><strong>CREAR<\/strong><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-11d3614 elementor-widget elementor-widget-text-editor\" data-id=\"11d3614\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>02 \/ 04<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-cfcf181 elementor-widget elementor-widget-html\" data-id=\"cfcf181\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- =========================================================\r\n     EDIBIM \u00b7 BLOQUE 4 \u00b7 TARJETA 02\r\n     CREAR DESDE DOCUMENTACI\u00d3N\r\n     ========================================================= -->\r\n\r\n<div id=\"edibim-video-b4-02\" class=\"edibim-video-b4-component\">\r\n\r\n    <div class=\"edibim-showcase\">\r\n\r\n        <article\r\n            class=\"edibim-media-card\"\r\n            data-title=\"VER C\u00d3MO MODELA\"\r\n            data-video=\"https:\/\/mcpbim.com\/wp-content\/uploads\/2026\/09\/Montajes-y-documentacion.mp4\"\r\n            tabindex=\"0\"\r\n            role=\"button\"\r\n            aria-label=\"Abrir v\u00eddeo\"\r\n        >\r\n\r\n            <video\r\n                class=\"edibim-media-preview\"\r\n                autoplay\r\n                muted\r\n                loop\r\n                playsinline\r\n                preload=\"auto\"\r\n            ><\/video>\r\n\r\n            <div class=\"edibim-card-shade\"><\/div>\r\n\r\n            <div class=\"edibim-card-title\">\r\n                VER EL CASO EN ACCI\u00d3N\r\n            <\/div>\r\n\r\n            <span class=\"edibim-corner tl\"><\/span>\r\n            <span class=\"edibim-corner tr\"><\/span>\r\n            <span class=\"edibim-corner bl\"><\/span>\r\n            <span class=\"edibim-corner br\"><\/span>\r\n\r\n        <\/article>\r\n\r\n    <\/div>\r\n\r\n\r\n    <div class=\"edibim-modal\" aria-hidden=\"true\">\r\n\r\n        <div class=\"edibim-modal-backdrop\"><\/div>\r\n\r\n        <div class=\"edibim-modal-window\">\r\n\r\n            <button\r\n                class=\"edibim-modal-close\"\r\n                type=\"button\"\r\n                aria-label=\"Cerrar v\u00eddeo\"\r\n            >\u00d7<\/button>\r\n\r\n            <div class=\"edibim-modal-label\">\r\n                EDIBIM\r\n            <\/div>\r\n\r\n            <div class=\"edibim-video-holder\"><\/div>\r\n\r\n        <\/div>\r\n\r\n    <\/div>\r\n\r\n<\/div>\r\n\r\n\r\n<style>\r\n\r\n\/* TARJETA *\/\r\n\r\n#edibim-video-b4-02 .edibim-showcase{\r\n    width:100%;\r\n    display:grid;\r\n    grid-template-columns:repeat(1,minmax(0,1fr));\r\n    gap:28px;\r\n}\r\n\r\n#edibim-video-b4-02 .edibim-media-card{\r\n    position:relative;\r\n    width:100%;\r\n    aspect-ratio:16 \/ 8.5;\r\n    background:#171717;\r\n    overflow:hidden;\r\n    cursor:pointer;\r\n    isolation:isolate;\r\n    border:1px solid rgba(255,255,255,.035);\r\n    transition:\r\n        background .45s ease,\r\n        transform .45s cubic-bezier(.2,.75,.25,1);\r\n}\r\n\r\n#edibim-video-b4-02 .edibim-media-preview{\r\n    position:absolute;\r\n    top:18px;\r\n    left:18px;\r\n    width:42%;\r\n    height:45%;\r\n    object-fit:cover;\r\n    display:block;\r\n    filter:\r\n        grayscale(100%)\r\n        contrast(1.05)\r\n        brightness(.78);\r\n    transform:scale(1);\r\n    transform-origin:left top;\r\n    transition:\r\n        top .6s cubic-bezier(.2,.8,.2,1),\r\n        left .6s cubic-bezier(.2,.8,.2,1),\r\n        width .6s cubic-bezier(.2,.8,.2,1),\r\n        height .6s cubic-bezier(.2,.8,.2,1),\r\n        filter .6s ease,\r\n        transform .8s cubic-bezier(.2,.8,.2,1);\r\n    z-index:1;\r\n}\r\n\r\n#edibim-video-b4-02 .edibim-media-card:hover .edibim-media-preview,\r\n#edibim-video-b4-02 .edibim-media-card:focus-visible .edibim-media-preview{\r\n    top:0;\r\n    left:0;\r\n    width:100%;\r\n    height:100%;\r\n    filter:\r\n        grayscale(0%)\r\n        contrast(1.02)\r\n        brightness(.72);\r\n    transform:scale(1.035);\r\n}\r\n\r\n#edibim-video-b4-02 .edibim-card-shade{\r\n    position:absolute;\r\n    inset:0;\r\n    background:linear-gradient(\r\n        to top,\r\n        rgba(10,10,10,.76) 0%,\r\n        rgba(10,10,10,.20) 48%,\r\n        rgba(10,10,10,.05) 100%\r\n    );\r\n    opacity:0;\r\n    transition:opacity .45s ease;\r\n    z-index:2;\r\n}\r\n\r\n#edibim-video-b4-02 .edibim-media-card:hover .edibim-card-shade,\r\n#edibim-video-b4-02 .edibim-media-card:focus-visible .edibim-card-shade{\r\n    opacity:1;\r\n}\r\n\r\n#edibim-video-b4-02 .edibim-card-title{\r\n    position:absolute;\r\n    left:18px;\r\n    bottom:18px;\r\n    font-family:inherit;\r\n    font-size:16px;\r\n    font-weight:500;\r\n    line-height:1;\r\n    color:#F26B3A;\r\n    z-index:4;\r\n    transition:transform .45s ease;\r\n}\r\n\r\n#edibim-video-b4-02 .edibim-media-card:hover .edibim-card-title{\r\n    transform:translateY(-2px);\r\n}\r\n\r\n\r\n\/* ESQUINAS *\/\r\n\r\n#edibim-video-b4-02 .edibim-corner{\r\n    position:absolute;\r\n    width:14px;\r\n    height:14px;\r\n    z-index:5;\r\n    opacity:.55;\r\n    transition:\r\n        opacity .4s ease,\r\n        width .4s ease,\r\n        height .4s ease;\r\n}\r\n\r\n#edibim-video-b4-02 .edibim-corner.tl{\r\n    top:0; left:0;\r\n    border-top:1px solid #F26B3A;\r\n    border-left:1px solid #F26B3A;\r\n}\r\n\r\n#edibim-video-b4-02 .edibim-corner.tr{\r\n    top:0; right:0;\r\n    border-top:1px solid #F26B3A;\r\n    border-right:1px solid #F26B3A;\r\n}\r\n\r\n#edibim-video-b4-02 .edibim-corner.bl{\r\n    bottom:0; left:0;\r\n    border-bottom:1px solid #F26B3A;\r\n    border-left:1px solid #F26B3A;\r\n}\r\n\r\n#edibim-video-b4-02 .edibim-corner.br{\r\n    bottom:0; right:0;\r\n    border-bottom:1px solid #F26B3A;\r\n    border-right:1px solid #F26B3A;\r\n}\r\n\r\n#edibim-video-b4-02 .edibim-media-card:hover .edibim-corner{\r\n    width:20px;\r\n    height:20px;\r\n    opacity:1;\r\n}\r\n\r\n\r\n\/* POPUP *\/\r\n\r\n.edibim-modal-b4-02{\r\n    position:fixed !important;\r\n    inset:0 !important;\r\n    width:100vw !important;\r\n    height:100vh !important;\r\n    z-index:2147483000 !important;\r\n    display:flex;\r\n    align-items:center;\r\n    justify-content:center;\r\n    padding:40px;\r\n    box-sizing:border-box;\r\n    opacity:0;\r\n    visibility:hidden;\r\n    pointer-events:none;\r\n    transition:opacity .35s ease, visibility .35s ease;\r\n}\r\n\r\n.edibim-modal-b4-02.is-open{\r\n    opacity:1;\r\n    visibility:visible;\r\n    pointer-events:auto;\r\n}\r\n\r\n.edibim-modal-b4-02 .edibim-modal-backdrop{\r\n    position:absolute;\r\n    inset:0;\r\n    background:rgba(8,8,8,.88);\r\n    backdrop-filter:blur(8px);\r\n    -webkit-backdrop-filter:blur(8px);\r\n}\r\n\r\n.edibim-modal-b4-02 .edibim-modal-window{\r\n    position:relative;\r\n    width:min(1280px,92vw);\r\n    background:#101010;\r\n    z-index:2;\r\n    transform:translateY(25px) scale(.97);\r\n    opacity:0;\r\n    transition:\r\n        transform .45s cubic-bezier(.2,.8,.2,1),\r\n        opacity .35s ease;\r\n}\r\n\r\n.edibim-modal-b4-02.is-open .edibim-modal-window{\r\n    transform:translateY(0) scale(1);\r\n    opacity:1;\r\n}\r\n\r\n.edibim-modal-b4-02 .edibim-modal-label{\r\n    position:absolute;\r\n    left:24px;\r\n    top:22px;\r\n    z-index:4;\r\n    color:#F26B3A;\r\n    font-size:13px;\r\n    font-weight:600;\r\n    letter-spacing:.04em;\r\n}\r\n\r\n.edibim-modal-b4-02 .edibim-modal-close{\r\n    position:absolute;\r\n    top:15px;\r\n    right:18px;\r\n    z-index:10;\r\n    width:42px;\r\n    height:42px;\r\n    display:flex;\r\n    justify-content:center;\r\n    align-items:center;\r\n    border:0;\r\n    background:rgba(15,15,15,.75);\r\n    color:#fff;\r\n    font-size:31px;\r\n    font-weight:200;\r\n    line-height:1;\r\n    cursor:pointer;\r\n}\r\n\r\n.edibim-modal-b4-02 .edibim-modal-close:hover{\r\n    background:#F26B3A;\r\n}\r\n\r\n.edibim-modal-b4-02 .edibim-video-holder{\r\n    width:100%;\r\n    aspect-ratio:16 \/ 9;\r\n    background:#000;\r\n}\r\n\r\n.edibim-modal-b4-02 .edibim-video-holder iframe,\r\n.edibim-modal-b4-02 .edibim-video-holder video{\r\n    width:100%;\r\n    height:100%;\r\n    display:block;\r\n    border:0;\r\n    object-fit:contain;\r\n}\r\n\r\n@media(max-width:600px){\r\n\r\n    .edibim-modal-b4-02{\r\n        padding:12px;\r\n    }\r\n\r\n    #edibim-video-b4-02 .edibim-media-card{\r\n        aspect-ratio:4 \/ 3;\r\n    }\r\n\r\n    #edibim-video-b4-02 .edibim-media-preview{\r\n        width:56%;\r\n        height:45%;\r\n        top:14px;\r\n        left:14px;\r\n    }\r\n\r\n}\r\n\r\n<\/style>\r\n\r\n\r\n<script>\r\n(function(){\r\n\r\n    const component = document.getElementById('edibim-video-b4-02');\r\n    if(!component) return;\r\n\r\n    const cards = component.querySelectorAll('.edibim-media-card');\r\n    const modal = component.querySelector('.edibim-modal');\r\n    if(!modal) return;\r\n\r\n    const holder = modal.querySelector('.edibim-video-holder');\r\n    const modalTitle = modal.querySelector('.edibim-modal-label');\r\n    const closeButton = modal.querySelector('.edibim-modal-close');\r\n    const backdrop = modal.querySelector('.edibim-modal-backdrop');\r\n\r\n    modal.classList.add('edibim-modal-b4-02');\r\n    document.body.appendChild(modal);\r\n\r\n\r\n    cards.forEach(function(card){\r\n\r\n        const titulo = card.dataset.title;\r\n        const visible = card.querySelector('.edibim-card-title');\r\n\r\n        if(visible && titulo){\r\n            visible.textContent = titulo;\r\n        }\r\n\r\n        const url = card.dataset.video;\r\n        const preview = card.querySelector('.edibim-media-preview');\r\n\r\n        if(url && preview && \/\\.(mp4|webm|ogg)(\\?.*)?$\/i.test(url)){\r\n\r\n            preview.src = url;\r\n            preview.muted = true;\r\n            preview.defaultMuted = true;\r\n            preview.loop = true;\r\n            preview.autoplay = true;\r\n            preview.playsInline = true;\r\n\r\n            preview.setAttribute('muted','');\r\n            preview.setAttribute('autoplay','');\r\n            preview.setAttribute('loop','');\r\n            preview.setAttribute('playsinline','');\r\n\r\n            preview.load();\r\n\r\n            function playPreview(){\r\n                const p = preview.play();\r\n                if(p) p.catch(function(){});\r\n            }\r\n\r\n            preview.addEventListener('canplay',playPreview);\r\n            card.addEventListener('mouseenter',playPreview);\r\n            setTimeout(playPreview,300);\r\n        }\r\n    });\r\n\r\n\r\n    function crearVideo(url){\r\n\r\n        holder.innerHTML='';\r\n\r\n        const video=document.createElement('video');\r\n\r\n        video.src=url;\r\n        video.controls=true;\r\n        video.autoplay=true;\r\n        video.playsInline=true;\r\n        video.muted=true;\r\n\r\n        holder.appendChild(video);\r\n\r\n        const p=video.play();\r\n        if(p) p.catch(function(){});\r\n    }\r\n\r\n\r\n    function abrir(card){\r\n\r\n        modalTitle.textContent =\r\n            card.dataset.title || 'EDIBIM';\r\n\r\n        crearVideo(card.dataset.video);\r\n\r\n        modal.classList.add('is-open');\r\n        modal.setAttribute('aria-hidden','false');\r\n\r\n        document.documentElement.classList.add('edibim-modal-active');\r\n        document.body.classList.add('edibim-modal-active');\r\n    }\r\n\r\n\r\n    function cerrar(){\r\n\r\n        modal.classList.remove('is-open');\r\n        modal.setAttribute('aria-hidden','true');\r\n\r\n        document.documentElement.classList.remove('edibim-modal-active');\r\n        document.body.classList.remove('edibim-modal-active');\r\n\r\n        setTimeout(function(){\r\n            holder.innerHTML='';\r\n        },300);\r\n    }\r\n\r\n\r\n    cards.forEach(function(card){\r\n\r\n        card.addEventListener('click',function(e){\r\n            e.preventDefault();\r\n            e.stopPropagation();\r\n            abrir(card);\r\n        });\r\n\r\n        card.addEventListener('keydown',function(e){\r\n\r\n            if(e.key==='Enter' || e.key===' '){\r\n                e.preventDefault();\r\n                e.stopPropagation();\r\n                abrir(card);\r\n            }\r\n\r\n        });\r\n    });\r\n\r\n\r\n    closeButton.addEventListener('click',function(e){\r\n        e.preventDefault();\r\n        e.stopPropagation();\r\n        cerrar();\r\n    });\r\n\r\n    backdrop.addEventListener('click',cerrar);\r\n\r\n    document.addEventListener('keydown',function(e){\r\n        if(e.key==='Escape' && modal.classList.contains('is-open')){\r\n            cerrar();\r\n        }\r\n    });\r\n\r\n})();\r\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-811518d elementor-widget elementor-widget-text-editor\" data-id=\"811518d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p class=\"PDq2pG_selectionAnchorContainer\" data-start=\"673\" data-end=\"773\"><strong data-start=\"673\" data-end=\"688\">EL PROBLEMA<\/strong><br data-start=\"688\" data-end=\"691\" \/>DWG, DXF, fotograf\u00edas, tablas o cat\u00e1logos que todav\u00eda deben convertirse en modelo.<\/p><p data-start=\"775\" data-end=\"904\"><strong data-start=\"775\" data-end=\"793\">LO QUE EJECUTA<\/strong><br data-start=\"793\" data-end=\"796\" \/>El asistente interpreta las referencias disponibles y, mediante EdiBIM, construye elementos dentro de Revit.<\/p><p data-start=\"906\" data-end=\"1009\"><strong data-start=\"906\" data-end=\"925\">LO QUE APRENDES<\/strong><br data-start=\"925\" data-end=\"928\" \/>Preparar fuentes, resolver ambig\u00fcedades y contrastar geometr\u00eda con documentaci\u00f3n.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-c72e893 e-con e-atomic-element e-flexbox-base e-d213000 edibim-capacidad-card card-03 \" data-id=\"c72e893\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"c72e893\">\n    \t\t<div class=\"elementor-element elementor-element-6269c4e elementor-widget elementor-widget-text-editor\" data-id=\"6269c4e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p><strong>DOCUMENTAR<\/strong><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5a08e9e elementor-widget elementor-widget-text-editor\" data-id=\"5a08e9e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>03 \/ 04<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-4bbb8dd elementor-widget elementor-widget-html\" data-id=\"4bbb8dd\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- =========================================================\r\n     EDIBIM \u00b7 BLOQUE 4 \u00b7 TARJETA 03\r\n     DOCUMENTAR TRABAJO REPETITIVO\r\n     ========================================================= -->\r\n\r\n<div id=\"edibim-video-b4-03\" class=\"edibim-video-b4-component\">\r\n\r\n    <div class=\"edibim-showcase\">\r\n\r\n        <article\r\n            class=\"edibim-media-card\"\r\n            data-title=\"VER C\u00d3MO DOCUMENTA\"\r\n            data-video=\"https:\/\/mcpbim.com\/wp-content\/uploads\/2026\/09\/Armaduras-de-hormigon.mp4\"\r\n            tabindex=\"0\"\r\n            role=\"button\"\r\n            aria-label=\"Abrir v\u00eddeo\"\r\n        >\r\n\r\n            <video\r\n                class=\"edibim-media-preview\"\r\n                autoplay\r\n                muted\r\n                loop\r\n                playsinline\r\n                preload=\"auto\"\r\n            ><\/video>\r\n\r\n            <div class=\"edibim-card-shade\"><\/div>\r\n\r\n            <div class=\"edibim-card-title\">\r\n                VER C\u00d3MO DOCUMENTA\r\n            <\/div>\r\n\r\n            <span class=\"edibim-corner tl\"><\/span>\r\n            <span class=\"edibim-corner tr\"><\/span>\r\n            <span class=\"edibim-corner bl\"><\/span>\r\n            <span class=\"edibim-corner br\"><\/span>\r\n\r\n        <\/article>\r\n\r\n    <\/div>\r\n\r\n\r\n    <div class=\"edibim-modal\" aria-hidden=\"true\">\r\n\r\n        <div class=\"edibim-modal-backdrop\"><\/div>\r\n\r\n        <div class=\"edibim-modal-window\">\r\n\r\n            <button\r\n                class=\"edibim-modal-close\"\r\n                type=\"button\"\r\n                aria-label=\"Cerrar v\u00eddeo\"\r\n            >\u00d7<\/button>\r\n\r\n            <div class=\"edibim-modal-label\">\r\n                EDIBIM\r\n            <\/div>\r\n\r\n            <div class=\"edibim-video-holder\"><\/div>\r\n\r\n        <\/div>\r\n\r\n    <\/div>\r\n\r\n<\/div>\r\n\r\n\r\n<style>\r\n\r\n#edibim-video-b4-03 .edibim-showcase{\r\n    width:100%;\r\n    display:grid;\r\n    grid-template-columns:repeat(1,minmax(0,1fr));\r\n    gap:28px;\r\n}\r\n\r\n#edibim-video-b4-03 .edibim-media-card{\r\n    position:relative;\r\n    width:100%;\r\n    aspect-ratio:16 \/ 8.5;\r\n    background:#171717;\r\n    overflow:hidden;\r\n    cursor:pointer;\r\n    isolation:isolate;\r\n    border:1px solid rgba(255,255,255,.035);\r\n}\r\n\r\n#edibim-video-b4-03 .edibim-media-preview{\r\n    position:absolute;\r\n    top:18px;\r\n    left:18px;\r\n    width:42%;\r\n    height:45%;\r\n    object-fit:cover;\r\n    display:block;\r\n    filter:grayscale(100%) contrast(1.05) brightness(.78);\r\n    transform:scale(1);\r\n    transform-origin:left top;\r\n    transition:\r\n        top .6s cubic-bezier(.2,.8,.2,1),\r\n        left .6s cubic-bezier(.2,.8,.2,1),\r\n        width .6s cubic-bezier(.2,.8,.2,1),\r\n        height .6s cubic-bezier(.2,.8,.2,1),\r\n        filter .6s ease,\r\n        transform .8s cubic-bezier(.2,.8,.2,1);\r\n    z-index:1;\r\n}\r\n\r\n#edibim-video-b4-03 .edibim-media-card:hover .edibim-media-preview,\r\n#edibim-video-b4-03 .edibim-media-card:focus-visible .edibim-media-preview{\r\n    top:0;\r\n    left:0;\r\n    width:100%;\r\n    height:100%;\r\n    filter:grayscale(0%) contrast(1.02) brightness(.72);\r\n    transform:scale(1.035);\r\n}\r\n\r\n#edibim-video-b4-03 .edibim-card-shade{\r\n    position:absolute;\r\n    inset:0;\r\n    background:linear-gradient(\r\n        to top,\r\n        rgba(10,10,10,.76),\r\n        rgba(10,10,10,.20) 48%,\r\n        rgba(10,10,10,.05)\r\n    );\r\n    opacity:0;\r\n    transition:opacity .45s ease;\r\n    z-index:2;\r\n}\r\n\r\n#edibim-video-b4-03 .edibim-media-card:hover .edibim-card-shade{\r\n    opacity:1;\r\n}\r\n\r\n#edibim-video-b4-03 .edibim-card-title{\r\n    position:absolute;\r\n    left:18px;\r\n    bottom:18px;\r\n    font-size:16px;\r\n    font-weight:500;\r\n    line-height:1;\r\n    color:#F26B3A;\r\n    z-index:4;\r\n}\r\n\r\n#edibim-video-b4-03 .edibim-corner{\r\n    position:absolute;\r\n    width:14px;\r\n    height:14px;\r\n    z-index:5;\r\n    opacity:.55;\r\n    transition:.4s ease;\r\n}\r\n\r\n#edibim-video-b4-03 .edibim-corner.tl{\r\n    top:0;left:0;\r\n    border-top:1px solid #F26B3A;\r\n    border-left:1px solid #F26B3A;\r\n}\r\n\r\n#edibim-video-b4-03 .edibim-corner.tr{\r\n    top:0;right:0;\r\n    border-top:1px solid #F26B3A;\r\n    border-right:1px solid #F26B3A;\r\n}\r\n\r\n#edibim-video-b4-03 .edibim-corner.bl{\r\n    bottom:0;left:0;\r\n    border-bottom:1px solid #F26B3A;\r\n    border-left:1px solid #F26B3A;\r\n}\r\n\r\n#edibim-video-b4-03 .edibim-corner.br{\r\n    bottom:0;right:0;\r\n    border-bottom:1px solid #F26B3A;\r\n    border-right:1px solid #F26B3A;\r\n}\r\n\r\n#edibim-video-b4-03 .edibim-media-card:hover .edibim-corner{\r\n    width:20px;\r\n    height:20px;\r\n    opacity:1;\r\n}\r\n\r\n\r\n\/* POPUP *\/\r\n\r\n.edibim-modal-b4-03{\r\n    position:fixed !important;\r\n    inset:0 !important;\r\n    width:100vw !important;\r\n    height:100vh !important;\r\n    z-index:2147483000 !important;\r\n    display:flex;\r\n    align-items:center;\r\n    justify-content:center;\r\n    padding:40px;\r\n    box-sizing:border-box;\r\n    opacity:0;\r\n    visibility:hidden;\r\n    pointer-events:none;\r\n    transition:opacity .35s ease,visibility .35s ease;\r\n}\r\n\r\n.edibim-modal-b4-03.is-open{\r\n    opacity:1;\r\n    visibility:visible;\r\n    pointer-events:auto;\r\n}\r\n\r\n.edibim-modal-b4-03 .edibim-modal-backdrop{\r\n    position:absolute;\r\n    inset:0;\r\n    background:rgba(8,8,8,.88);\r\n    backdrop-filter:blur(8px);\r\n    -webkit-backdrop-filter:blur(8px);\r\n}\r\n\r\n.edibim-modal-b4-03 .edibim-modal-window{\r\n    position:relative;\r\n    width:min(1280px,92vw);\r\n    background:#101010;\r\n    z-index:2;\r\n    transform:translateY(25px) scale(.97);\r\n    opacity:0;\r\n    transition:\r\n        transform .45s cubic-bezier(.2,.8,.2,1),\r\n        opacity .35s ease;\r\n}\r\n\r\n.edibim-modal-b4-03.is-open .edibim-modal-window{\r\n    transform:translateY(0) scale(1);\r\n    opacity:1;\r\n}\r\n\r\n.edibim-modal-b4-03 .edibim-modal-label{\r\n    position:absolute;\r\n    left:24px;\r\n    top:22px;\r\n    z-index:4;\r\n    color:#F26B3A;\r\n    font-size:13px;\r\n    font-weight:600;\r\n}\r\n\r\n.edibim-modal-b4-03 .edibim-modal-close{\r\n    position:absolute;\r\n    top:15px;\r\n    right:18px;\r\n    z-index:10;\r\n    width:42px;\r\n    height:42px;\r\n    border:0;\r\n    background:rgba(15,15,15,.75);\r\n    color:#fff;\r\n    font-size:31px;\r\n    cursor:pointer;\r\n}\r\n\r\n.edibim-modal-b4-03 .edibim-modal-close:hover{\r\n    background:#F26B3A;\r\n}\r\n\r\n.edibim-modal-b4-03 .edibim-video-holder{\r\n    width:100%;\r\n    aspect-ratio:16 \/ 9;\r\n    background:#000;\r\n}\r\n\r\n.edibim-modal-b4-03 .edibim-video-holder video,\r\n.edibim-modal-b4-03 .edibim-video-holder iframe{\r\n    width:100%;\r\n    height:100%;\r\n    display:block;\r\n    border:0;\r\n    object-fit:contain;\r\n}\r\n\r\n@media(max-width:600px){\r\n\r\n    .edibim-modal-b4-03{\r\n        padding:12px;\r\n    }\r\n\r\n    #edibim-video-b4-03 .edibim-media-card{\r\n        aspect-ratio:4 \/ 3;\r\n    }\r\n\r\n}\r\n\r\n<\/style>\r\n\r\n\r\n<script>\r\n(function(){\r\n\r\n    const component=document.getElementById('edibim-video-b4-03');\r\n    if(!component)return;\r\n\r\n    const cards=component.querySelectorAll('.edibim-media-card');\r\n    const modal=component.querySelector('.edibim-modal');\r\n    if(!modal)return;\r\n\r\n    const holder=modal.querySelector('.edibim-video-holder');\r\n    const title=modal.querySelector('.edibim-modal-label');\r\n    const close=modal.querySelector('.edibim-modal-close');\r\n    const backdrop=modal.querySelector('.edibim-modal-backdrop');\r\n\r\n    modal.classList.add('edibim-modal-b4-03');\r\n    document.body.appendChild(modal);\r\n\r\n\r\n    cards.forEach(function(card){\r\n\r\n        const preview=card.querySelector('.edibim-media-preview');\r\n        const url=card.dataset.video;\r\n\r\n        if(url && preview){\r\n\r\n            preview.src=url;\r\n            preview.muted=true;\r\n            preview.defaultMuted=true;\r\n            preview.loop=true;\r\n            preview.autoplay=true;\r\n            preview.playsInline=true;\r\n\r\n            preview.setAttribute('muted','');\r\n            preview.setAttribute('autoplay','');\r\n            preview.setAttribute('loop','');\r\n            preview.setAttribute('playsinline','');\r\n\r\n            preview.load();\r\n\r\n            const play=function(){\r\n                const p=preview.play();\r\n                if(p)p.catch(function(){});\r\n            };\r\n\r\n            preview.addEventListener('canplay',play);\r\n            card.addEventListener('mouseenter',play);\r\n            setTimeout(play,300);\r\n        }\r\n    });\r\n\r\n\r\n    function abrir(card){\r\n\r\n        holder.innerHTML='';\r\n\r\n        const video=document.createElement('video');\r\n\r\n        video.src=card.dataset.video;\r\n        video.controls=true;\r\n        video.autoplay=true;\r\n        video.playsInline=true;\r\n        video.muted=true;\r\n\r\n        holder.appendChild(video);\r\n\r\n        title.textContent=card.dataset.title || 'EDIBIM';\r\n\r\n        modal.classList.add('is-open');\r\n        modal.setAttribute('aria-hidden','false');\r\n\r\n        document.documentElement.classList.add('edibim-modal-active');\r\n        document.body.classList.add('edibim-modal-active');\r\n\r\n        const p=video.play();\r\n        if(p)p.catch(function(){});\r\n    }\r\n\r\n\r\n    function cerrar(){\r\n\r\n        modal.classList.remove('is-open');\r\n        modal.setAttribute('aria-hidden','true');\r\n\r\n        document.documentElement.classList.remove('edibim-modal-active');\r\n        document.body.classList.remove('edibim-modal-active');\r\n\r\n        setTimeout(function(){\r\n            holder.innerHTML='';\r\n        },300);\r\n    }\r\n\r\n\r\n    cards.forEach(function(card){\r\n\r\n        card.addEventListener('click',function(e){\r\n            e.preventDefault();\r\n            e.stopPropagation();\r\n            abrir(card);\r\n        });\r\n\r\n    });\r\n\r\n\r\n    close.addEventListener('click',function(e){\r\n        e.stopPropagation();\r\n        cerrar();\r\n    });\r\n\r\n    backdrop.addEventListener('click',cerrar);\r\n\r\n    document.addEventListener('keydown',function(e){\r\n        if(e.key==='Escape' && modal.classList.contains('is-open')){\r\n            cerrar();\r\n        }\r\n    });\r\n\r\n})();\r\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-933f66b elementor-widget elementor-widget-text-editor\" data-id=\"933f66b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p class=\"PDq2pG_selectionAnchorContainer\" data-start=\"1050\" data-end=\"1154\"><strong data-start=\"1050\" data-end=\"1065\">EL PROBLEMA<\/strong><br data-start=\"1065\" data-end=\"1068\" \/>Montajes, vistas, cotas, planos, armaduras, mediciones y tablas que repiten un patr\u00f3n.<\/p><p data-start=\"1156\" data-end=\"1250\"><strong data-start=\"1156\" data-end=\"1174\">LO QUE EJECUTA<\/strong><br data-start=\"1174\" data-end=\"1177\" \/>Ejecuta un piloto y puede escalar operaciones preparadas sobre el modelo.<\/p><p data-start=\"1252\" data-end=\"1352\"><strong data-start=\"1252\" data-end=\"1271\">LO QUE APRENDES<\/strong><br data-start=\"1271\" data-end=\"1274\" \/>Definir el patr\u00f3n, revisar el piloto y controlar excepciones antes de escalar.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-92b2583 e-con e-atomic-element e-flexbox-base e-c0f98e1 edibim-capacidad-card card-04 \" data-id=\"92b2583\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"92b2583\">\n    \t\t<div class=\"elementor-element elementor-element-b76ffc0 elementor-widget elementor-widget-text-editor\" data-id=\"b76ffc0\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p><strong>CONSTRUIR<\/strong><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-1e1ab67 elementor-widget elementor-widget-text-editor\" data-id=\"1e1ab67\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>04 \/ 04<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e1e27c6 elementor-widget elementor-widget-html\" data-id=\"e1e27c6\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- =========================================================\r\n     EDIBIM \u00b7 BLOQUE 4 \u00b7 TARJETA 04\r\n     CONSTRUIR FAMILIAS\r\n     ========================================================= -->\r\n\r\n<div id=\"edibim-video-b4-04\" class=\"edibim-video-b4-component\">\r\n\r\n    <div class=\"edibim-showcase\">\r\n\r\n        <article\r\n            class=\"edibim-media-card\"\r\n            data-title=\"VER C\u00d3MO CONSTRUYE\"\r\n            data-video=\"https:\/\/mcpbim.com\/wp-content\/uploads\/2026\/09\/Creacion-de-familias-parametricas.mp4\"\r\n            tabindex=\"0\"\r\n            role=\"button\"\r\n            aria-label=\"Abrir v\u00eddeo\"\r\n        >\r\n\r\n            <video\r\n                class=\"edibim-media-preview\"\r\n                autoplay\r\n                muted\r\n                loop\r\n                playsinline\r\n                preload=\"auto\"\r\n            ><\/video>\r\n\r\n            <div class=\"edibim-card-shade\"><\/div>\r\n\r\n            <div class=\"edibim-card-title\">\r\n                VER C\u00d3MO CONSTRUYE\r\n            <\/div>\r\n\r\n            <span class=\"edibim-corner tl\"><\/span>\r\n            <span class=\"edibim-corner tr\"><\/span>\r\n            <span class=\"edibim-corner bl\"><\/span>\r\n            <span class=\"edibim-corner br\"><\/span>\r\n\r\n        <\/article>\r\n\r\n    <\/div>\r\n\r\n\r\n    <div class=\"edibim-modal\" aria-hidden=\"true\">\r\n\r\n        <div class=\"edibim-modal-backdrop\"><\/div>\r\n\r\n        <div class=\"edibim-modal-window\">\r\n\r\n            <button\r\n                class=\"edibim-modal-close\"\r\n                type=\"button\"\r\n                aria-label=\"Cerrar v\u00eddeo\"\r\n            >\u00d7<\/button>\r\n\r\n            <div class=\"edibim-modal-label\">\r\n                EDIBIM\r\n            <\/div>\r\n\r\n            <div class=\"edibim-video-holder\"><\/div>\r\n\r\n        <\/div>\r\n\r\n    <\/div>\r\n\r\n<\/div>\r\n\r\n\r\n<style>\r\n\r\n#edibim-video-b4-04 .edibim-showcase{\r\n    width:100%;\r\n    display:grid;\r\n    grid-template-columns:repeat(1,minmax(0,1fr));\r\n}\r\n\r\n#edibim-video-b4-04 .edibim-media-card{\r\n    position:relative;\r\n    width:100%;\r\n    aspect-ratio:16 \/ 8.5;\r\n    background:#171717;\r\n    overflow:hidden;\r\n    cursor:pointer;\r\n    isolation:isolate;\r\n    border:1px solid rgba(255,255,255,.035);\r\n}\r\n\r\n#edibim-video-b4-04 .edibim-media-preview{\r\n    position:absolute;\r\n    top:18px;\r\n    left:18px;\r\n    width:42%;\r\n    height:45%;\r\n    object-fit:cover;\r\n    display:block;\r\n    filter:grayscale(100%) contrast(1.05) brightness(.78);\r\n    transform:scale(1);\r\n    transform-origin:left top;\r\n    transition:\r\n        top .6s cubic-bezier(.2,.8,.2,1),\r\n        left .6s cubic-bezier(.2,.8,.2,1),\r\n        width .6s cubic-bezier(.2,.8,.2,1),\r\n        height .6s cubic-bezier(.2,.8,.2,1),\r\n        filter .6s ease,\r\n        transform .8s cubic-bezier(.2,.8,.2,1);\r\n    z-index:1;\r\n}\r\n\r\n#edibim-video-b4-04 .edibim-media-card:hover .edibim-media-preview{\r\n    top:0;\r\n    left:0;\r\n    width:100%;\r\n    height:100%;\r\n    filter:grayscale(0%) contrast(1.02) brightness(.72);\r\n    transform:scale(1.035);\r\n}\r\n\r\n#edibim-video-b4-04 .edibim-card-shade{\r\n    position:absolute;\r\n    inset:0;\r\n    background:linear-gradient(\r\n        to top,\r\n        rgba(10,10,10,.76),\r\n        rgba(10,10,10,.20) 48%,\r\n        rgba(10,10,10,.05)\r\n    );\r\n    opacity:0;\r\n    transition:opacity .45s ease;\r\n    z-index:2;\r\n}\r\n\r\n#edibim-video-b4-04 .edibim-media-card:hover .edibim-card-shade{\r\n    opacity:1;\r\n}\r\n\r\n#edibim-video-b4-04 .edibim-card-title{\r\n    position:absolute;\r\n    left:18px;\r\n    bottom:18px;\r\n    color:#F26B3A;\r\n    font-size:16px;\r\n    font-weight:500;\r\n    z-index:4;\r\n}\r\n\r\n#edibim-video-b4-04 .edibim-corner{\r\n    position:absolute;\r\n    width:14px;\r\n    height:14px;\r\n    z-index:5;\r\n    opacity:.55;\r\n    transition:.4s ease;\r\n}\r\n\r\n#edibim-video-b4-04 .edibim-corner.tl{\r\n    top:0;left:0;\r\n    border-top:1px solid #F26B3A;\r\n    border-left:1px solid #F26B3A;\r\n}\r\n\r\n#edibim-video-b4-04 .edibim-corner.tr{\r\n    top:0;right:0;\r\n    border-top:1px solid #F26B3A;\r\n    border-right:1px solid #F26B3A;\r\n}\r\n\r\n#edibim-video-b4-04 .edibim-corner.bl{\r\n    bottom:0;left:0;\r\n    border-bottom:1px solid #F26B3A;\r\n    border-left:1px solid #F26B3A;\r\n}\r\n\r\n#edibim-video-b4-04 .edibim-corner.br{\r\n    bottom:0;right:0;\r\n    border-bottom:1px solid #F26B3A;\r\n    border-right:1px solid #F26B3A;\r\n}\r\n\r\n#edibim-video-b4-04 .edibim-media-card:hover .edibim-corner{\r\n    width:20px;\r\n    height:20px;\r\n    opacity:1;\r\n}\r\n\r\n\r\n\/* POPUP *\/\r\n\r\n.edibim-modal-b4-04{\r\n    position:fixed !important;\r\n    inset:0 !important;\r\n    width:100vw !important;\r\n    height:100vh !important;\r\n    z-index:2147483000 !important;\r\n    display:flex;\r\n    align-items:center;\r\n    justify-content:center;\r\n    padding:40px;\r\n    box-sizing:border-box;\r\n    opacity:0;\r\n    visibility:hidden;\r\n    pointer-events:none;\r\n    transition:opacity .35s ease,visibility .35s ease;\r\n}\r\n\r\n.edibim-modal-b4-04.is-open{\r\n    opacity:1;\r\n    visibility:visible;\r\n    pointer-events:auto;\r\n}\r\n\r\n.edibim-modal-b4-04 .edibim-modal-backdrop{\r\n    position:absolute;\r\n    inset:0;\r\n    background:rgba(8,8,8,.88);\r\n    backdrop-filter:blur(8px);\r\n    -webkit-backdrop-filter:blur(8px);\r\n}\r\n\r\n.edibim-modal-b4-04 .edibim-modal-window{\r\n    position:relative;\r\n    width:min(1280px,92vw);\r\n    background:#101010;\r\n    z-index:2;\r\n    transform:translateY(25px) scale(.97);\r\n    opacity:0;\r\n    transition:\r\n        transform .45s cubic-bezier(.2,.8,.2,1),\r\n        opacity .35s ease;\r\n}\r\n\r\n.edibim-modal-b4-04.is-open .edibim-modal-window{\r\n    transform:translateY(0) scale(1);\r\n    opacity:1;\r\n}\r\n\r\n.edibim-modal-b4-04 .edibim-modal-label{\r\n    position:absolute;\r\n    left:24px;\r\n    top:22px;\r\n    z-index:4;\r\n    color:#F26B3A;\r\n    font-size:13px;\r\n    font-weight:600;\r\n}\r\n\r\n.edibim-modal-b4-04 .edibim-modal-close{\r\n    position:absolute;\r\n    top:15px;\r\n    right:18px;\r\n    z-index:10;\r\n    width:42px;\r\n    height:42px;\r\n    border:0;\r\n    background:rgba(15,15,15,.75);\r\n    color:#fff;\r\n    font-size:31px;\r\n    cursor:pointer;\r\n}\r\n\r\n.edibim-modal-b4-04 .edibim-modal-close:hover{\r\n    background:#F26B3A;\r\n}\r\n\r\n.edibim-modal-b4-04 .edibim-video-holder{\r\n    width:100%;\r\n    aspect-ratio:16 \/ 9;\r\n    background:#000;\r\n}\r\n\r\n.edibim-modal-b4-04 .edibim-video-holder video,\r\n.edibim-modal-b4-04 .edibim-video-holder iframe{\r\n    width:100%;\r\n    height:100%;\r\n    display:block;\r\n    border:0;\r\n    object-fit:contain;\r\n}\r\n\r\n@media(max-width:600px){\r\n\r\n    .edibim-modal-b4-04{\r\n        padding:12px;\r\n    }\r\n\r\n    #edibim-video-b4-04 .edibim-media-card{\r\n        aspect-ratio:4 \/ 3;\r\n    }\r\n\r\n}\r\n\r\n<\/style>\r\n\r\n\r\n<script>\r\n(function(){\r\n\r\n    const component=document.getElementById('edibim-video-b4-04');\r\n    if(!component)return;\r\n\r\n    const cards=component.querySelectorAll('.edibim-media-card');\r\n    const modal=component.querySelector('.edibim-modal');\r\n    if(!modal)return;\r\n\r\n    const holder=modal.querySelector('.edibim-video-holder');\r\n    const title=modal.querySelector('.edibim-modal-label');\r\n    const close=modal.querySelector('.edibim-modal-close');\r\n    const backdrop=modal.querySelector('.edibim-modal-backdrop');\r\n\r\n    modal.classList.add('edibim-modal-b4-04');\r\n    document.body.appendChild(modal);\r\n\r\n\r\n    cards.forEach(function(card){\r\n\r\n        const preview=card.querySelector('.edibim-media-preview');\r\n        const url=card.dataset.video;\r\n\r\n        if(url && preview){\r\n\r\n            preview.src=url;\r\n            preview.muted=true;\r\n            preview.defaultMuted=true;\r\n            preview.loop=true;\r\n            preview.autoplay=true;\r\n            preview.playsInline=true;\r\n\r\n            preview.setAttribute('muted','');\r\n            preview.setAttribute('autoplay','');\r\n            preview.setAttribute('loop','');\r\n            preview.setAttribute('playsinline','');\r\n\r\n            preview.load();\r\n\r\n            const play=function(){\r\n                const p=preview.play();\r\n                if(p)p.catch(function(){});\r\n            };\r\n\r\n            preview.addEventListener('canplay',play);\r\n            card.addEventListener('mouseenter',play);\r\n            setTimeout(play,300);\r\n        }\r\n    });\r\n\r\n\r\n    function abrir(card){\r\n\r\n        holder.innerHTML='';\r\n\r\n        const video=document.createElement('video');\r\n\r\n        video.src=card.dataset.video;\r\n        video.controls=true;\r\n        video.autoplay=true;\r\n        video.playsInline=true;\r\n        video.muted=true;\r\n\r\n        holder.appendChild(video);\r\n\r\n        title.textContent=card.dataset.title || 'EDIBIM';\r\n\r\n        modal.classList.add('is-open');\r\n        modal.setAttribute('aria-hidden','false');\r\n\r\n        document.documentElement.classList.add('edibim-modal-active');\r\n        document.body.classList.add('edibim-modal-active');\r\n\r\n        const p=video.play();\r\n        if(p)p.catch(function(){});\r\n    }\r\n\r\n\r\n    function cerrar(){\r\n\r\n        modal.classList.remove('is-open');\r\n        modal.setAttribute('aria-hidden','true');\r\n\r\n        document.documentElement.classList.remove('edibim-modal-active');\r\n        document.body.classList.remove('edibim-modal-active');\r\n\r\n        setTimeout(function(){\r\n            holder.innerHTML='';\r\n        },300);\r\n    }\r\n\r\n\r\n    cards.forEach(function(card){\r\n\r\n        card.addEventListener('click',function(e){\r\n            e.preventDefault();\r\n            e.stopPropagation();\r\n            abrir(card);\r\n        });\r\n\r\n    });\r\n\r\n\r\n    close.addEventListener('click',function(e){\r\n        e.stopPropagation();\r\n        cerrar();\r\n    });\r\n\r\n    backdrop.addEventListener('click',cerrar);\r\n\r\n    document.addEventListener('keydown',function(e){\r\n        if(e.key==='Escape' && modal.classList.contains('is-open')){\r\n            cerrar();\r\n        }\r\n    });\r\n\r\n})();\r\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-4a337a1 elementor-widget elementor-widget-text-editor\" data-id=\"4a337a1\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p class=\"PDq2pG_selectionAnchorContainer\" data-start=\"1382\" data-end=\"1474\"><strong data-start=\"1382\" data-end=\"1397\">EL PROBLEMA<\/strong><br data-start=\"1397\" data-end=\"1400\" \/>Referencias de fabricante que deben convertirse en una familia utilizable.<\/p><p data-start=\"1476\" data-end=\"1573\"><strong data-start=\"1476\" data-end=\"1494\">LO QUE EJECUTA<\/strong><br data-start=\"1494\" data-end=\"1497\" \/>Combina fotograf\u00eda, DXF y cat\u00e1logo para construir y parametrizar la familia.<\/p><p data-start=\"1575\" data-end=\"1715\"><strong data-start=\"1575\" data-end=\"1594\">LO QUE APRENDES<\/strong><br data-start=\"1594\" data-end=\"1597\" \/>Comprobar qu\u00e9 par\u00e1metros gobiernan realmente la geometr\u00eda y qu\u00e9 requiere ajuste.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n\n<\/div>\n<div class=\"elementor-element elementor-element-ff5aab8 e-con e-atomic-element e-flexbox-base e-ff5aab8-904cb2f \" data-id=\"ff5aab8\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"ff5aab8\">\n    \t\t<div class=\"elementor-element elementor-element-cb72f15 elementor-widget elementor-widget-text-editor\" data-id=\"cb72f15\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>En armaduras, EdiBIM ayuda a modelar, documentar y revisar informaci\u00f3n. No sustituye el c\u00e1lculo estructural ni la validaci\u00f3n del especialista.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n\n<\/div>\n<div class=\"elementor-element elementor-element-d8e8895 e-con e-atomic-element e-flexbox-base e-6f64edd edibim-seccion2 \" data-id=\"d8e8895\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"d8e8895\">\n    <div class=\"elementor-element elementor-element-a0909be e-con e-atomic-element e-flexbox-base e-a0909be-5e8de1f \" data-id=\"a0909be\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"a0909be\">\n    \t\t<div class=\"elementor-element elementor-element-dea9647 elementor-widget elementor-widget-html\" data-id=\"dea9647\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"edibim-fondo-sticky\">\r\n    <canvas id=\"edibimPointsCanvas\"><\/canvas>\r\n<\/div>\r\n\r\n<style>\r\n\r\n\/* =========================================================\r\n   SECCI\u00d3N\r\n   ========================================================= *\/\r\n\r\n.edibim-seccion2{\r\n    position:relative !important;\r\n    overflow:visible !important;\r\n    isolation:isolate;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   WIDGET HTML = CAPA DE FONDO\r\n\r\n   Sigue siendo ABSOLUTO.\r\n   NO ocupa espacio.\r\n   NO crea ninguna columna.\r\n   ========================================================= *\/\r\n\r\n.edibim-seccion2 .elementor-widget-html:has(#edibimPointsCanvas){\r\n\r\n    position:absolute !important;\r\n\r\n    top:0 !important;\r\n    left:0 !important;\r\n\r\n    width:100% !important;\r\n    height:100% !important;\r\n\r\n    margin:0 !important;\r\n    padding:0 !important;\r\n\r\n    pointer-events:none !important;\r\n\r\n    z-index:0 !important;\r\n}\r\n\r\n\r\n\/* Wrapper que mete Elementor *\/\r\n\r\n.edibim-seccion2\r\n.elementor-widget-html:has(#edibimPointsCanvas)\r\n> .elementor-widget-container{\r\n\r\n    position:relative !important;\r\n\r\n    width:100% !important;\r\n    height:100% !important;\r\n\r\n    margin:0 !important;\r\n    padding:0 !important;\r\n\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   ESTO ES LO QUE SE QUEDA STICKY\r\n   ========================================================= *\/\r\n\r\n.edibim-fondo-sticky{\r\n    position:relative !important;\r\n    top:0 !important;\r\n\r\n    width:100% !important;\r\n\r\n    \/* IMPORTANTE:\r\n       ya no obligamos al canvas a medir 100vh *\/\r\n    height:100% !important;\r\n\r\n    overflow:hidden !important;\r\n    pointer-events:none !important;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   CANVAS\r\n   ========================================================= *\/\r\n\r\n#edibimPointsCanvas{\r\n\r\n    position:absolute !important;\r\n\r\n    inset:0 !important;\r\n\r\n    width:100% !important;\r\n    height:100% !important;\r\n\r\n    display:block !important;\r\n\r\n    pointer-events:none !important;\r\n\r\n    background:transparent !important;\r\n\r\n    \/* Conservamos exactamente tu desplazamiento actual *\/\r\n    transform:translateX(350px) !important;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   TODO TU CONTENIDO SIGUE ENCIMA\r\n   ========================================================= *\/\r\n\r\n.edibim-seccion2 > *{\r\n    position:relative;\r\n    z-index:2;\r\n}\r\n\r\n\r\n\/* El widget HTML siempre al fondo *\/\r\n\r\n.edibim-seccion2 .elementor-widget-html:has(#edibimPointsCanvas){\r\n    z-index:0 !important;\r\n}\r\n\r\n\r\n\/* Tu columna derecha sigue sticky *\/\r\n\r\n.edibim-sticky{\r\n    position:sticky !important;\r\n    top:80px !important;\r\n    align-self:flex-start !important;\r\n    z-index:5 !important;\r\n}\r\n\r\n<\/style>\r\n\r\n\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/three.js\/r128\/three.min.js\"><\/script>\r\n\r\n<script>\r\n(function(){\r\n\r\n    let iniciado = false;\r\n\r\n    function initEdiBim(){\r\n\r\n        if(iniciado) return;\r\n\r\n        const section =\r\n            document.querySelector('.edibim-seccion2');\r\n\r\n        const canvas =\r\n            document.getElementById('edibimPointsCanvas');\r\n\r\n        if(!section || !canvas) return;\r\n\r\n        if(typeof THREE === 'undefined'){\r\n            setTimeout(initEdiBim, 100);\r\n            return;\r\n        }\r\n\r\n        iniciado = true;\r\n\r\n\r\n        \/* =====================================================\r\n           TAMA\u00d1O REAL DE LA SECCI\u00d3N\r\n           ===================================================== *\/\r\n\r\n        function obtenerTamano(){\r\n\r\n            const r =\r\n                section.getBoundingClientRect();\r\n\r\n            return {\r\n                width: Math.max(1, r.width),\r\n                height: Math.max(1, r.height)\r\n            };\r\n        }\r\n\r\n        const inicial =\r\n            obtenerTamano();\r\n\r\n\r\n        \/* =====================================================\r\n           ESCENA\r\n           ===================================================== *\/\r\n\r\n        const scene =\r\n            new THREE.Scene();\r\n\r\n\r\n        \/* =====================================================\r\n           C\u00c1MARA ORIGINAL\r\n           ===================================================== *\/\r\n\r\n        const camera =\r\n            new THREE.PerspectiveCamera(\r\n                60,\r\n                inicial.width \/ inicial.height,\r\n                0.1,\r\n                1000\r\n            );\r\n\r\n        camera.position.set(\r\n            0,\r\n            0.4,\r\n            49.6\r\n        );\r\n\r\n\r\n        \/* =====================================================\r\n           RENDERER\r\n           ===================================================== *\/\r\n\r\n        const renderer =\r\n            new THREE.WebGLRenderer({\r\n                canvas:canvas,\r\n                alpha:true,\r\n                antialias:false,\r\n                powerPreference:'high-performance'\r\n            });\r\n\r\n        renderer.setPixelRatio(\r\n            Math.min(\r\n                window.devicePixelRatio || 1,\r\n                2\r\n            )\r\n        );\r\n\r\n        renderer.setSize(\r\n            inicial.width,\r\n            inicial.height,\r\n            false\r\n        );\r\n\r\n        \/* Transparente *\/\r\n        renderer.setClearColor(\r\n            0x000000,\r\n            0\r\n        );\r\n\r\n\r\n        \/* =====================================================\r\n           NIEBLA\r\n\r\n           El original la usa para dar profundidad.\r\n           ===================================================== *\/\r\n\r\n        scene.fog =\r\n            new THREE.Fog(\r\n                0xD1D1D1,\r\n                0,\r\n                15\r\n            );\r\n\r\n\r\n        \/* =====================================================\r\n           GEOMETR\u00cdA ORIGINAL\r\n           ===================================================== *\/\r\n\r\n        const curve =\r\n            new THREE.QuadraticBezierCurve3(\r\n\r\n                new THREE.Vector3(\r\n                    -11,\r\n                    0,\r\n                    0\r\n                ),\r\n\r\n                new THREE.Vector3(\r\n                    0,\r\n                    0,\r\n                    0\r\n                ),\r\n\r\n                new THREE.Vector3(\r\n                    11,\r\n                    0,\r\n                    0\r\n                )\r\n            );\r\n\r\n\r\n        const geometry =\r\n            new THREE.TubeGeometry(\r\n                curve,\r\n\r\n                60,     \/\/ tubularSegments ORIGINAL\r\n\r\n                1.5,    \/\/ radio ORIGINAL\r\n\r\n                60,     \/\/ radialSegments ORIGINAL\r\n\r\n                false\r\n            );\r\n\r\n\r\n        \/* Guardamos la geometr\u00eda original para el movimiento *\/\r\n        const originalPositions =\r\n            new Float32Array(\r\n                geometry.attributes.position.array\r\n            );\r\n\r\n\r\n        \/* =====================================================\r\n           SHADER DE PUNTOS CIRCULARES\r\n           ===================================================== *\/\r\n\r\n        const material =\r\n            new THREE.ShaderMaterial({\r\n\r\n                uniforms:{\r\n\r\n                    color:{\r\n                        value:\r\n                            new THREE.Color(\r\n                                0x777777\r\n                            )\r\n                    },\r\n\r\n                    size:{\r\n                        value:0.20\r\n                    },\r\n\r\n                    sizeAttenuation:{\r\n                        value:170\r\n                    },\r\n\r\n                    opacity:{\r\n                        value:0.38\r\n                    }\r\n                },\r\n\r\n                vertexShader:`\r\n\r\n                    uniform float size;\r\n                    uniform float sizeAttenuation;\r\n\r\n                    void main(){\r\n\r\n                        vec4 mvPosition =\r\n                            modelViewMatrix *\r\n                            vec4(position, 1.0);\r\n\r\n                        gl_PointSize =\r\n                            size *\r\n                            (\r\n                                sizeAttenuation \/\r\n                                -mvPosition.z\r\n                            );\r\n\r\n                        gl_Position =\r\n                            projectionMatrix *\r\n                            mvPosition;\r\n                    }\r\n\r\n                `,\r\n\r\n                fragmentShader:`\r\n\r\n                    uniform vec3 color;\r\n                    uniform float opacity;\r\n\r\n                    void main(){\r\n\r\n                        vec2 center =\r\n                            gl_PointCoord -\r\n                            vec2(0.5);\r\n\r\n                        float dist =\r\n                            dot(center,center);\r\n\r\n                        if(dist > 0.25){\r\n                            discard;\r\n                        }\r\n\r\n                        float edge =\r\n                            1.0 -\r\n                            smoothstep(\r\n                                0.18,\r\n                                0.25,\r\n                                dist\r\n                            );\r\n\r\n                        gl_FragColor =\r\n                            vec4(\r\n                                color,\r\n                                opacity * edge\r\n                            );\r\n                    }\r\n\r\n                `,\r\n\r\n                transparent:true,\r\n                depthWrite:false,\r\n                fog:false\r\n            });\r\n\r\n\r\n        const points =\r\n            new THREE.Points(\r\n                geometry,\r\n                material\r\n            );\r\n\r\n\r\n        \/* =====================================================\r\n           POSICI\u00d3N ORIGINAL\r\n           ===================================================== *\/\r\n\r\n        points.position.set(\r\n            -4.2,\r\n            -2.5,\r\n            36\r\n        );\r\n\r\n\r\n        \/* =====================================================\r\n           ROTACI\u00d3N ORIGINAL\r\n\r\n           ESTA es la que crea:\r\n\r\n           GRANDE IZQUIERDA  ======>  PEQUE\u00d1O DERECHA\r\n           ===================================================== *\/\r\n\r\n        points.rotation.set(\r\n            THREE.MathUtils.degToRad(0),\r\n            THREE.MathUtils.degToRad(-65),\r\n            THREE.MathUtils.degToRad(16)\r\n        );\r\n\r\n\r\n        \/*\r\n         * Un poco m\u00e1s peque\u00f1o que el original\r\n         * para nuestra composici\u00f3n con tarjetas.\r\n         *\/\r\n        points.scale.set(\r\n             0.8,\r\n             0.8,\r\n             0.8\r\n        );\r\n\r\n\r\n        scene.add(points);\r\n\r\n\r\n        \/* Necesario para proyectar correctamente *\/\r\n        scene.updateMatrixWorld(true);\r\n\r\n\r\n        \/* =====================================================\r\n           RAT\u00d3N\r\n           ===================================================== *\/\r\n\r\n        const mouse = {\r\n            x:-9999,\r\n            y:-9999\r\n        };\r\n\r\n\r\n        const targets =\r\n            new Float32Array(\r\n                originalPositions.length\r\n            );\r\n\r\n        const offsets =\r\n            new Float32Array(\r\n                originalPositions.length\r\n            );\r\n\r\n\r\n        const world =\r\n            new THREE.Vector3();\r\n\r\n        const projected =\r\n            new THREE.Vector3();\r\n\r\n\r\n        section.addEventListener(\r\n            'pointermove',\r\n            function(e){\r\n\r\n                const r =\r\n                    canvas.getBoundingClientRect();\r\n\r\n                mouse.x =\r\n                    e.clientX -\r\n                    r.left;\r\n\r\n                mouse.y =\r\n                    e.clientY -\r\n                    r.top;\r\n            },\r\n            {\r\n                passive:true\r\n            }\r\n        );\r\n\r\n\r\n        section.addEventListener(\r\n            'pointerleave',\r\n            function(){\r\n\r\n                mouse.x = -9999;\r\n                mouse.y = -9999;\r\n            }\r\n        );\r\n\r\n\r\n        \/* =====================================================\r\n           ONDA ORIGINAL\r\n           ===================================================== *\/\r\n\r\n        function wave(\r\n            time,\r\n            coordinate,\r\n            speed,\r\n            useCos\r\n        ){\r\n\r\n            const trig =\r\n                useCos ?\r\n                    Math.cos :\r\n                    Math.sin;\r\n\r\n            return (\r\n                0.5 *\r\n                Math.sin(\r\n                    trig(\r\n                        time +\r\n                        coordinate *\r\n                        speed\r\n                    ) *\r\n                    Math.PI\r\n                )\r\n            );\r\n        }\r\n\r\n\r\n        \/* =====================================================\r\n           ANIMACI\u00d3N\r\n           ===================================================== *\/\r\n\r\n        let time = 0;\r\n\r\n\r\n        function animate(){\r\n\r\n            requestAnimationFrame(\r\n                animate\r\n            );\r\n\r\n\r\n            time += 0.01;\r\n\r\n\r\n            const positions =\r\n                geometry.attributes.position.array;\r\n\r\n\r\n            const rect =\r\n                canvas.getBoundingClientRect();\r\n\r\n\r\n            points.updateMatrixWorld(true);\r\n\r\n\r\n            for(\r\n                let i = 0;\r\n                i < positions.length;\r\n                i += 3\r\n            ){\r\n\r\n                const ox =\r\n                    originalPositions[i];\r\n\r\n                const oy =\r\n                    originalPositions[i + 1];\r\n\r\n                const oz =\r\n                    originalPositions[i + 2];\r\n\r\n\r\n                \/* =============================================\r\n                   POSICI\u00d3N DEL PUNTO EN PANTALLA\r\n                   ============================================= *\/\r\n\r\n                world.set(\r\n                    ox,\r\n                    oy,\r\n                    oz\r\n                );\r\n\r\n                world.applyMatrix4(\r\n                    points.matrixWorld\r\n                );\r\n\r\n                projected.copy(\r\n                    world\r\n                );\r\n\r\n                projected.project(\r\n                    camera\r\n                );\r\n\r\n\r\n                const px =\r\n                    (\r\n                        projected.x *\r\n                        0.5 +\r\n                        0.5\r\n                    ) *\r\n                    rect.width;\r\n\r\n\r\n                const py =\r\n                    (\r\n                        -projected.y *\r\n                        0.5 +\r\n                        0.5\r\n                    ) *\r\n                    rect.height;\r\n\r\n\r\n                const dx =\r\n                    px -\r\n                    mouse.x;\r\n\r\n                const dy =\r\n                    py -\r\n                    mouse.y;\r\n\r\n\r\n                const distance =\r\n                    Math.sqrt(\r\n                        dx * dx +\r\n                        dy * dy\r\n                    );\r\n\r\n\r\n                \/* =============================================\r\n                   REPULSI\u00d3N\r\n\r\n                   Valores del comportamiento original.\r\n                   ============================================= *\/\r\n\r\n                if(distance < 125){\r\n\r\n                    const strength =\r\n                        1 -\r\n                        distance \/ 125;\r\n\r\n                    const angle =\r\n                        Math.atan2(\r\n                            dy,\r\n                            dx\r\n                        );\r\n\r\n\r\n                    targets[i] =\r\n                        -Math.cos(angle) *\r\n                        strength *\r\n                        1.5;\r\n\r\n\r\n                    targets[i + 1] =\r\n                        -Math.sin(angle) *\r\n                        strength *\r\n                        1.5;\r\n\r\n\r\n                    targets[i + 2] =\r\n                        strength *\r\n                        0.75;\r\n\r\n                }else{\r\n\r\n                    targets[i] = 0;\r\n                    targets[i + 1] = 0;\r\n                    targets[i + 2] = 0;\r\n                }\r\n\r\n\r\n                \/* =============================================\r\n                   RETORNO SUAVE\r\n                   ============================================= *\/\r\n\r\n                offsets[i] +=\r\n                    (\r\n                        targets[i] -\r\n                        offsets[i]\r\n                    ) *\r\n                    0.10;\r\n\r\n\r\n                offsets[i + 1] +=\r\n                    (\r\n                        targets[i + 1] -\r\n                        offsets[i + 1]\r\n                    ) *\r\n                    0.10;\r\n\r\n\r\n                offsets[i + 2] +=\r\n                    (\r\n                        targets[i + 2] -\r\n                        offsets[i + 2]\r\n                    ) *\r\n                    0.10;\r\n\r\n\r\n                \/* =============================================\r\n                   ONDA MUY SUAVE\r\n                   ============================================= *\/\r\n\r\n                const wx =\r\n                    wave(\r\n                        time,\r\n                        ox,\r\n                        0.01,\r\n                        false\r\n                    ) *\r\n                    0.02;\r\n\r\n\r\n                const wy =\r\n                    wave(\r\n                        time,\r\n                        oy,\r\n                        0.015,\r\n                        true\r\n                    ) *\r\n                    0.02;\r\n\r\n\r\n                const wz =\r\n                    wave(\r\n                        time,\r\n                        oz,\r\n                        0.02,\r\n                        false\r\n                    ) *\r\n                    0.02;\r\n\r\n\r\n                positions[i] =\r\n                    ox +\r\n                    wx +\r\n                    offsets[i];\r\n\r\n\r\n                positions[i + 1] =\r\n                    oy +\r\n                    wy +\r\n                    offsets[i + 1];\r\n\r\n\r\n                positions[i + 2] =\r\n                    oz +\r\n                    wz +\r\n                    offsets[i + 2];\r\n\r\n            }\r\n\r\n\r\n            geometry\r\n                .attributes\r\n                .position\r\n                .needsUpdate = true;\r\n\r\n\r\n            renderer.render(\r\n                scene,\r\n                camera\r\n            );\r\n        }\r\n\r\n\r\n        \/* =====================================================\r\n           RESIZE \u2014 SIN SCROLL INFINITO\r\n           ===================================================== *\/\r\n\r\n        let resizeTimer = null;\r\n\r\n\r\n        window.addEventListener(\r\n            'resize',\r\n            function(){\r\n\r\n                clearTimeout(\r\n                    resizeTimer\r\n                );\r\n\r\n\r\n                resizeTimer =\r\n                    setTimeout(\r\n                        function(){\r\n\r\n                            const size =\r\n                                obtenerTamano();\r\n\r\n                            camera.aspect =\r\n                                size.width \/\r\n                                size.height;\r\n\r\n                            camera.updateProjectionMatrix();\r\n\r\n\r\n                            renderer.setSize(\r\n                                size.width,\r\n                                size.height,\r\n                                false\r\n                            );\r\n\r\n                        },\r\n                        150\r\n                    );\r\n\r\n            },\r\n            {\r\n                passive:true\r\n            }\r\n        );\r\n\r\n\/* =====================================================\r\n   STICKY VISUAL DEL FONDO\r\n   SIN CAMBIAR TAMA\u00d1O NI PERSPECTIVA\r\n   ===================================================== *\/\r\n\r\nfunction actualizarStickyFondo(){\r\n\r\n    const rect =\r\n        section.getBoundingClientRect();\r\n\r\n    \/*\r\n     * Cu\u00e1nto hemos avanzado dentro de la secci\u00f3n.\r\n     *\/\r\n    let desplazamiento =\r\n        -rect.top;\r\n\r\n    \/*\r\n     * Antes de llegar a la secci\u00f3n:\r\n     * no mover.\r\n     *\/\r\n    if(desplazamiento < 0){\r\n        desplazamiento = 0;\r\n    }\r\n\r\n    \/*\r\n     * Evitar que siga movi\u00e9ndose\r\n     * cuando llegamos al final de la secci\u00f3n.\r\n     *\/\r\n    const maximo =\r\n        Math.max(\r\n            0,\r\n            rect.height -\r\n            window.innerHeight\r\n        );\r\n\r\n    if(desplazamiento > maximo){\r\n        desplazamiento = maximo;\r\n    }\r\n\r\n    \/*\r\n     * IMPORTANTE:\r\n     * mantenemos tus 350px horizontales\r\n     * y solo a\u00f1adimos movimiento vertical.\r\n     *\/\r\n    canvas.style.transform =\r\n        `translateX(350px) translateY(${desplazamiento}px)`;\r\n}\r\n\r\n\r\nwindow.addEventListener(\r\n    'scroll',\r\n    actualizarStickyFondo,\r\n    {passive:true}\r\n);\r\n\r\n\r\nactualizarStickyFondo();\r\n\r\n        \/* =====================================================\r\n           INICIO\r\n           ===================================================== *\/\r\n\r\n        animate();\r\n\r\n    }\r\n\r\n\r\n    function esperar(){\r\n\r\n        if(typeof THREE === 'undefined'){\r\n\r\n            setTimeout(\r\n                esperar,\r\n                100\r\n            );\r\n\r\n            return;\r\n        }\r\n\r\n        initEdiBim();\r\n    }\r\n\r\n\r\n    if(document.readyState === 'loading'){\r\n\r\n        document.addEventListener(\r\n            'DOMContentLoaded',\r\n            esperar\r\n        );\r\n\r\n    }else{\r\n\r\n        esperar();\r\n    }\r\n\r\n})();\r\n\r\n\r\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-85b490c elementor-widget elementor-widget-text-editor\" data-id=\"85b490c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>EDIBIM EN ACCI\u00d3N<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-f79c39f elementor-widget elementor-widget-text-editor\" data-id=\"f79c39f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h2 class=\"edibim-title\"><strong>INSTALADO, YA PUEDE ACTUAR<\/strong><br data-start=\"409\" data-end=\"412\" \/><strong data-start=\"414\" data-end=\"442\">LA FORMACI\u00d3N TE ENSE\u00d1A A DIRIGIRLO<\/strong><\/h2>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-c19e052 elementor-widget elementor-widget-text-editor\" data-id=\"c19e052\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>EdiBIM puede consultar el modelo y ejecutar operaciones desde el primer momento. Pero para obtener resultados \u00fatiles necesita contexto, l\u00edmites y criterio.<\/p><p>Aprendes a reconocer qu\u00e9 tareas conviene delegar, aportar el contexto necesario, supervisar la ejecuci\u00f3n, detectar resultados incompletos, validar los cambios y convertir procedimientos \u00fatiles en flujos repetibles.<\/p><p><strong data-start=\"1852\" data-end=\"1892\">UNA HERRAMIENTA NO ES UNA CAPACIDAD.<\/strong><br data-start=\"1892\" data-end=\"1895\" \/>La capacidad aparece cuando sabes cu\u00e1ndo utilizarla, c\u00f3mo dirigirla y c\u00f3mo comprobarla.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-af5ba4f e-con e-atomic-element e-flexbox-base e-af5ba4f-54ec4f3 \" data-id=\"af5ba4f\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"af5ba4f\">\n    <div class=\"elementor-element elementor-element-01e2413 e-con e-atomic-element e-flexbox-base e-01e2413-91e1a01 \" data-id=\"01e2413\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"01e2413\">\n    \t\t\t<button class=\"e-0e09362-d8bcb77 edibim-open-contact e-button-base\" data-interaction-id=\"0e09362\">\n\t\tCONT\u00c1CTANOS\n\t<\/button>\t\t\t\t\t<a href=\"#edibim-acesso\" target=\"_self\" class=\"e-54b85d8-fe6ee93 e-button-base\" data-interaction-id=\"54b85d8\">\n\t\tACCEDER A EDIBIM\n\t<\/a>\t\t\n<\/div>\n\n<\/div>\n\n<\/div>\n<div class=\"elementor-element elementor-element-d6a8f5c e-con e-atomic-element e-flexbox-base e-c8cea0d \" data-id=\"d6a8f5c\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"d6a8f5c\">\n    <div class=\"elementor-element elementor-element-fa42e75 e-con e-atomic-element e-flexbox-base e-4a297fc \" data-id=\"fa42e75\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"fa42e75\">\n    \t\t<div class=\"elementor-element elementor-element-7822231 elementor-position-inline-end elementor-widget__width-initial elementor-view-default elementor-mobile-position-block-start elementor-widget elementor-widget-icon-box\" data-id=\"7822231\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon-box.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-box-wrapper\">\n\n\t\t\t\t\t\t<div class=\"elementor-icon-box-icon\">\n\t\t\t\t<span  class=\"elementor-icon\">\n\t\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" id=\"Layer_1\" data-name=\"Layer 1\" viewBox=\"0 0 24 24\"><path d=\"m12.5,12h1.586c.936,0,1.814-.364,2.475-1.025l6.707-6.707c.473-.472.732-1.1.732-1.768s-.26-1.296-.732-1.768c-.975-.975-2.561-.975-3.535,0l-6.707,6.707c-.651.651-1.025,1.554-1.025,2.475v1.586c0,.276.224.5.5.5Zm.5-2.086c0-.658.267-1.302.732-1.768l6.707-6.707c.584-.585,1.537-.585,2.121,0,.283.283.439.66.439,1.061s-.156.777-.439,1.061l-6.707,6.707c-.472.472-1.1.732-1.768.732h-1.086v-1.086Zm-1,6.086c0-.552.448-1,1-1s1,.448,1,1-.448,1-1,1-1-.448-1-1Zm-7-1c.552,0,1,.448,1,1s-.448,1-1,1-1-.448-1-1,.448-1,1-1Zm5,1c0,.552-.448,1-1,1s-1-.448-1-1,.448-1,1-1,1,.448,1,1Zm14-3.5v6c0,2.481-2.019,4.5-4.5,4.5H4.5c-2.481,0-4.5-2.019-4.5-4.5v-6c0-2.481,2.019-4.5,4.5-4.5h5c.276,0,.5.224.5.5s-.224.5-.5.5h-5c-1.93,0-3.5,1.57-3.5,3.5v6c0,1.93,1.57,3.5,3.5,3.5h15c1.93,0,3.5-1.57,3.5-3.5v-6c0-1.246-.671-2.408-1.75-3.032-.239-.138-.321-.444-.183-.683s.446-.32.683-.182c1.388.802,2.25,2.296,2.25,3.897Z\"><\/path><\/svg>\t\t\t\t<\/span>\n\t\t\t<\/div>\n\t\t\t\n\t\t\t\t\t\t<div class=\"elementor-icon-box-content\">\n\n\t\t\t\t\t\t\t\t\t<h3 class=\"elementor-icon-box-title\">\n\t\t\t\t\t\t<span  >\n\t\t\t\t\t\t\tPREPARAR\t\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/h3>\n\t\t\t\t\n\t\t\t\t\t\t\t\t\t<p class=\"elementor-icon-box-description\">\n\t\t\t\t\t\t01<br>\n\u2500\u2500\u2500\n<br><br>Identificas qu\u00e9 tareas tiene sentido delegar a EdiBIM y aprendes a aportar el contexto necesario para que pueda trabajar sobre el modelo con informaci\u00f3n suficiente.\t\t\t\t\t<\/p>\n\t\t\t\t\n\t\t\t<\/div>\n\t\t\t\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ded50c5 elementor-position-inline-end elementor-widget__width-initial elementor-view-default elementor-mobile-position-block-start elementor-widget elementor-widget-icon-box\" data-id=\"ded50c5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon-box.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-box-wrapper\">\n\n\t\t\t\t\t\t<div class=\"elementor-icon-box-icon\">\n\t\t\t\t<span  class=\"elementor-icon\">\n\t\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" id=\"Layer_1\" data-name=\"Layer 1\" viewBox=\"0 0 24 24\"><path d=\"m21.5,19c-.648,0-1.234.254-1.679.661l-3.176-2.31,2.356-1.346V7.996l-2.356-1.346,3.176-2.31c.445.406,1.031.661,1.679.661,1.378,0,2.5-1.121,2.5-2.5s-1.122-2.5-2.5-2.5-2.5,1.121-2.5,2.5c0,.369.085.717.23,1.033l-3.537,2.573-3.693-2.111-3.693,2.111-3.537-2.573c.144-.316.23-.664.23-1.033,0-1.379-1.122-2.5-2.5-2.5S0,1.121,0,2.5s1.122,2.5,2.5,2.5c.648,0,1.234-.254,1.679-.661l3.176,2.31-2.356,1.346v8.008l2.356,1.346-3.177,2.31c-.445-.406-1.031-.66-1.679-.66-1.378,0-2.5,1.121-2.5,2.5s1.122,2.5,2.5,2.5,2.5-1.121,2.5-2.5c0-.369-.085-.717-.23-1.033l3.537-2.572,3.693,2.111,3.693-2.111,3.537,2.573c-.144.316-.23.664-.23,1.033,0,1.379,1.122,2.5,2.5,2.5s2.5-1.121,2.5-2.5-1.122-2.5-2.5-2.5Zm0-18c.827,0,1.5.673,1.5,1.5s-.673,1.5-1.5,1.5-1.5-.673-1.5-1.5.673-1.5,1.5-1.5ZM2.5,4c-.827,0-1.5-.673-1.5-1.5s.673-1.5,1.5-1.5,1.5.673,1.5,1.5-.673,1.5-1.5,1.5Zm0,19c-.827,0-1.5-.673-1.5-1.5s.673-1.5,1.5-1.5,1.5.673,1.5,1.5-.673,1.5-1.5,1.5Zm10-4.433v-6.45l5.5-3.143v6.45l-5.5,3.143Zm4.839-10.368l-5.339,3.052-5.339-3.052,5.339-3.051,5.339,3.051Zm-11.339.775l5.5,3.143v6.45l-5.5-3.143v-6.45Zm15.5,14.027c-.827,0-1.5-.673-1.5-1.5s.673-1.5,1.5-1.5,1.5.673,1.5,1.5-.673,1.5-1.5,1.5Z\"><\/path><\/svg>\t\t\t\t<\/span>\n\t\t\t<\/div>\n\t\t\t\n\t\t\t\t\t\t<div class=\"elementor-icon-box-content\">\n\n\t\t\t\t\t\t\t\t\t<h3 class=\"elementor-icon-box-title\">\n\t\t\t\t\t\t<span  >\n\t\t\t\t\t\t\tEJECUTAR\t\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/h3>\n\t\t\t\t\n\t\t\t\t\t\t\t\t\t<p class=\"elementor-icon-box-description\">\n\t\t\t\t\t\t03<br>\n\u2500\u2500\u2500\n<br><br>Aprendes a dirigir la ejecuci\u00f3n dentro de Revit, supervisar lo que ocurre y convertir procedimientos que funcionan en flujos de trabajo repetibles.\t\t\t\t\t<\/p>\n\t\t\t\t\n\t\t\t<\/div>\n\t\t\t\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-3786e37 e-con e-atomic-element e-flexbox-base e-188bfb5 \" data-id=\"3786e37\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"3786e37\">\n    \t\t<div class=\"elementor-element elementor-element-be4c79a elementor-position-inline-end elementor-widget__width-initial elementor-view-default elementor-mobile-position-block-start elementor-widget elementor-widget-icon-box\" data-id=\"be4c79a\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon-box.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-box-wrapper\">\n\n\t\t\t\t\t\t<div class=\"elementor-icon-box-icon\">\n\t\t\t\t<span  class=\"elementor-icon\">\n\t\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" id=\"Layer_1\" data-name=\"Layer 1\" viewBox=\"0 0 24 24\" width=\"512\" height=\"512\"><path d=\"M23,12c0,.13-.05,.26-.15,.35l-1.83,1.83c-.66,.66-1.02,1.54-1.02,2.48v2.84c0,2.48-2.02,4.5-4.5,4.5h-1c-.28,0-.5-.22-.5-.5s.22-.5,.5-.5h1c1.93,0,3.5-1.57,3.5-3.5v-2.84c0-1.2,.47-2.33,1.32-3.18l1.48-1.47-1.48-1.47c-.85-.85-1.32-1.98-1.32-3.18v-2.84c0-1.93-1.57-3.5-3.5-3.5h-1c-.28,0-.5-.22-.5-.5s.22-.5,.5-.5h1c2.48,0,4.5,2.02,4.5,4.5v2.84c0,.94,.36,1.81,1.02,2.48l1.83,1.83c.09,.09,.15,.22,.15,.35ZM5,7.34v-2.84c0-1.93,1.57-3.5,3.5-3.5h1c.28,0,.5-.22,.5-.5s-.22-.5-.5-.5h-1c-2.48,0-4.5,2.02-4.5,4.5v2.84c0,.94-.36,1.81-1.02,2.48l-1.83,1.83c-.09,.09-.15,.22-.15,.35s.05,.26,.15,.35l1.83,1.83c.66,.66,1.02,1.54,1.02,2.48v2.84c0,2.48,2.02,4.5,4.5,4.5h1c.28,0,.5-.22,.5-.5s-.22-.5-.5-.5h-1c-1.93,0-3.5-1.57-3.5-3.5v-2.84c0-1.2-.47-2.33-1.32-3.18l-1.48-1.47,1.48-1.47c.85-.85,1.32-1.98,1.32-3.18Z\"><\/path><\/svg>\t\t\t\t<\/span>\n\t\t\t<\/div>\n\t\t\t\n\t\t\t\t\t\t<div class=\"elementor-icon-box-content\">\n\n\t\t\t\t\t\t\t\t\t<h3 class=\"elementor-icon-box-title\">\n\t\t\t\t\t\t<span  >\n\t\t\t\t\t\t\tDELIMITAR\t\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/h3>\n\t\t\t\t\n\t\t\t\t\t\t\t\t\t<p class=\"elementor-icon-box-description\">\n\t\t\t\t\t\t02<br>\n\u2500\u2500\u2500\n<br><br>Estableces qu\u00e9 quieres conseguir, qu\u00e9 puede ejecutar EdiBIM y qu\u00e9 decisiones deben permanecer bajo tu criterio antes de iniciar el trabajo.\t\t\t\t\t<\/p>\n\t\t\t\t\n\t\t\t<\/div>\n\t\t\t\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-bc8b5a7 elementor-position-inline-end elementor-widget__width-initial elementor-view-default elementor-mobile-position-block-start elementor-widget elementor-widget-icon-box\" data-id=\"bc8b5a7\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon-box.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-box-wrapper\">\n\n\t\t\t\t\t\t<div class=\"elementor-icon-box-icon\">\n\t\t\t\t<span  class=\"elementor-icon\">\n\t\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" id=\"Layer_1\" data-name=\"Layer 1\" viewBox=\"0 0 24 24\"><path d=\"m16.84,13.133c.203.188.215.504.027.707l-4.086,4.414c-.485.486-1.113.746-1.781.746s-1.296-.26-1.768-.732l-2.079-1.993c-.199-.191-.206-.507-.015-.707.191-.199.507-.205.707-.015l2.086,2c.581.581,1.562.574,2.128.007l4.073-4.4c.187-.203.504-.216.707-.027Zm5.16-3.148v9.515c0,2.481-2.019,4.5-4.5,4.5H6.5c-2.481,0-4.5-2.019-4.5-4.5V4.5C2,2.019,4.019,0,6.5,0h5.515c1.736,0,3.369.676,4.596,1.904l3.485,3.485c1.228,1.228,1.904,2.86,1.904,4.596Zm-6.096-7.375c-.55-.55-1.2-.959-1.904-1.231v5.121c0,.827.673,1.5,1.5,1.5h5.121c-.273-.704-.682-1.354-1.231-1.904l-3.485-3.485Zm5.096,7.375c0-.335-.038-.663-.096-.985h-5.404c-1.378,0-2.5-1.122-2.5-2.5V1.096c-.323-.058-.651-.096-.985-.096h-5.515c-1.93,0-3.5,1.57-3.5,3.5v15c0,1.93,1.57,3.5,3.5,3.5h11c1.93,0,3.5-1.57,3.5-3.5v-9.515Z\"><\/path><\/svg>\t\t\t\t<\/span>\n\t\t\t<\/div>\n\t\t\t\n\t\t\t\t\t\t<div class=\"elementor-icon-box-content\">\n\n\t\t\t\t\t\t\t\t\t<h3 class=\"elementor-icon-box-title\">\n\t\t\t\t\t\t<span  >\n\t\t\t\t\t\t\tVERIFICAR\t\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/h3>\n\t\t\t\t\n\t\t\t\t\t\t\t\t\t<p class=\"elementor-icon-box-description\">\n\t\t\t\t\t\t04<br>\n\u2500\u2500\u2500\n<br><br>Detectas resultados incompletos o incorrectos, revisas los cambios realizados y validas el resultado antes de incorporarlo al flujo de trabajo.\t\t\t\t\t<\/p>\n\t\t\t\t\n\t\t\t<\/div>\n\t\t\t\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n\n<\/div>\n<div class=\"elementor-element elementor-element-24b04fa e-con e-atomic-element e-flexbox-base e-192c29e edibim-seguridad \" data-id=\"24b04fa\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"24b04fa\">\n    <div class=\"elementor-element elementor-element-529633b e-con e-atomic-element e-flexbox-base edibim-seguridad-contenido e-529633b-b5aad3a \" data-id=\"529633b\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"529633b\">\n    \t\t<div class=\"elementor-element elementor-element-e715f08 elementor-widget__width-inherit elementor-absolute edibim-focus-layer elementor-widget elementor-widget-html\" data-id=\"e715f08\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;_position&quot;:&quot;absolute&quot;}\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- =========================================================\r\n     EDIBIM \u00b7 FONDO FOCUS INDUSTRIES\r\n     Basado en la escena original de la referencia\r\n========================================================= -->\r\n\r\n<div class=\"edibim-focus-bg\">\r\n    <canvas class=\"edibim-focus-canvas\"><\/canvas>\r\n<\/div>\r\n\r\n<style>\r\n\r\n\/* =========================================================\r\n   CONTENEDOR PRINCIPAL\r\n   El Flexbox de Elementor debe tener:\r\n   edibim-seguridad\r\n========================================================= *\/\r\n\r\n.edibim-seguridad{\r\n    position:relative !important;\r\n    overflow:hidden !important;\r\n    isolation:isolate;\r\n    background:#DADADA !important;\r\n    padding:0 !important;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   WIDGET HTML = CAPA COMPLETA DEL FONDO\r\n========================================================= *\/\r\n\r\n.edibim-seguridad .edibim-focus-layer{\r\n    position:absolute !important;\r\n\r\n    top:0 !important;\r\n    right:0 !important;\r\n    bottom:0 !important;\r\n    left:0 !important;\r\n\r\n    width:100% !important;\r\n    height:100% !important;\r\n\r\n    max-width:none !important;\r\n\r\n    margin:0 !important;\r\n    padding:0 !important;\r\n\r\n    z-index:0 !important;\r\n\r\n    pointer-events:none !important;\r\n}\r\n\r\n\r\n\/* Wrapper interno de Elementor *\/\r\n.edibim-seguridad\r\n.edibim-focus-layer\r\n.elementor-widget-container{\r\n\r\n    position:absolute !important;\r\n\r\n    inset:0 !important;\r\n\r\n    width:100% !important;\r\n    height:100% !important;\r\n\r\n    margin:0 !important;\r\n    padding:0 !important;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   CONTENEDOR THREE\r\n========================================================= *\/\r\n\r\n.edibim-focus-bg{\r\n\r\n    position:absolute !important;\r\n\r\n    inset:0 !important;\r\n\r\n    width:100% !important;\r\n    height:100% !important;\r\n\r\n    overflow:hidden !important;\r\n\r\n    pointer-events:none !important;\r\n\r\n    z-index:0 !important;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   CANVAS\r\n========================================================= *\/\r\n\r\n.edibim-focus-canvas{\r\n\r\n    position:absolute !important;\r\n\r\n    top:0 !important;\r\n    left:0 !important;\r\n\r\n    display:block !important;\r\n\r\n    width:100% !important;\r\n    height:100% !important;\r\n\r\n    max-width:none !important;\r\n\r\n    pointer-events:none !important;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   CONTENIDO ELEMENTOR POR ENCIMA\r\n========================================================= *\/\r\n\r\n.edibim-seguridad-contenido{\r\n\r\n    position:relative !important;\r\n\r\n    z-index:2 !important;\r\n\r\n    width:100% !important;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   RESPONSIVE\r\n========================================================= *\/\r\n\r\n@media(max-width:767px){\r\n\r\n    .edibim-focus-bg{\r\n        opacity:.9;\r\n    }\r\n\r\n}\r\n\r\n\/* =========================================================\r\n   DIFUMINADO SUPERIOR E INFERIOR DEL FONDO\r\n========================================================= *\/\r\n\r\n.edibim-focus-bg{\r\n    -webkit-mask-image:\r\n        linear-gradient(\r\n            to bottom,\r\n            transparent 0%,\r\n            rgba(0,0,0,.25) 4%,\r\n            #000 12%,\r\n            #000 88%,\r\n            rgba(0,0,0,.25) 96%,\r\n            transparent 100%\r\n        );\r\n\r\n    mask-image:\r\n        linear-gradient(\r\n            to bottom,\r\n            transparent 0%,\r\n            rgba(0,0,0,.25) 4%,\r\n            #000 12%,\r\n            #000 88%,\r\n            rgba(0,0,0,.25) 96%,\r\n            transparent 100%\r\n        );\r\n}\r\n\r\n\r\n\r\n<\/style>\r\n\r\n\r\n<script>\r\n\r\n(function(){\r\n\r\n    \/* =====================================================\r\n       BUSCAR SECCI\u00d3N\r\n    ===================================================== *\/\r\n\r\n    const section =\r\n        document.querySelector(\r\n            '.edibim-seguridad'\r\n        );\r\n\r\n    if(!section) return;\r\n\r\n\r\n    const canvas =\r\n        section.querySelector(\r\n            '.edibim-focus-canvas'\r\n        );\r\n\r\n    if(!canvas) return;\r\n\r\n\r\n    \/* Evitar duplicados en Elementor *\/\r\n\r\n    if(\r\n        canvas.dataset\r\n        .edibimFocusInit === '1'\r\n    ){\r\n        return;\r\n    }\r\n\r\n    canvas.dataset\r\n        .edibimFocusInit = '1';\r\n\r\n\r\n    \/* =====================================================\r\n       INICIAR THREE\r\n    ===================================================== *\/\r\n\r\n    function iniciar(){\r\n\r\n        if(\r\n            typeof THREE ===\r\n            'undefined'\r\n        ){\r\n\r\n            setTimeout(\r\n                iniciar,\r\n                100\r\n            );\r\n\r\n            return;\r\n        }\r\n\r\n\r\n        \/* =================================================\r\n           MEDIDAS\r\n        ================================================= *\/\r\n\r\n        function getSize(){\r\n\r\n            return {\r\n\r\n                width:\r\n                    section.clientWidth\r\n                    || window.innerWidth,\r\n\r\n                height:\r\n                    section.clientHeight\r\n                    || window.innerHeight\r\n\r\n            };\r\n\r\n        }\r\n\r\n\r\n        let size =\r\n            getSize();\r\n\r\n\r\n        \/* =================================================\r\n           ESCENA\r\n        ================================================= *\/\r\n\r\n        const scene =\r\n            new THREE.Scene();\r\n\r\n\r\n        \/* =================================================\r\n           C\u00c1MARA\r\n\r\n           ORIGINAL:\r\n           FOV 60\r\n           Z 15\r\n        ================================================= *\/\r\n\r\n        const camera =\r\n            new THREE.PerspectiveCamera(\r\n\r\n                60,\r\n\r\n                size.width \/\r\n                size.height,\r\n\r\n                0.1,\r\n\r\n                1000\r\n\r\n            );\r\n\r\n\r\n        camera.position.set(\r\n            0,\r\n            0,\r\n            15\r\n        );\r\n\r\n\r\n        \/* =================================================\r\n           RENDERER\r\n        ================================================= *\/\r\n\r\n        const renderer =\r\n            new THREE.WebGLRenderer({\r\n\r\n                canvas:canvas,\r\n\r\n                powerPreference:\r\n                    'high-performance',\r\n\r\n                antialias:false,\r\n\r\n                alpha:true\r\n\r\n            });\r\n\r\n\r\n        renderer.setPixelRatio(\r\n\r\n            Math.min(\r\n                window.devicePixelRatio,\r\n                2\r\n            )\r\n\r\n        );\r\n\r\n\r\n        renderer.setSize(\r\n\r\n            size.width,\r\n            size.height,\r\n\r\n            false\r\n\r\n        );\r\n\r\n\r\n        \/*\r\n         * Fondo transparente para que\r\n         * Elementor controle #DADADA\r\n         *\/\r\n\r\n        renderer.setClearColor(\r\n            0x000000,\r\n            0\r\n        );\r\n\r\n\r\n        \/* =================================================\r\n           TORO ORIGINAL\r\n\r\n           radius = 3\r\n           tube = 1\r\n           radialSegments = 102\r\n           tubularSegments = 180\r\n        ================================================= *\/\r\n\r\n        const geometry =\r\n            new THREE.TorusGeometry(\r\n\r\n                3,\r\n                1,\r\n                102,\r\n                180\r\n\r\n            );\r\n\r\n\r\n        \/* =================================================\r\n           MATERIAL DE PUNTOS\r\n\r\n           Original:\r\n           color #A9A9A9\r\n           size .04\r\n           sizeAttenuation 170\r\n        ================================================= *\/\r\n\r\n        const material =\r\n            new THREE.ShaderMaterial({\r\n\r\n                uniforms:{\r\n\r\n                    color:{\r\n                        value:\r\n                            new THREE.Color(\r\n                                '#A9A9A9'\r\n                            )\r\n                    },\r\n\r\n                    size:{\r\n                        value:.04\r\n                    },\r\n\r\n                    sizeAttenuation:{\r\n                        value:170\r\n                    }\r\n\r\n                },\r\n\r\n\r\n                vertexShader:`\r\n\r\n                    uniform float size;\r\n                    uniform float sizeAttenuation;\r\n\r\n                    void main(){\r\n\r\n                        vec4 mvPosition =\r\n                            modelViewMatrix\r\n                            *\r\n                            vec4(\r\n                                position,\r\n                                1.0\r\n                            );\r\n\r\n                        gl_PointSize =\r\n                            size\r\n                            *\r\n                            (\r\n                                sizeAttenuation\r\n                                \/\r\n                                -mvPosition.z\r\n                            );\r\n\r\n                        gl_Position =\r\n                            projectionMatrix\r\n                            *\r\n                            mvPosition;\r\n\r\n                    }\r\n\r\n                `,\r\n\r\n\r\n                fragmentShader:`\r\n\r\n                    uniform vec3 color;\r\n\r\n                    void main(){\r\n\r\n                        vec2 center =\r\n                            gl_PointCoord\r\n                            -\r\n                            vec2(.5);\r\n\r\n                        if(\r\n                            dot(\r\n                                center,\r\n                                center\r\n                            )\r\n                            >\r\n                            .25\r\n                        ){\r\n                            discard;\r\n                        }\r\n\r\n                        gl_FragColor =\r\n                            vec4(\r\n                                color,\r\n                                1.0\r\n                            );\r\n\r\n                    }\r\n\r\n                `,\r\n\r\n                transparent:false\r\n\r\n            });\r\n\r\n\r\n        \/* =================================================\r\n           OBJETO\r\n        ================================================= *\/\r\n\r\n        const torus =\r\n            new THREE.Points(\r\n\r\n                geometry,\r\n                material\r\n\r\n            );\r\n\r\n\r\n        \/*\r\n         * POSICI\u00d3N ORIGINAL\r\n         *\r\n         * X 0\r\n         * Y 3\r\n         * Z 14\r\n         *\/\r\n\r\n        torus.position.set(\r\n\r\n            0,\r\n            3,\r\n            14\r\n\r\n        );\r\n\r\n\r\n        \/*\r\n         * ROTACI\u00d3N ORIGINAL\r\n         *\r\n         * X 0\u00b0\r\n         * Y 90\u00b0\r\n         * Z 0\u00b0\r\n         *\/\r\n\r\n        torus.rotation.set(\r\n\r\n            0,\r\n\r\n            THREE.MathUtils\r\n                .degToRad(90),\r\n\r\n            0\r\n\r\n        );\r\n\r\n\r\n        scene.add(\r\n            torus\r\n        );\r\n\r\n\r\n        \/* =================================================\r\n           ESTADO DE VISIBILIDAD\r\n        ================================================= *\/\r\n\r\n        let visible =\r\n            true;\r\n\r\n\r\n        const observer =\r\n            new IntersectionObserver(\r\n\r\n                function(entries){\r\n\r\n                    visible =\r\n                        entries[0]\r\n                        .isIntersecting;\r\n\r\n                },\r\n\r\n                {\r\n                    threshold:.1\r\n                }\r\n\r\n            );\r\n\r\n\r\n        observer.observe(\r\n            section\r\n        );\r\n\r\n\r\n        \/* =================================================\r\n           MOVIMIENTO REDUCIDO\r\n        ================================================= *\/\r\n\r\n        const reducedMotion =\r\n            window.matchMedia(\r\n\r\n                '(prefers-reduced-motion: reduce)'\r\n\r\n            ).matches;\r\n\r\n\r\n        \/* =================================================\r\n           ANIMACI\u00d3N ORIGINAL\r\n\r\n           time += .005\r\n\r\n           X:\r\n           center 0 \u00b1 20\u00b0\r\n\r\n           Y:\r\n           center 90\u00b0 \u00b1 20\u00b0\r\n\r\n           Z:\r\n           -time\r\n\r\n           Y position:\r\n           3 + .3*sin(time)\r\n        ================================================= *\/\r\n\r\n        let time = 0;\r\n\r\n\r\n        function animate(){\r\n\r\n            requestAnimationFrame(\r\n                animate\r\n            );\r\n\r\n\r\n            if(!visible){\r\n                return;\r\n            }\r\n\r\n\r\n            if(\r\n                !reducedMotion\r\n            ){\r\n\r\n                time += .005;\r\n\r\n\r\n                const wave =\r\n                    Math.sin(\r\n                        time\r\n                    );\r\n\r\n\r\n                torus.rotation.set(\r\n\r\n                    THREE.MathUtils\r\n                        .degToRad(\r\n                            20 *\r\n                            wave\r\n                        ),\r\n\r\n                    THREE.MathUtils\r\n                        .degToRad(\r\n                            90\r\n                            +\r\n                            20 *\r\n                            wave\r\n                        ),\r\n\r\n                    -time\r\n\r\n                );\r\n\r\n\r\n                torus.position.set(\r\n\r\n                    0,\r\n\r\n                    3\r\n                    +\r\n                    .3 *\r\n                    wave,\r\n\r\n                    14\r\n\r\n                );\r\n\r\n            }\r\n\r\n\r\n            renderer.render(\r\n\r\n                scene,\r\n                camera\r\n\r\n            );\r\n\r\n        }\r\n\r\n\r\n        requestAnimationFrame(\r\n            animate\r\n        );\r\n\r\n\r\n        \/* =================================================\r\n           RESIZE\r\n        ================================================= *\/\r\n\r\n        function resize(){\r\n\r\n            const current =\r\n                getSize();\r\n\r\n\r\n            if(\r\n                !current.width\r\n                ||\r\n                !current.height\r\n            ){\r\n                return;\r\n            }\r\n\r\n\r\n            camera.aspect =\r\n\r\n                current.width\r\n                \/\r\n                current.height;\r\n\r\n\r\n            camera\r\n                .updateProjectionMatrix();\r\n\r\n\r\n            renderer.setSize(\r\n\r\n                current.width,\r\n                current.height,\r\n\r\n                false\r\n\r\n            );\r\n\r\n        }\r\n\r\n\r\n        const resizeObserver =\r\n            new ResizeObserver(\r\n                resize\r\n            );\r\n\r\n\r\n        resizeObserver.observe(\r\n            section\r\n        );\r\n\r\n\r\n        window.addEventListener(\r\n\r\n            'resize',\r\n\r\n            resize,\r\n\r\n            {\r\n                passive:true\r\n            }\r\n\r\n        );\r\n\r\n\r\n    }\r\n\r\n\r\n    \/* =====================================================\r\n       CARGAR THREE.JS\r\n    ===================================================== *\/\r\n\r\n    if(\r\n        typeof THREE ===\r\n        'undefined'\r\n    ){\r\n\r\n        const script =\r\n            document.createElement(\r\n                'script'\r\n            );\r\n\r\n\r\n        script.src =\r\n\r\n            'https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/three.js\/0.160.0\/three.min.js';\r\n\r\n\r\n        script.onload =\r\n            iniciar;\r\n\r\n\r\n        document.head.appendChild(\r\n            script\r\n        );\r\n\r\n    }\r\n\r\n    else{\r\n\r\n        iniciar();\r\n\r\n    }\r\n\r\n})();\r\n\r\n<\/script>\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-10fbbf2 e-con e-atomic-element e-flexbox-base e-10fbbf2-7a747c0 \" data-id=\"10fbbf2\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"10fbbf2\">\n    \t\t<div class=\"elementor-element elementor-element-516e553 elementor-widget elementor-widget-text-editor\" data-id=\"516e553\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>SEGURIDAD Y CONTROL<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-4a7daf9 elementor-widget elementor-widget-text-editor\" data-id=\"4a7daf9\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h1 class=\"edibim-title\"><strong>Una herramienta que act\u00faa necesita controles claros<\/strong><\/h1>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-19f78ca elementor-widget elementor-widget-text-editor\" data-id=\"19f78ca\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>EdiBIM puede consultar y modificar informaci\u00f3n dentro de Revit. Por eso es importante saber <strong data-start=\"326\" data-end=\"412\">qu\u00e9 informaci\u00f3n procesa, c\u00f3mo ejecuta los cambios y qu\u00e9 permanece bajo tu control.<\/strong><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-41f2150 elementor-widget elementor-widget-html\" data-id=\"41f2150\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"edibim-cycle\">\r\n\r\n    <div class=\"edibim-cycle-item is-active\">\r\n        <h3>EL MODELO PERMANECE EN TU EQUIPO<\/h3>\r\n        <p>\r\n            El archivo de Revit (.rvt) permanece en tu equipo y no se carga completo\r\n            en servidores de Editeca ni del proveedor del asistente.\r\n        <\/p>\r\n    <\/div>\r\n\r\n    <div class=\"edibim-cycle-item\">\r\n        <h3>QU\u00c9 INFORMACI\u00d3N PROCESA LA IA<\/h3>\r\n        <p>\r\n            La aplicaci\u00f3n de IA s\u00ed procesa la conversaci\u00f3n, los datos que devuelven\r\n            las herramientas y las capturas que utilizas.\r\n        <\/p>\r\n    <\/div>\r\n\r\n    <div class=\"edibim-cycle-item\">\r\n        <h3>CAMBIOS CONTROLADOS EN REVIT<\/h3>\r\n        <p>\r\n            Cada operaci\u00f3n que modifica el modelo se ejecuta dentro de una transacci\u00f3n.\r\n            Si falla, se revierten los cambios de esa operaci\u00f3n; si termina, deja su\r\n            propia entrada de Deshacer.\r\n        <\/p>\r\n    <\/div>\r\n\r\n    <div class=\"edibim-cycle-item\">\r\n        <h3>C\u00d3DIGO Y ARCHIVOS LOCALES<\/h3>\r\n        <p>\r\n            El c\u00f3digo din\u00e1mico pasa por un validador antes de compilarse.\r\n            Las funciones de exportaci\u00f3n y diagn\u00f3stico pueden escribir archivos locales.\r\n        <\/p>\r\n    <\/div>\r\n\r\n<\/div>\r\n\r\n<style>\r\n\r\n.edibim-cycle{\r\n    position:relative;\r\n    width:100%;\r\n    max-width:1250px;\r\n    margin:0 auto;\r\n\r\n font-family:\"Oxanium\", sans-serif;\r\n\r\n    display:flex;\r\n    flex-direction:column;\r\n    align-items:center;\r\n    justify-content:center;\r\n\r\n    gap:4px;\r\n\r\n    min-height:700px;\r\n}\r\n\r\n.edibim-cycle-item{\r\n    position:relative;\r\n\r\n    width:100%;\r\n\r\n    text-align:center;\r\n\r\n    padding:24px 90px;\r\n\r\n    cursor:pointer;\r\n\r\n    opacity:.70;\r\n\r\n    transform:scale(.92);\r\n\r\n    transition:\r\n        opacity .45s cubic-bezier(.22,.8,.22,1),\r\n        transform .45s cubic-bezier(.22,.8,.22,1);\r\n}\r\n\r\n.edibim-cycle-item h3{\r\n    position:relative;\r\n\r\n    margin:0;\r\n\r\n    font-size:clamp(42px,5vw,90px);\r\n    line-height:.92;\r\n\r\n    font-weight:500;\r\n    letter-spacing:-.045em;\r\n\r\n    color:#111111;\r\n}\r\n\r\n.edibim-cycle-item p{\r\n    max-width:650px;\r\n    margin:14px auto 0;\r\n\r\n    min-height:84px;\r\n    max-height:84px;\r\n\r\n    opacity:0;\r\n\r\n    overflow:hidden;\r\n\r\n    transform:translateY(-8px);\r\n\r\n    font-size:16px;\r\n    line-height:1.3;\r\n\r\n    color:#111111;\r\n\r\n    visibility:hidden;\r\n\r\n    transition:\r\n        opacity .35s ease,\r\n        transform .45s ease,\r\n        visibility .35s ease;\r\n}\r\n\r\n.edibim-cycle-item.is-active{\r\n    opacity:1;\r\n    transform:scale(1);\r\n}\r\n\r\n.edibim-cycle-item.is-active p{\r\n    opacity:1;\r\n    transform:translateY(0);\r\n    visibility:visible;\r\n}\r\n\r\n\r\n\/* =====================================================\r\n   ESQUINAS DEL ELEMENTO ACTIVO\r\n===================================================== *\/\r\n\r\n.edibim-cycle-item.is-active::before,\r\n.edibim-cycle-item.is-active::after,\r\n.edibim-cycle-item.is-active h3::before,\r\n.edibim-cycle-item.is-active h3::after{\r\n\r\n    content:\"\";\r\n\r\n    position:absolute;\r\n\r\n    width:28px;\r\n    height:28px;\r\n\r\n    pointer-events:none;\r\n}\r\n\r\n\r\n\/* superior izquierda *\/\r\n\r\n.edibim-cycle-item.is-active::before{\r\n\r\n    top:8px;\r\n    left:60px;\r\n\r\n    border-top:3px solid #ED6D40;\r\n    border-left:3px solid #ED6D40;\r\n}\r\n\r\n\r\n\/* superior derecha *\/\r\n\r\n.edibim-cycle-item.is-active::after{\r\n\r\n    top:8px;\r\n    right:60px;\r\n\r\n    border-top:3px solid #ED6D40;\r\n    border-right:3px solid #ED6D40;\r\n}\r\n\r\n\r\n\/* inferior izquierda *\/\r\n\r\n.edibim-cycle-item.is-active h3::before{\r\n\r\n    bottom:-58px;\r\n    left:0;\r\n\r\n    border-bottom:3px solid #ED6D40;\r\n    border-left:3px solid #ED6D40;\r\n}\r\n\r\n\r\n\/* inferior derecha *\/\r\n\r\n.edibim-cycle-item.is-active h3::after{\r\n\r\n    bottom:-58px;\r\n    right:0;\r\n\r\n    border-bottom:3px solid #ED6D40;\r\n    border-right:3px solid #ED6D40;\r\n}\r\n\r\n\r\n\/* =====================================================\r\n   HOVER DE LOS NO ACTIVOS\r\n===================================================== *\/\r\n\r\n.edibim-cycle-item:not(.is-active):hover{\r\n    opacity:.55;\r\n    transform:scale(.95);\r\n}\r\n\r\n\r\n\/* =====================================================\r\n   RESPONSIVE\r\n===================================================== *\/\r\n\r\n@media(max-width:767px){\r\n\r\n    .edibim-cycle{\r\n        min-height:620px;\r\n    }\r\n\r\n    .edibim-cycle-item{\r\n        padding:22px 25px;\r\n    }\r\n\r\n    .edibim-cycle-item h3{\r\n        font-size:clamp(34px,10vw,56px);\r\n    }\r\n\r\n    .edibim-cycle-item.is-active::before{\r\n        left:5px;\r\n    }\r\n\r\n    .edibim-cycle-item.is-active::after{\r\n        right:5px;\r\n    }\r\n\r\n}\r\n\r\n<\/style>\r\n\r\n<script>\r\n\r\n(function(){\r\n\r\n    const cycle =\r\n        document.querySelector(\r\n            '.edibim-cycle'\r\n        );\r\n\r\n    if(!cycle) return;\r\n\r\n\r\n    const items =\r\n        Array.from(\r\n            cycle.querySelectorAll(\r\n                '.edibim-cycle-item'\r\n            )\r\n        );\r\n\r\n\r\n    function activar(item){\r\n\r\n        items.forEach(function(el){\r\n\r\n            el.classList.remove(\r\n                'is-active'\r\n            );\r\n\r\n        });\r\n\r\n\r\n        item.classList.add(\r\n            'is-active'\r\n        );\r\n\r\n    }\r\n\r\n\r\n    items.forEach(function(item){\r\n\r\n        item.addEventListener(\r\n            'mouseenter',\r\n            function(){\r\n\r\n                activar(item);\r\n\r\n            }\r\n        );\r\n\r\n\r\n        item.addEventListener(\r\n            'focusin',\r\n            function(){\r\n\r\n                activar(item);\r\n\r\n            }\r\n        );\r\n\r\n\r\n        item.addEventListener(\r\n            'click',\r\n            function(){\r\n\r\n                activar(item);\r\n\r\n            }\r\n        );\r\n\r\n    });\r\n\r\n})();\r\n\r\n<\/script>\t\t\t\t<\/div>\n\t\t\n<\/div>\n\n<\/div>\n\n<\/div>\n<div class=\"elementor-element elementor-element-899a5b1 e-con e-atomic-element e-flexbox-base e-2ae8ea2 edibim-side-panel \" data-id=\"899a5b1\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"899a5b1\">\n    <div class=\"elementor-element elementor-element-8cf3dee e-con e-atomic-element e-flexbox-base edibim-side-inner e-8cf3dee-d26852b \" data-id=\"8cf3dee\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"8cf3dee\">\n    \t\t<div class=\"elementor-element elementor-element-292d647 elementor-position-left elementor-vertical-align-middle elementor-widget__width-initial elementor-widget elementor-widget-image-box\" data-id=\"292d647\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image-box.default\">\n\t\t\t\t\t<div class=\"elementor-image-box-wrapper\"><figure class=\"elementor-image-box-img\"><img decoding=\"async\" src=\"https:\/\/mcpbim.com\/wp-content\/uploads\/2026\/09\/logo-en-narnaja-edibim.svg\" class=\"attachment-full size-full wp-image-1662\" alt=\"\" \/><\/figure><div class=\"elementor-image-box-content\"><h3 class=\"elementor-image-box-title\">EDIBIM<\/h3><\/div><\/div>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-de5fd38 edibim-panel-close elementor-view-default elementor-widget elementor-widget-icon\" data-id=\"de5fd38\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-icon-wrapper\">\n\t\t\t<div class=\"elementor-icon elementor-animation-grow\">\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" id=\"Layer_1\" data-name=\"Layer 1\" viewBox=\"0 0 24 24\" width=\"512\" height=\"512\"><path d=\"M19.5,2H4.5C2.019,2,0,4.019,0,6.5v11c0,2.481,2.019,4.5,4.5,4.5h15c2.481,0,4.5-2.019,4.5-4.5V6.5c0-2.481-2.019-4.5-4.5-4.5Zm3.5,15.5c0,1.93-1.57,3.5-3.5,3.5H4.5c-1.93,0-3.5-1.57-3.5-3.5V6.5c0-1.93,1.57-3.5,3.5-3.5h15c1.93,0,3.5,1.57,3.5,3.5v11Zm-7.146-8.646l-3.146,3.146,3.146,3.146c.195,.195,.195,.512,0,.707-.098,.098-.226,.146-.354,.146s-.256-.049-.354-.146l-3.146-3.146-3.146,3.146c-.098,.098-.226,.146-.354,.146s-.256-.049-.354-.146c-.195-.195-.195-.512,0-.707l3.146-3.146-3.146-3.146c-.195-.195-.195-.512,0-.707s.512-.195,.707,0l3.146,3.146,3.146-3.146c.195-.195,.512-.195,.707,0s.195,.512,0,.707Z\"><\/path><\/svg>\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-fc078f7 e-con e-atomic-element e-flexbox-base e-fc078f7-1bde952 \" data-id=\"fc078f7\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"fc078f7\">\n    <div class=\"elementor-element elementor-element-0932689 e-con e-atomic-element e-flexbox-base e-0932689-2976b42 \" data-id=\"0932689\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"0932689\">\n    \t\t<div class=\"elementor-element elementor-element-78332a0 elementor-widget__width-initial elementor-widget elementor-widget-text-editor\" data-id=\"78332a0\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<div style=\"font-family: 'Oxanium', sans-serif; text-transform: uppercase;\"><div style=\"color: #52606a; font-size: 14px; font-weight: 500; line-height: 1.1; margin-bottom: 9px;\">APLICACI\u00d3N ACTUAL<\/div><div style=\"color: #111111; font-size: 16px; font-weight: 600; line-height: 1.25;\">REVIT<\/div><\/div>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-41a6eff elementor-widget__width-initial elementor-widget elementor-widget-text-editor\" data-id=\"41a6eff\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<div style=\"font-family: 'Oxanium', sans-serif; text-transform: uppercase;\"><div style=\"color: #52606a; font-size: 14px; font-weight: 500; line-height: 1.1; margin-bottom: 9px;\">SIGUIENTE INTEGRACI\u00d3N<\/div><div style=\"color: #111111; font-size: 16px; font-weight: 600; line-height: 1.25;\">NAVISWORKS<\/div><\/div>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ed2755c elementor-widget__width-initial elementor-widget elementor-widget-text-editor\" data-id=\"ed2755c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<div style=\"font-family: 'Oxanium', sans-serif; text-transform: uppercase;\"><div style=\"color: #52606a; font-size: 14px; font-weight: 500; line-height: 1.1; margin-bottom: 9px;\">FLUJO PROBADO<\/div><div style=\"color: #111111; font-size: 16px; font-weight: 600; line-height: 1.25;\">DETECCI\u00d3N \u2192 CORRECCI\u00d3N<\/div><\/div>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-fd98355 elementor-widget__width-initial elementor-widget elementor-widget-text-editor\" data-id=\"fd98355\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<div style=\"font-family: 'Oxanium', sans-serif; text-transform: uppercase;\"><div style=\"color: #52606a; font-size: 14px; font-weight: 500; line-height: 1.1; margin-bottom: 9px;\">ACCESO NAVISWORKS<\/div><div style=\"color: #111111; font-size: 16px; font-weight: 600; line-height: 1.25;\">EN PREPARACI\u00d3N<\/div><\/div>\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-6857bc1 e-con e-atomic-element e-flexbox-base e-6857bc1-b8b83c4 edibim-sticky \" data-id=\"6857bc1\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"6857bc1\">\n    \t\t<div class=\"elementor-element elementor-element-1bbd855 elementor-widget elementor-widget-text-editor\" data-id=\"1bbd855\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h3 class=\"PDq2pG_selectionAnchorContainer\" data-section-id=\"fcrq78\" data-start=\"106\" data-end=\"132\"><b>PREPARADO PARA CRECER HACIA NUEVAS APLICACIONES<\/b><\/h3>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6418a45 elementor-widget elementor-widget-text-editor\" data-id=\"6418a45\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p class=\"PDq2pG_selectionAnchorContainer\" data-start=\"212\" data-end=\"429\"><strong>EdiBIM no est\u00e1 ligado a un \u00fanico programa<\/strong>, asistente de IA o conjunto cerrado de funciones. Su arquitectura permite incorporar nuevas aplicaciones y ampliar sus flujos a medida que Editeca los desarrolla y prueba.<\/p><p data-start=\"434\" data-end=\"638\"><strong>Hoy comienza en Revit y se extiende hacia Navisworks<\/strong>, con la capacidad de acompa\u00f1ar otros procesos BIM en el futuro. Cada ampliaci\u00f3n requiere una versi\u00f3n desarrollada, probada y publicada por Editeca.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<a href=\"#edibim-acesso\" target=\"_self\" class=\"e-1935b5f-9863ddd e-button-base\" data-interaction-id=\"1935b5f\">\n\t\tACCEDER A EDIBIM\n\t<\/a>\t\t\n<\/div>\n\n<\/div>\n\n<\/div>\n<div class=\"elementor-element elementor-element-df548ed e-con e-atomic-element e-flexbox-base e-73367d0 edibim-familia \" data-id=\"df548ed\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"df548ed\">\n    <div class=\"elementor-element elementor-element-94ef3e5 e-con e-atomic-element e-flexbox-base e-94ef3e5-6ccc257 \" data-id=\"94ef3e5\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"94ef3e5\" id=\"ecosistema\">\n    <div class=\"elementor-element elementor-element-cd692f6 e-con e-atomic-element e-flexbox-base e-cd692f6-8af2daf \" data-id=\"cd692f6\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"cd692f6\">\n    \t\t<div class=\"elementor-element elementor-element-8383941 elementor-widget elementor-widget-text-editor\" data-id=\"8383941\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>FAMILIA EdiBIM<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-09a71e6 elementor-widget elementor-widget-text-editor\" data-id=\"09a71e6\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h2 class=\"edibim-title\"><strong>UNA FAMILIA QUE COMIENZA EN REVIT<\/strong><\/h2>\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n\t\t<div class=\"elementor-element elementor-element-94afc94 elementor-widget elementor-widget-text-editor\" data-id=\"94afc94\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p class=\"PDq2pG_selectionAnchorContainer\" data-start=\"2671\" data-end=\"2903\">EdiBIM re\u00fane los conectores desarrollados por Editeca para diferentes aplicaciones y flujos BIM, con un mismo criterio: darle contexto a la inteligencia artificial, dirigir operaciones dentro del software y comprobar sus resultados.<\/p><p style=\"padding-left: 40px;\"><strong data-start=\"2905\" data-end=\"2926\">\u25a0\u00a0EDIBIM PARA REVIT<\/strong><br data-start=\"2926\" data-end=\"2929\" \/>Ya est\u00e1 operativo y se incluye en la formaci\u00f3n Revit + IA.<\/p><p style=\"padding-left: 40px;\" data-start=\"2989\" data-end=\"3184\"><strong data-start=\"2989\" data-end=\"3015\">\u25a0\u00a0EDIBIM PARA NAVISWORKS<\/strong><br data-start=\"3015\" data-end=\"3018\" \/>Su conexi\u00f3n con Revit ya ha sido probada. Permite trabajar con incidencias detectadas en Navisworks, realizar correcciones en Revit y volver a comprobar el resultado.<\/p><h5 data-start=\"2989\" data-end=\"3184\">La formaci\u00f3n y el acceso para alumnos de EdiBIM para Navisworks se encuentran en preparaci\u00f3n.<\/h5>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-7ec0f73 elementor-widget elementor-widget-html\" data-id=\"7ec0f73\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<style>\r\n\r\n\/* =========================================================\r\n   CONTENEDOR DE LAS 4 TARJETAS\r\n   ========================================================= *\/\r\n\r\n.edibim-capacidades-cards{\r\n    display:flex !important;\r\n    flex-direction:row !important;\r\n    flex-wrap:nowrap !important;\r\n\r\n    gap:0 !important;\r\n\r\n    width:100% !important;\r\n\r\n    overflow:hidden !important;\r\n\r\n    align-items:stretch !important;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   TARJETAS\r\n   ========================================================= *\/\r\n\r\n.edibim-capacidad-card{\r\n\r\n    position:relative !important;\r\n\r\n    width:25% !important;\r\n    flex:0 0 25% !important;\r\n\r\n    min-width:0 !important;\r\n\r\n    overflow:hidden !important;\r\n\r\n    display:flex !important;\r\n    flex-direction:column !important;\r\n    justify-content:flex-start !important;\r\n\r\n    transition:\r\n        width .55s cubic-bezier(.65,0,.35,1),\r\n        flex-basis .55s cubic-bezier(.65,0,.35,1);\r\n\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   CONTENIDO INTERIOR\r\n\r\n   Mantiene un ancho estable para que\r\n   los textos no cambien de l\u00ednea durante el hover.\r\n   ========================================================= *\/\r\n\r\n.edibim-capacidad-card > .e-con-inner{\r\n\r\n    width:var(--edibim-content-width, 25%) !important;\r\n\r\n    min-width:var(--edibim-content-width, 25%) !important;\r\n    max-width:var(--edibim-content-width, 25%) !important;\r\n\r\n    flex:none !important;\r\n\r\n}\r\n\r\n\r\n\/* Si Elementor no utiliza .e-con-inner *\/\r\n.edibim-capacidad-card > .elementor-element{\r\n\r\n    flex-shrink:0 !important;\r\n\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   TEXTOS\r\n\r\n   Sin transiciones ni cambios raros.\r\n   ========================================================= *\/\r\n\r\n.edibim-capacidad-card .elementor-heading-title,\r\n.edibim-capacidad-card .elementor-widget-text-editor,\r\n.edibim-capacidad-card .elementor-widget-text-editor p{\r\n\r\n    transition:none !important;\r\n\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   M\u00d3VIL\r\n   ========================================================= *\/\r\n\r\n@media (max-width:767px){\r\n\r\n    .edibim-capacidades-cards{\r\n\r\n        flex-direction:column !important;\r\n\r\n        overflow:visible !important;\r\n\r\n    }\r\n\r\n\r\n    .edibim-capacidad-card{\r\n\r\n        width:100% !important;\r\n        flex-basis:100% !important;\r\n\r\n    }\r\n\r\n\r\n    .edibim-capacidad-card > .e-con-inner{\r\n\r\n        width:100% !important;\r\n\r\n        min-width:0 !important;\r\n        max-width:none !important;\r\n\r\n    }\r\n\r\n}\r\n\r\n<\/style>\r\n\r\n\r\n<script>\r\n\r\ndocument.addEventListener('DOMContentLoaded', function(){\r\n\r\n    const grupo = document.querySelector(\r\n        '.edibim-capacidades-cards'\r\n    );\r\n\r\n    if(!grupo){\r\n        console.log('No encuentro .edibim-capacidades-cards');\r\n        return;\r\n    }\r\n\r\n\r\n    const tarjetas = Array.from(\r\n        grupo.querySelectorAll(\r\n            '.edibim-capacidad-card'\r\n        )\r\n    );\r\n\r\n\r\n    console.log(\r\n        'Tarjetas encontradas:',\r\n        tarjetas.length\r\n    );\r\n\r\n\r\n    if(tarjetas.length < 4){\r\n        return;\r\n    }\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       MEDIR EL ANCHO ORIGINAL DE UNA TARJETA\r\n\r\n       En vez de usar 25vw, calculamos el\r\n       25% REAL del contenedor.\r\n       ===================================================== *\/\r\n\r\n    function medirContenido(){\r\n\r\n        if(window.innerWidth <= 767){\r\n            return;\r\n        }\r\n\r\n\r\n        const anchoGrupo =\r\n            grupo.getBoundingClientRect().width;\r\n\r\n\r\n        const anchoBase =\r\n            anchoGrupo \/ 4;\r\n\r\n\r\n        tarjetas.forEach(function(card){\r\n\r\n            card.style.setProperty(\r\n                '--edibim-content-width',\r\n                anchoBase + 'px'\r\n            );\r\n\r\n        });\r\n\r\n    }\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       ESTADO NORMAL\r\n       ===================================================== *\/\r\n\r\n    function normal(){\r\n\r\n        if(window.innerWidth <= 767){\r\n            return;\r\n        }\r\n\r\n\r\n        tarjetas.forEach(function(card){\r\n\r\n            card.style.setProperty(\r\n                'width',\r\n                '25%',\r\n                'important'\r\n            );\r\n\r\n\r\n            card.style.setProperty(\r\n                'flex-basis',\r\n                '25%',\r\n                'important'\r\n            );\r\n\r\n        });\r\n\r\n    }\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       ACTIVAR TARJETA\r\n\r\n       ACTIVA = 40%\r\n       RESTO  = 20%\r\n       ===================================================== *\/\r\n\r\n    function activar(activa){\r\n\r\n        if(window.innerWidth <= 767){\r\n            return;\r\n        }\r\n\r\n\r\n        tarjetas.forEach(function(card){\r\n\r\n            const valor =\r\n                card === activa\r\n                    ? '40%'\r\n                    : '20%';\r\n\r\n\r\n            card.style.setProperty(\r\n                'width',\r\n                valor,\r\n                'important'\r\n            );\r\n\r\n\r\n            card.style.setProperty(\r\n                'flex-basis',\r\n                valor,\r\n                'important'\r\n            );\r\n\r\n        });\r\n\r\n    }\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       HOVER\r\n       ===================================================== *\/\r\n\r\n    tarjetas.forEach(function(card){\r\n\r\n        card.addEventListener(\r\n            'mouseenter',\r\n            function(){\r\n\r\n                activar(card);\r\n\r\n            }\r\n        );\r\n\r\n    });\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       AL SALIR DEL GRUPO\r\n       ===================================================== *\/\r\n\r\n    grupo.addEventListener(\r\n        'mouseleave',\r\n        function(){\r\n\r\n            normal();\r\n\r\n        }\r\n    );\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       INICIO\r\n       ===================================================== *\/\r\n\r\n    medirContenido();\r\n    normal();\r\n\r\n\r\n\r\n    \/* =====================================================\r\n       REDIMENSIONAR\r\n       ===================================================== *\/\r\n\r\n    let resizeTimer;\r\n\r\n\r\n    window.addEventListener(\r\n        'resize',\r\n        function(){\r\n\r\n            clearTimeout(\r\n                resizeTimer\r\n            );\r\n\r\n\r\n            resizeTimer =\r\n                setTimeout(\r\n                    function(){\r\n\r\n                        medirContenido();\r\n                        normal();\r\n\r\n                    },\r\n                    150\r\n                );\r\n\r\n        }\r\n    );\r\n\r\n});\r\n\r\n<\/script>\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-bf40247 e-con e-atomic-element e-flexbox-base e-48088bd \" data-id=\"bf40247\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"bf40247\">\n    \t\t<div class=\"elementor-element elementor-element-76cb1a4 elementor-widget__width-initial video-hud edibim-open-panel elementor-widget elementor-widget-video\" data-id=\"76cb1a4\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;video_type&quot;:&quot;hosted&quot;,&quot;autoplay&quot;:&quot;yes&quot;,&quot;play_on_mobile&quot;:&quot;yes&quot;,&quot;mute&quot;:&quot;yes&quot;,&quot;loop&quot;:&quot;yes&quot;}\" data-widget_type=\"video.default\">\n\t\t\t\t\t\t\t<div class=\"e-hosted-video elementor-wrapper elementor-open-inline\">\n\t\t\t\t\t<video class=\"elementor-video\" src=\"https:\/\/mcpbim.com\/wp-content\/uploads\/2026\/09\/Mi-video.mp4\" autoplay=\"\" loop=\"\" muted=\"muted\" playsinline=\"\" controlsList=\"nodownload\" poster=\"https:\/\/mcpbim.com\/wp-content\/uploads\/2026\/09\/Snapshot_20260901101404.jpg\"><\/video>\n\t\t\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-04af089 elementor-widget elementor-widget-html\" data-id=\"04af089\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- FONDO OSCURO -->\r\n<div class=\"edibim-panel-overlay\"><\/div>\r\n\r\n<style>\r\n\r\n\/* =========================================\r\n   PANEL CREADO CON ELEMENTOR\r\n========================================= *\/\r\n\r\n.edibim-side-panel{\r\n    position:fixed !important;\r\n\r\n    top:0 !important;\r\n    right:0 !important;\r\n\r\n    width:min(50vw, 860px) !important;\r\n    max-width:none !important;\r\n\r\n    height:100dvh !important;\r\n    min-height:100dvh !important;\r\n\r\n    margin:0 !important;\r\n\r\n    background:#EDEDED;\r\n\r\n    z-index:99999 !important;\r\n\r\n    overflow-y:auto !important;\r\n    overflow-x:hidden !important;\r\n\r\n    border-radius:24px 0 0 24px;\r\n\r\n    transform:translateX(105%) !important;\r\n\r\n    transition:\r\n        transform .55s cubic-bezier(.22,.8,.22,1) !important;\r\n}\r\n\r\n\r\n\/* PANEL ABIERTO *\/\r\n\r\nbody.edibim-panel-open .edibim-side-panel{\r\n    transform:translateX(0) !important;\r\n}\r\n\r\n\r\n\/* =========================================\r\n   FONDO OSCURO \/ DESENFOQUE\r\n========================================= *\/\r\n\r\n.edibim-panel-overlay{\r\n    position:fixed;\r\n\r\n    inset:0;\r\n\r\n    width:100%;\r\n    height:100%;\r\n\r\n    background:rgba(0,0,0,.58);\r\n\r\n    backdrop-filter:blur(8px);\r\n    -webkit-backdrop-filter:blur(8px);\r\n\r\n    opacity:0;\r\n    visibility:hidden;\r\n\r\n    transition:\r\n        opacity .4s ease,\r\n        visibility .4s ease;\r\n\r\n    z-index:99998;\r\n}\r\n\r\n\r\nbody.edibim-panel-open .edibim-panel-overlay{\r\n    opacity:1;\r\n    visibility:visible;\r\n}\r\n\r\n\r\n\/* =========================================\r\n   M\u00d3VIL\r\n========================================= *\/\r\n\r\n@media(max-width:767px){\r\n\r\n    .edibim-side-panel{\r\n        width:100% !important;\r\n        border-radius:0 !important;\r\n    }\r\n\r\n}\r\n\r\n<\/style>\r\n\r\n\r\n<script>\r\n\r\n(function(){\r\n\r\n    function initEdibimPanel(){\r\n\r\n        const trigger = document.querySelector('.edibim-open-panel');\r\n        const panel = document.querySelector('.edibim-side-panel');\r\n        const overlay = document.querySelector('.edibim-panel-overlay');\r\n        const closeButton = document.querySelector('.edibim-panel-close');\r\n\r\n        if(!trigger || !panel || !overlay){\r\n            return;\r\n        }\r\n\r\n\r\n        function abrir(){\r\n\r\n            document.body.classList.add('edibim-panel-open');\r\n\r\n            document.documentElement.style.overflow = 'hidden';\r\n            document.body.style.overflow = 'hidden';\r\n\r\n        }\r\n\r\n\r\n        function cerrar(){\r\n\r\n            document.body.classList.remove('edibim-panel-open');\r\n\r\n            document.documentElement.style.overflow = '';\r\n            document.body.style.overflow = '';\r\n\r\n        }\r\n\r\n\r\n        \/* CLIC EN EL V\u00cdDEO *\/\r\n\r\n        trigger.addEventListener('click', function(e){\r\n\r\n            e.preventDefault();\r\n\r\n            abrir();\r\n\r\n        });\r\n\r\n\r\n        \/* CLIC FUERA *\/\r\n\r\n        overlay.addEventListener('click', cerrar);\r\n        \r\n        if(closeButton){\r\n    closeButton.addEventListener('click', cerrar);\r\n}\r\n\r\n\r\n        \/* ESC *\/\r\n\r\n        document.addEventListener('keydown', function(e){\r\n\r\n            if(e.key === 'Escape'){\r\n                cerrar();\r\n            }\r\n\r\n        });\r\n\r\n    }\r\n\r\n\r\n    if(document.readyState === 'loading'){\r\n\r\n        document.addEventListener(\r\n            'DOMContentLoaded',\r\n            initEdibimPanel\r\n        );\r\n\r\n    }else{\r\n\r\n        initEdibimPanel();\r\n\r\n    }\r\n\r\n})();\r\n\r\n<\/script>\t\t\t\t<\/div>\n\t\t\n<\/div>\n\t\t<div class=\"elementor-element elementor-element-36bb84c edibim-familia-bg-layer elementor-widget elementor-widget-html\" data-id=\"36bb84c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- =========================================================\r\n     EDIBIM \u00b7 BLOQUE 7 \u00b7 FONDO ONDAS DE PUNTOS\r\n========================================================= -->\r\n\r\n<div class=\"edibim-familia-bg\">\r\n    <canvas class=\"edibim-familia-canvas\"><\/canvas>\r\n<\/div>\r\n\r\n<style>\r\n\r\n\/* =========================================================\r\n   SECCI\u00d3N PRINCIPAL\r\n========================================================= *\/\r\n\r\n.edibim-familia{\r\n    position:relative !important;\r\n    overflow:hidden !important;\r\n    isolation:isolate;\r\n\r\n\r\n    padding:0 !important;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   WIDGET HTML COMO FONDO\r\n========================================================= *\/\r\n\r\n.edibim-familia .edibim-familia-bg-layer{\r\n    position:absolute !important;\r\n\r\n    inset:0 !important;\r\n\r\n    width:100% !important;\r\n    height:100% !important;\r\n\r\n    max-width:none !important;\r\n\r\n    margin:0 !important;\r\n    padding:0 !important;\r\n\r\n    z-index:0 !important;\r\n\r\n    pointer-events:none !important;\r\n}\r\n\r\n\r\n.edibim-familia\r\n.edibim-familia-bg-layer\r\n.elementor-widget-container{\r\n\r\n    position:absolute !important;\r\n\r\n    inset:0 !important;\r\n\r\n    width:100% !important;\r\n    height:100% !important;\r\n\r\n    margin:0 !important;\r\n    padding:0 !important;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   FONDO THREE.JS\r\n========================================================= *\/\r\n\r\n.edibim-familia-bg{\r\n\r\n    position:absolute !important;\r\n\r\n    inset:0 !important;\r\n\r\n    width:100% !important;\r\n    height:100% !important;\r\n\r\n    overflow:hidden !important;\r\n\r\n    pointer-events:none !important;\r\n\r\n    z-index:0 !important;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   CANVAS\r\n========================================================= *\/\r\n\r\n.edibim-familia-canvas{\r\n\r\n    position:absolute !important;\r\n\r\n    inset:0 !important;\r\n\r\n    display:block !important;\r\n\r\n    width:100% !important;\r\n    height:100% !important;\r\n\r\n    max-width:none !important;\r\n\r\n    pointer-events:none !important;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   CONTENIDO DEL BLOQUE 7 POR ENCIMA\r\n========================================================= *\/\r\n\r\n.edibim-familia-contenido{\r\n\r\n    position:relative !important;\r\n\r\n    z-index:2 !important;\r\n\r\n    width:100% !important;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   DIFUMINADO ARRIBA Y ABAJO\r\n========================================================= *\/\r\n\r\n.edibim-familia-bg{\r\n\r\n    -webkit-mask-image:\r\n        linear-gradient(\r\n            to bottom,\r\n            transparent 0%,\r\n            #000 12%,\r\n            #000 88%,\r\n            transparent 100%\r\n        );\r\n\r\n    mask-image:\r\n        linear-gradient(\r\n            to bottom,\r\n            transparent 0%,\r\n            #000 12%,\r\n            #000 88%,\r\n            transparent 100%\r\n        );\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   MOBILE\r\n========================================================= *\/\r\n\r\n@media(max-width:767px){\r\n\r\n    .edibim-familia-bg{\r\n        opacity:.85;\r\n    }\r\n\r\n}\r\n\r\n<\/style>\r\n\r\n\r\n<script>\r\n\r\n(function(){\r\n\r\n    \/* =====================================================\r\n       LOCALIZAR SECCI\u00d3N\r\n    ===================================================== *\/\r\n\r\n    const section =\r\n        document.querySelector(\r\n            '.edibim-familia'\r\n        );\r\n\r\n    if(!section) return;\r\n\r\n\r\n    const canvas =\r\n        section.querySelector(\r\n            '.edibim-familia-canvas'\r\n        );\r\n\r\n    if(!canvas) return;\r\n\r\n\r\n    \/* Evitar inicializaci\u00f3n doble *\/\r\n\r\n    if(\r\n        canvas.dataset.edibimFamiliaInit === '1'\r\n    ){\r\n        return;\r\n    }\r\n\r\n    canvas.dataset.edibimFamiliaInit = '1';\r\n\r\n\r\n    \/* =====================================================\r\n       INICIAR ESCENA\r\n    ===================================================== *\/\r\n\r\n    function iniciar(){\r\n\r\n        if(\r\n            typeof THREE === 'undefined'\r\n        ){\r\n\r\n            setTimeout(\r\n                iniciar,\r\n                100\r\n            );\r\n\r\n            return;\r\n        }\r\n\r\n\r\n        \/* =================================================\r\n           TAMA\u00d1O\r\n        ================================================= *\/\r\n\r\n        function getSize(){\r\n\r\n            return{\r\n\r\n                width:\r\n                    section.clientWidth\r\n                    ||\r\n                    window.innerWidth,\r\n\r\n                height:\r\n                    section.clientHeight\r\n                    ||\r\n                    window.innerHeight\r\n\r\n            };\r\n\r\n        }\r\n\r\n\r\n        let size =\r\n            getSize();\r\n\r\n\r\n        \/* =================================================\r\n           ESCENA\r\n        ================================================= *\/\r\n\r\n        const scene =\r\n            new THREE.Scene();\r\n\r\n\r\n        \/* =================================================\r\n           C\u00c1MARA\r\n        ================================================= *\/\r\n\r\n        const camera =\r\n            new THREE.PerspectiveCamera(\r\n\r\n                65,\r\n\r\n                size.width \/\r\n                size.height,\r\n\r\n                .1,\r\n\r\n                1000\r\n\r\n            );\r\n\r\n\r\n        camera.position.set(\r\n\r\n            0,\r\n            8,\r\n            18\r\n\r\n        );\r\n\r\n\r\n        \/* =================================================\r\n           RENDERER\r\n        ================================================= *\/\r\n\r\n        const renderer =\r\n            new THREE.WebGLRenderer({\r\n\r\n                canvas:canvas,\r\n\r\n                alpha:true,\r\n\r\n                antialias:false,\r\n\r\n                powerPreference:\r\n                    'high-performance'\r\n\r\n            });\r\n\r\n\r\n        renderer.setPixelRatio(\r\n\r\n            Math.min(\r\n                window.devicePixelRatio,\r\n                2\r\n            )\r\n\r\n        );\r\n\r\n\r\n        renderer.setSize(\r\n\r\n            size.width,\r\n            size.height,\r\n\r\n            false\r\n\r\n        );\r\n\r\n\r\n        renderer.setClearColor(\r\n\r\n            0x000000,\r\n            0\r\n\r\n        );\r\n\r\n\r\n        \/* =================================================\r\n           GEOMETR\u00cdA\r\n\r\n           ORIGINAL:\r\n           34 x 24\r\n           85 x 60 segmentos\r\n        ================================================= *\/\r\n\r\n        const geometry =\r\n            new THREE.PlaneGeometry(\r\n\r\n                34,\r\n                24,\r\n\r\n                85,\r\n                60\r\n\r\n            );\r\n\r\n\r\n        \/* Guardamos geometr\u00eda original *\/\r\n\r\n        const position =\r\n            geometry.attributes.position;\r\n\r\n\r\n        const basePositions =\r\n            new Float32Array(\r\n                position.array\r\n            );\r\n\r\n\r\n        \/* =================================================\r\n           MATERIAL DE PUNTOS\r\n        ================================================= *\/\r\n\r\n        const material =\r\n            new THREE.PointsMaterial({\r\n\r\n                color:\r\n                    new THREE.Color(\r\n                        '#A9A9A9'\r\n                    ),\r\n\r\n                size:.055,\r\n\r\n                sizeAttenuation:true,\r\n\r\n                transparent:true,\r\n\r\n                opacity:.82\r\n\r\n            });\r\n\r\n\r\n        \/* =================================================\r\n           MALLA DE PUNTOS\r\n        ================================================= *\/\r\n\r\n        const points =\r\n            new THREE.Points(\r\n\r\n                geometry,\r\n                material\r\n\r\n            );\r\n\r\n\r\n        \/*\r\n         * Inclinaci\u00f3n que ten\u00edamos\r\n         * aproximadamente -70.2 grados\r\n         *\/\r\n\r\n        points.rotation.x =\r\n            -Math.PI * .39;\r\n\r\n\r\n        points.position.set(\r\n\r\n            0,\r\n            5,\r\n            -1\r\n\r\n        );\r\n\r\n\r\n        scene.add(\r\n            points\r\n        );\r\n\r\n\r\n        \/* =================================================\r\n           VISIBILIDAD\r\n        ================================================= *\/\r\n\r\n        let visible =\r\n            true;\r\n\r\n\r\n        const intersectionObserver =\r\n            new IntersectionObserver(\r\n\r\n                function(entries){\r\n\r\n                    visible =\r\n                        entries[0]\r\n                        .isIntersecting;\r\n\r\n                },\r\n\r\n                {\r\n                    threshold:.05\r\n                }\r\n\r\n            );\r\n\r\n\r\n        intersectionObserver.observe(\r\n            section\r\n        );\r\n\r\n\r\n        \/* =================================================\r\n           REDUCED MOTION\r\n        ================================================= *\/\r\n\r\n        const reducedMotion =\r\n            window.matchMedia(\r\n\r\n                '(prefers-reduced-motion: reduce)'\r\n\r\n            ).matches;\r\n\r\n\r\n        \/* =================================================\r\n           ANIMACI\u00d3N DE ONDAS\r\n        ================================================= *\/\r\n\r\n        let time = 0;\r\n\r\n\r\n        function animate(){\r\n\r\n            requestAnimationFrame(\r\n                animate\r\n            );\r\n\r\n\r\n            if(!visible){\r\n                return;\r\n            }\r\n\r\n\r\n            if(!reducedMotion){\r\n\r\n                time += .012;\r\n\r\n\r\n                for(\r\n                    let i = 0;\r\n                    i < position.count;\r\n                    i++\r\n                ){\r\n\r\n                    const index =\r\n                        i * 3;\r\n\r\n\r\n                    const x =\r\n                        basePositions[\r\n                            index\r\n                        ];\r\n\r\n\r\n                    const y =\r\n                        basePositions[\r\n                            index + 1\r\n                        ];\r\n\r\n\r\n                    \/*\r\n                     * Onda principal\r\n                     *\/\r\n\r\n                    const wave1 =\r\n\r\n                        Math.sin(\r\n\r\n                            x * .55\r\n                            +\r\n                            time * .8\r\n\r\n                        )\r\n\r\n                        * .42;\r\n\r\n\r\n                    \/*\r\n                     * Segunda onda\r\n                     *\/\r\n\r\n                    const wave2 =\r\n\r\n                        Math.cos(\r\n\r\n                            y * .7\r\n                            -\r\n                            time * .55\r\n\r\n                        )\r\n\r\n                        * .28;\r\n\r\n\r\n                    \/*\r\n                     * Irregularidad suave\r\n                     *\/\r\n\r\n                    const noise =\r\n\r\n                        Math.sin(\r\n\r\n                            (x + y) * .32\r\n                            +\r\n                            time * .35\r\n\r\n                        )\r\n\r\n                        * .12;\r\n\r\n\r\n                    position.array[\r\n                        index + 2\r\n                    ] =\r\n\r\n                        wave1\r\n                        +\r\n                        wave2\r\n                        +\r\n                        noise;\r\n\r\n                }\r\n\r\n\r\n                position.needsUpdate =\r\n                    true;\r\n\r\n            }\r\n\r\n\r\n            renderer.render(\r\n\r\n                scene,\r\n                camera\r\n\r\n            );\r\n\r\n        }\r\n\r\n\r\n        requestAnimationFrame(\r\n            animate\r\n        );\r\n\r\n\r\n        \/* =================================================\r\n           RESIZE\r\n        ================================================= *\/\r\n\r\n        function resize(){\r\n\r\n            const current =\r\n                getSize();\r\n\r\n\r\n            if(\r\n                !current.width\r\n                ||\r\n                !current.height\r\n            ){\r\n                return;\r\n            }\r\n\r\n\r\n            camera.aspect =\r\n\r\n                current.width\r\n                \/\r\n                current.height;\r\n\r\n\r\n            camera\r\n                .updateProjectionMatrix();\r\n\r\n\r\n            renderer.setSize(\r\n\r\n                current.width,\r\n                current.height,\r\n\r\n                false\r\n\r\n            );\r\n\r\n        }\r\n\r\n\r\n        window.addEventListener(\r\n\r\n            'resize',\r\n\r\n            resize,\r\n\r\n            {\r\n                passive:true\r\n            }\r\n\r\n        );\r\n\r\n    }\r\n\r\n\r\n    \/* =====================================================\r\n       CARGAR THREE.JS\r\n    ===================================================== *\/\r\n\r\n    if(\r\n        typeof THREE === 'undefined'\r\n    ){\r\n\r\n        const script =\r\n            document.createElement(\r\n                'script'\r\n            );\r\n\r\n\r\n        script.src =\r\n            'https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/three.js\/0.160.0\/three.min.js';\r\n\r\n\r\n        script.onload =\r\n            iniciar;\r\n\r\n\r\n        document.head.appendChild(\r\n            script\r\n        );\r\n\r\n    }\r\n\r\n    else{\r\n\r\n        iniciar();\r\n\r\n    }\r\n\r\n})();\r\n\r\n<\/script>\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-3574651 e-con e-atomic-element e-flexbox-base e-577c626 \" data-id=\"3574651\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"3574651\" id=\"edibim-acesso\">\n    <div class=\"elementor-element elementor-element-c2f2ae0 e-con e-atomic-element e-flexbox-base e-bcc7357 \" data-id=\"c2f2ae0\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"c2f2ae0\">\n    <div class=\"elementor-element elementor-element-0a42775 e-con e-atomic-element e-flexbox-base e-0a42775-a5294c5 \" data-id=\"0a42775\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"0a42775\">\n    \t\t<div class=\"elementor-element elementor-element-e0a6acc elementor-widget__width-initial elementor-widget elementor-widget-image\" data-id=\"e0a6acc\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" width=\"1\" height=\"1\" src=\"https:\/\/mcpbim.com\/wp-content\/uploads\/2026\/09\/logo-en-blanco-edibim.svg\" class=\"attachment-large size-large wp-image-1661\" alt=\"\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-02d1b0c e-con e-atomic-element e-flexbox-base e-02d1b0c-7591994 \" data-id=\"02d1b0c\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"02d1b0c\">\n    \t\t<div class=\"elementor-element elementor-element-0456690 elementor-widget elementor-widget-text-editor\" data-id=\"0456690\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>REG\u00cdSTRATE PARA EL LANZAMIENTO<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-863d0dc elementor-widget elementor-widget-text-editor\" data-id=\"863d0dc\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h2 class=\"edibim-title\">QUIERO CONOCER M\u00c1S SOBRE<strong> EDIBIM<\/strong><\/h2>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-e2ce26f elementor-widget elementor-widget-text-editor\" data-id=\"e2ce26f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p dir=\"auto\" data-start=\"497\" data-end=\"614\">Recibe informaci\u00f3n sobre el lanzamiento, nuevas integraciones y pr\u00f3ximas novedades de EdiBIM.<\/p><p dir=\"auto\" data-start=\"497\" data-end=\"614\"><strong>El registro es gratuito y no implica ninguna compra ni compromiso.<br \/><br \/><\/strong><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-eb764cb e-con e-atomic-element e-flexbox-base \" data-id=\"eb764cb\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"eb764cb\">\n    \t\t\t<button class=\"e-467f9ab-a54d1b3 edibim-open-contact e-button-base\" data-interaction-id=\"467f9ab\">\n\t\tQUIERO ESTAR EN LA LISTA DE ESPERA\n\t<\/button>\t\t\n<\/div>\n\n<\/div>\n\n<\/div>\n<div class=\"elementor-element elementor-element-f9f6d0e e-con e-atomic-element e-flexbox-base e-f9f6d0e-12e71b3 \" data-id=\"f9f6d0e\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"f9f6d0e\">\n    <div class=\"elementor-element elementor-element-359ca2c e-con e-atomic-element e-flexbox-base e-359ca2c-04182d8 \" data-id=\"359ca2c\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"359ca2c\">\n    \t\t<div class=\"elementor-element elementor-element-b1d40a6 elementor-widget__width-initial elementor-widget elementor-widget-image\" data-id=\"b1d40a6\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" width=\"1\" height=\"1\" src=\"https:\/\/mcpbim.com\/wp-content\/uploads\/2026\/09\/logo-en-negro-edibim.svg\" class=\"attachment-large size-large wp-image-1663\" alt=\"\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-011d077 e-con e-atomic-element e-flexbox-base e-011d077-c2c2505 \" data-id=\"011d077\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"011d077\">\n    \t\t<div class=\"elementor-element elementor-element-fcd8a2b elementor-widget elementor-widget-text-editor\" data-id=\"fcd8a2b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p style=\"text-align: right;\">ACCEDE A EDIBIM PARA REVIT<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6bc9030 elementor-widget elementor-widget-text-editor\" data-id=\"6bc9030\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h2 class=\"edibim-title\" style=\"text-align: right;\">QUIERO USAR YA<strong> EDIBIM CON EDITECA<\/strong><\/h2>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-caa21e5 elementor-widget elementor-widget-text-editor\" data-id=\"caa21e5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p class=\"PDq2pG_selectionAnchorContainer\" dir=\"auto\" style=\"text-align: right;\" data-start=\"730\" data-end=\"816\"><strong>EdiBIM para Revit ya est\u00e1 operativo y forma parte de nuestra formaci\u00f3n Revit + IA de Editeca.<\/strong><\/p><p dir=\"auto\" style=\"text-align: right;\" data-start=\"818\" data-end=\"961\">Aprende a instalarlo y utilizarlo sobre casos BIM reales: consultar el modelo, ejecutar operaciones dentro de Revit y comprobar los resultados.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-d253ea5 e-con e-atomic-element e-flexbox-base \" data-id=\"d253ea5\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"d253ea5\">\n    \t\t\t<a href=\"https:\/\/editeca.com\/arquitectura\/revit-ia\/\" target=\"_self\" class=\"e-d339bd7-cd428c9 e-button-base\" data-interaction-id=\"d339bd7\" rel=\"noopener\">\n\t\tACCEDER AL CURSO REVIT + IA\n\t<\/a>\t\t\n<\/div>\n\n<\/div>\n\n<\/div>\n\n<\/div>\n<div class=\"elementor-element elementor-element-02bc37a e-con e-atomic-element e-flexbox-base e-02bc37a-5fa23ce editeca-live-plan \" data-id=\"02bc37a\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"02bc37a\" id=\"edibim-acesso\">\n    <div class=\"elementor-element elementor-element-df0c08a e-con e-atomic-element e-flexbox-base e-df0c08a-ef9f92f \" data-id=\"df0c08a\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"df0c08a\">\n    \t\t<div class=\"elementor-element elementor-element-07dc8c8 edibim-footer-plan-widget elementor-widget elementor-widget-html\" data-id=\"07dc8c8\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div class=\"edibim-footer-plan-host\"><\/div>\r\n\r\n<style>\r\n\/* ============================================================\r\n   EDIBIM \u00b7 FOOTER \u00b7 EL MISMO PLANO ORIGINAL, LIMITADO A COLUMNA\r\n   ============================================================ *\/\r\n.edibim-footer-plan-widget,\r\n.edibim-footer-plan-widget .elementor-widget-container{\r\n    width:100% !important;\r\n    height:100% !important;\r\n    min-height:620px !important;\r\n}\r\n\r\n.edibim-footer-plan-host{\r\n    width:100% !important;\r\n    height:100% !important;\r\n    min-height:620px !important;\r\n}\r\n\r\n\/* En esta copia NO mostramos ning\u00fan c\u00edrculo\/cursor especial.\r\n   La interacci\u00f3n de habitaciones y muros sigue funcionando. *\/\r\n.edibim-footer-plan-host .elp-pointer,\r\n.edibim-footer-plan-host .elp-pointer-ring,\r\n.edibim-footer-plan-host .elp-pointer-glow,\r\n#edibim-footer-global-cursor{\r\n    display:none !important;\r\n}\r\n\r\n\/* ============================================================\r\n   EDITECA \u00b7 PLANO ARQUITECT\u00d3NICO INTERACTIVO + MOBILIARIO\r\n   Versi\u00f3n de un solo plano centrado\r\n   ============================================================ *\/\r\n\r\n.edibim-footer-plan-host{\r\n    position:relative !important;\r\n    overflow:hidden !important;\r\n    background:#201716 !important;\r\n    isolation:isolate;\r\n}\r\n\r\n#edibim-footer-plan-svg{\r\n    position:absolute !important;\r\n    inset:0 !important;\r\n    width:100% !important;\r\n    height:100% !important;\r\n    z-index:0 !important;\r\n    pointer-events:none !important;\r\n}\r\n\r\n#edibim-footer-plan-svg svg{\r\n    display:block;\r\n    width:100%;\r\n    height:100%;\r\n    overflow:hidden;\r\n}\r\n\r\n\/* Contenido Elementor por encima *\/\r\n.edibim-footer-plan-host > *:not(#edibim-footer-plan-svg):not(#edibim-footer-global-cursor){\r\n    \r\n    z-index:5 !important;\r\n}\r\n\r\n\/* Habitaciones *\/\r\n.elp-room{\r\n    fill:#b6c8bd;\r\n    fill-opacity:.016;\r\n    transition:fill-opacity .25s ease;\r\n}\r\n\r\n.elp-room.active{\r\n    fill-opacity:.085;\r\n}\r\n\r\n\/* Muros *\/\r\n.elp-outer-wall{\r\n    fill:none;\r\n    stroke:#cedbd2;\r\n    stroke-width:7;\r\n    stroke-opacity:.72;\r\n    stroke-linejoin:miter;\r\n    stroke-linecap:square;\r\n    vector-effect:non-scaling-stroke;\r\n}\r\n\r\n.elp-wall{\r\n    fill:none;\r\n    stroke:#baccc0;\r\n    stroke-width:3;\r\n    stroke-opacity:.58;\r\n    stroke-linecap:square;\r\n    vector-effect:non-scaling-stroke;\r\n}\r\n\r\n.elp-wall.active{\r\n    stroke:#e4eee7;\r\n    stroke-opacity:.95;\r\n    stroke-width:3.6;\r\n}\r\n\r\n\/* Puertas *\/\r\n.elp-door{\r\n    fill:none;\r\n    stroke:#aebfb5;\r\n    stroke-width:1.25;\r\n    stroke-opacity:.48;\r\n    vector-effect:non-scaling-stroke;\r\n}\r\n\r\n\/* Ventanas *\/\r\n.elp-window{\r\n    stroke:#d3dfd6;\r\n    stroke-width:1.25;\r\n    stroke-opacity:.52;\r\n    vector-effect:non-scaling-stroke;\r\n}\r\n\r\n\/* Nodos *\/\r\n.elp-node{\r\n    fill:#cfddd3;\r\n    opacity:.16;\r\n}\r\n\r\n\/* ============================================================\r\n   MOBILIARIO CENITAL\r\n   ============================================================ *\/\r\n\r\n.elp-furniture{\r\n    fill:none;\r\n    stroke:#c7d6cc;\r\n    stroke-width:1.35;\r\n    stroke-opacity:.45;\r\n    vector-effect:non-scaling-stroke;\r\n    transition:stroke-opacity .18s ease, fill-opacity .18s ease;\r\n}\r\n\r\n.elp-furniture-soft{\r\n    fill:#b8cabe;\r\n    fill-opacity:.018;\r\n    stroke:#c7d6cc;\r\n    stroke-width:1.05;\r\n    stroke-opacity:.34;\r\n    vector-effect:non-scaling-stroke;\r\n}\r\n\r\n.elp-furniture-detail{\r\n    fill:none;\r\n    stroke:#c7d6cc;\r\n    stroke-width:.9;\r\n    stroke-opacity:.34;\r\n    vector-effect:non-scaling-stroke;\r\n}\r\n\r\n.elp-furniture-fill{\r\n    fill:#c7d6cc;\r\n    fill-opacity:.022;\r\n    stroke:#c7d6cc;\r\n    stroke-width:1;\r\n    stroke-opacity:.38;\r\n    vector-effect:non-scaling-stroke;\r\n}\r\n\r\n.elp-furniture-accent{\r\n    fill:none;\r\n    stroke:#d9e5dd;\r\n    stroke-width:.85;\r\n    stroke-opacity:.42;\r\n    vector-effect:non-scaling-stroke;\r\n}\r\n\r\n.elp-furniture-room.active .elp-furniture,\r\n.elp-furniture-room.active .elp-furniture-soft{\r\n    stroke-opacity:.92;\r\n}\r\n\r\n.elp-furniture-room.active .elp-furniture-soft{\r\n    fill-opacity:.045;\r\n}\r\n\r\n.elp-furniture-room.active .elp-furniture-detail{\r\n    stroke-opacity:.62;\r\n}\r\n\r\n\/*\r\n * Las recolocaciones por colisi\u00f3n se hacen en SVG,\r\n * pero mantenemos un cambio visual limpio y discreto.\r\n *\/\r\n.elp-furniture-room > *{\r\n    transform-box:fill-box;\r\n    transform-origin:center;\r\n}\r\n\r\n\/* Cursor local *\/\r\n.elp-pointer{\r\n    fill:#e3ede6;\r\n    opacity:0;\r\n}\r\n\r\n.elp-pointer-ring{\r\n    fill:none;\r\n    stroke:#d7e4da;\r\n    stroke-width:1;\r\n    opacity:0;\r\n    vector-effect:non-scaling-stroke;\r\n}\r\n\r\n.elp-pointer-glow{\r\n    fill:url(#elpPointerGlow);\r\n    opacity:0;\r\n}\r\n\r\n\/* Cursor global *\/\r\n#edibim-footer-global-cursor{\r\n    position:fixed;\r\n    left:0;\r\n    top:0;\r\n    width:7.6px;\r\n    height:7.6px;\r\n    border-radius:50%;\r\n    background:#e3ede6;\r\n    pointer-events:none;\r\n    z-index:999;\r\n    opacity:0;\r\n    transform:translate(-50%,-50%);\r\n    will-change:left,top;\r\n    transition:opacity .18s ease;\r\n}\r\n\r\n#edibim-footer-global-cursor::before{\r\n    content:\"\";\r\n    position:absolute;\r\n    left:50%;\r\n    top:50%;\r\n    width:24px;\r\n    height:24px;\r\n    border-radius:50%;\r\n    border:1px solid rgba(215,228,218,.40);\r\n    transform:translate(-50%,-50%);\r\n    animation:elpCursorPulse 2s ease-in-out infinite;\r\n}\r\n\r\n#edibim-footer-global-cursor::after{\r\n    content:\"\";\r\n    position:absolute;\r\n    left:50%;\r\n    top:50%;\r\n    width:55px;\r\n    height:55px;\r\n    border-radius:50%;\r\n    transform:translate(-50%,-50%);\r\n    background:radial-gradient(\r\n        circle,\r\n        rgba(227,238,230,.20) 0%,\r\n        rgba(203,220,207,.07) 40%,\r\n        rgba(203,220,207,0) 72%\r\n    );\r\n}\r\n\r\n@keyframes elpCursorPulse{\r\n    0%,100%{\r\n        transform:translate(-50%,-50%) scale(.90);\r\n        opacity:.35;\r\n    }\r\n    50%{\r\n        transform:translate(-50%,-50%) scale(1.12);\r\n        opacity:.65;\r\n    }\r\n}\r\n\r\n@media (hover:none){\r\n    #edibim-footer-global-cursor{\r\n        display:none;\r\n    }\r\n}\r\n\r\n@media(max-width:1024px){\r\n    .edibim-footer-plan-widget,\r\n    .edibim-footer-plan-widget .elementor-widget-container,\r\n    .edibim-footer-plan-host{\r\n        min-height:520px !important;\r\n    }\r\n}\r\n\r\n@media(max-width:767px){\r\n    .edibim-footer-plan-widget,\r\n    .edibim-footer-plan-widget .elementor-widget-container,\r\n    .edibim-footer-plan-host{\r\n        min-height:420px !important;\r\n    }\r\n}\r\n<\/style>\r\n\r\n<script>\r\n(function(){\r\n\r\nfunction iniciarPlanoEditeca(){\r\n\r\n    const section = document.querySelector('.edibim-footer-plan-host');\r\n\r\n    if(!section){\r\n        console.warn('No encuentro .edibim-footer-plan-host');\r\n        return;\r\n    }\r\n\r\n    section.querySelectorAll(\r\n        '#edibim-footer-plan-svg'\r\n    ).forEach(el => el.remove());\r\n\r\n    \/* ========================================================\r\n       SVG \u00b7 SOLO UN PLANO Y CENTRADO\r\n       ======================================================== *\/\r\n\r\n    const layer = document.createElement('div');\r\n    layer.id = 'edibim-footer-plan-svg';\r\n\r\n    layer.innerHTML = `\r\n    <svg\r\n        viewBox=\"0 0 1200 900\"\r\n        preserveAspectRatio=\"xMidYMid meet\"\r\n        xmlns=\"http:\/\/www.w3.org\/2000\/svg\"\r\n        aria-hidden=\"true\"\r\n    >\r\n        <defs>\r\n            <radialGradient id=\"elpPointerGlow\">\r\n                <stop offset=\"0%\" stop-color=\"#e3eee6\" stop-opacity=\".28\"\/>\r\n                <stop offset=\"45%\" stop-color=\"#cbdccf\" stop-opacity=\".08\"\/>\r\n                <stop offset=\"100%\" stop-color=\"#cbdccf\" stop-opacity=\"0\"\/>\r\n            <\/radialGradient>\r\n        <\/defs>\r\n\r\n        <!--\r\n            FOOTER_PLAN_CENTER = se encarga de centrar\/mover el plano\r\n            FOOTER_PLAN_A      = se encarga \u00fanicamente de la escala\r\n\r\n            NO CAMBIES transform aqu\u00ed.\r\n            Para mover\/escalar usa PLAN_VIEW en el JavaScript.\r\n        -->\r\n        <g id=\"FOOTER_PLAN_CENTER\">\r\n            <g id=\"FOOTER_PLAN_A\">\r\n                <g id=\"footerRoomsA\"><\/g>\r\n            <g id=\"footerFurnitureA\"><\/g>\r\n            <g id=\"footerWallsA\"><\/g>\r\n            <g id=\"footerDoorsA\"><\/g>\r\n            <g id=\"footerWindowsA\"><\/g>\r\n            <g id=\"footerNodesA\"><\/g>\r\n\r\n                <circle id=\"footerPointerGlowA\" class=\"elp-pointer-glow\" r=\"55\"\/>\r\n                <circle id=\"footerPointerRingA\" class=\"elp-pointer-ring\" r=\"12\"\/>\r\n                <circle id=\"footerPointerA\" class=\"elp-pointer\" r=\"3.8\"\/>\r\n            <\/g>\r\n        <\/g>\r\n    <\/svg>`;\r\n\r\n    section.insertAdjacentElement('afterbegin',layer);\r\n\r\n    const svg = layer.querySelector('svg');\r\n    const planCenterGroup = layer.querySelector('#FOOTER_PLAN_CENTER');\r\n    const group = layer.querySelector('#FOOTER_PLAN_A');\r\n    const roomsGroup = layer.querySelector('#footerRoomsA');\r\n    const furnitureGroup = layer.querySelector('#footerFurnitureA');\r\n    const wallsGroup = layer.querySelector('#footerWallsA');\r\n    const doorsGroup = layer.querySelector('#footerDoorsA');\r\n    const windowsGroup = layer.querySelector('#footerWindowsA');\r\n    const nodesGroup = layer.querySelector('#footerNodesA');\r\n\r\n    const pointerCircle = layer.querySelector('#footerPointerA');\r\n    const pointerRing = layer.querySelector('#footerPointerRingA');\r\n    const pointerGlow = layer.querySelector('#footerPointerGlowA');\r\n\r\n    \/* Cursor global \u00fanico para ambas secciones *\/\r\nlet globalCursor = document.querySelector('#edibim-footer-global-cursor');\r\n\r\nif(!globalCursor){\r\n    globalCursor = document.createElement('div');\r\n    globalCursor.id = 'edibim-footer-global-cursor';\r\n    document.body.appendChild(globalCursor);\r\n}\r\n\r\n    let globalCursorX = 0;\r\n    let globalCursorY = 0;\r\n    let globalTargetX = 0;\r\n    let globalTargetY = 0;\r\n    let globalCursorActivo = false;\r\n\r\n    \/* ========================================================\r\n       POSICI\u00d3N Y ESCALA DEL PLANO\r\n       ========================================================\r\n\r\n       SOLO TIENES QUE TOCAR ESTOS 3 VALORES:\r\n\r\n       scale   = tama\u00f1o general del plano\r\n                 0.85 -> m\u00e1s peque\u00f1o\r\n                 1.00 -> tama\u00f1o normal\r\n                 1.15 -> m\u00e1s grande\r\n                 1.30 -> mucho m\u00e1s grande\r\n\r\n       offsetX = mover horizontalmente\r\n                 negativo -> izquierda\r\n                 positivo -> derecha\r\n\r\n       offsetY = mover verticalmente\r\n                 negativo -> arriba\r\n                 positivo -> abajo\r\n\r\n       IMPORTANTE:\r\n       El plano se centra AUTOM\u00c1TICAMENTE antes de aplicar estos offsets.\r\n       Por tanto puedes cambiar scale todas las veces que quieras y\r\n       seguir\u00e1 centrado.\r\n       ======================================================== *\/\r\n\r\n    const PLAN_VIEW = {\r\n        scale:1.18,\r\n        offsetX:-0,\r\n        offsetY:-0\r\n    };\r\n\r\n    const CONFIG = {\r\n        \/* M\u00e1s radio y bastante m\u00e1s desplazamiento que en la versi\u00f3n anterior *\/\r\n        wallRadius:135,\r\n        wallMovement:46,\r\n        roomExpansion:52,\r\n        easing:.072\r\n    };\r\n\r\n    const BASE = {\r\n        left:170,\r\n        right:995,\r\n        top:100,\r\n        bottom:650\r\n    };\r\n\r\n    \/*\r\n     * El dibujo tiene una peque\u00f1a prolongaci\u00f3n inferior hasta Y=690.\r\n     * Usamos esos l\u00edmites para calcular su centro geom\u00e9trico real.\r\n     *\/\r\n    const PLAN_BOUNDS = {\r\n        left:BASE.left,\r\n        right:BASE.right,\r\n        top:BASE.top,\r\n        bottom:690\r\n    };\r\n\r\n    function actualizarVistaPlano(){\r\n\r\n        const vb=svg.viewBox.baseVal;\r\n\r\n        \/*\r\n         * Centro del SVG \/ viewBox.\r\n         *\/\r\n        const svgCenterX=\r\n            vb.x+\r\n            vb.width\/2;\r\n\r\n        const svgCenterY=\r\n            vb.y+\r\n            vb.height\/2;\r\n\r\n        \/*\r\n         * Centro geom\u00e9trico real del plano.\r\n         *\/\r\n        const planCenterX=\r\n            PLAN_BOUNDS.left+\r\n            (PLAN_BOUNDS.right-PLAN_BOUNDS.left)\/2;\r\n\r\n        const planCenterY=\r\n            PLAN_BOUNDS.top+\r\n            (PLAN_BOUNDS.bottom-PLAN_BOUNDS.top)\/2;\r\n\r\n        \/*\r\n         * GRUPO EXTERIOR:\r\n         * coloca el centro del plano en el centro del SVG\r\n         * y despu\u00e9s aplica el desplazamiento manual.\r\n         *\/\r\n        planCenterGroup.setAttribute(\r\n            'transform',\r\n            `translate(${\r\n                svgCenterX+PLAN_VIEW.offsetX\r\n            } ${\r\n                svgCenterY+PLAN_VIEW.offsetY\r\n            })`\r\n        );\r\n\r\n        \/*\r\n         * GRUPO INTERIOR:\r\n         * escala alrededor del centro geom\u00e9trico del plano.\r\n         *\r\n         * As\u00ed cambiar scale NO altera su centrado.\r\n         *\/\r\n        group.setAttribute(\r\n            'transform',\r\n            `scale(${PLAN_VIEW.scale}) translate(${\r\n                -planCenterX\r\n            } ${\r\n                -planCenterY\r\n            })`\r\n        );\r\n    }\r\n\r\n    \/*\r\n     * Atajos opcionales para poder mover\/escalar el plano\r\n     * desde la consola sin editar otra parte del c\u00f3digo:\r\n     *\r\n     * edibimFooterPlano.setScale(1.15)\r\n     * edibimFooterPlano.setPosition(0,-40)\r\n     * edibimFooterPlano.setView(1.10,20,-30)\r\n     *\/\r\n    window.edibimFooterPlano = {\r\n        setScale(scale){\r\n            PLAN_VIEW.scale=Number(scale);\r\n            actualizarVistaPlano();\r\n        },\r\n\r\n        setPosition(x=0,y=0){\r\n            PLAN_VIEW.offsetX=Number(x);\r\n            PLAN_VIEW.offsetY=Number(y);\r\n            actualizarVistaPlano();\r\n        },\r\n\r\n        setView(scale=1,x=0,y=0){\r\n            PLAN_VIEW.scale=Number(scale);\r\n            PLAN_VIEW.offsetX=Number(x);\r\n            PLAN_VIEW.offsetY=Number(y);\r\n            actualizarVistaPlano();\r\n        },\r\n\r\n        getView(){\r\n            return {...PLAN_VIEW};\r\n        }\r\n    };\r\n\r\n    \/*\r\n     * Aplicamos el centrado inicial.\r\n     *\/\r\n    actualizarVistaPlano();\r\n\r\n    const ORIGINAL = {\r\n        x1:365,\r\n        x2:555,\r\n        x3:760,\r\n        y1:275,\r\n        y2:470\r\n    };\r\n\r\n    const LIMITS = {\r\n        \/*\r\n         * Permitimos cambios mucho m\u00e1s visibles.\r\n         * Los gaps m\u00ednimos evitan que una habitaci\u00f3n colapse.\r\n         *\/\r\n        x1min:285,\r\n        x1max:450,\r\n        x2max:690,\r\n        x3max:865,\r\n        minGapX:110,\r\n\r\n        y1min:205,\r\n        y1max:350,\r\n        y2max:565,\r\n        minGapY:115\r\n    };\r\n\r\n    const state = {\r\n        x1:ORIGINAL.x1,\r\n        x2:ORIGINAL.x2,\r\n        x3:ORIGINAL.x3,\r\n        y1:ORIGINAL.y1,\r\n        y2:ORIGINAL.y2,\r\n\r\n        targetX1:ORIGINAL.x1,\r\n        targetX2:ORIGINAL.x2,\r\n        targetX3:ORIGINAL.x3,\r\n        targetY1:ORIGINAL.y1,\r\n        targetY2:ORIGINAL.y2\r\n    };\r\n\r\n    const NS = 'http:\/\/www.w3.org\/2000\/svg';\r\n\r\n    function crearSVG(tipo,clase){\r\n        const element = document.createElementNS(NS,tipo);\r\n        if(clase) element.setAttribute('class',clase);\r\n        return element;\r\n    }\r\n\r\n    function setRect(el,x,y,w,h,rx=0){\r\n        el.setAttribute('x',x);\r\n        el.setAttribute('y',y);\r\n        el.setAttribute('width',Math.max(1,w));\r\n        el.setAttribute('height',Math.max(1,h));\r\n        if(rx) el.setAttribute('rx',rx);\r\n    }\r\n\r\n    function setLine(el,x1,y1,x2,y2){\r\n        el.setAttribute('x1',x1);\r\n        el.setAttribute('y1',y1);\r\n        el.setAttribute('x2',x2);\r\n        el.setAttribute('y2',y2);\r\n    }\r\n\r\n    function clamp(v,min,max){\r\n        return Math.max(min,Math.min(max,v));\r\n    }\r\n\r\n    function bbox(points){\r\n        const xs = points.map(p=>p[0]);\r\n        const ys = points.map(p=>p[1]);\r\n        return {\r\n            left:Math.min(...xs),\r\n            right:Math.max(...xs),\r\n            top:Math.min(...ys),\r\n            bottom:Math.max(...ys),\r\n            width:Math.max(...xs)-Math.min(...xs),\r\n            height:Math.max(...ys)-Math.min(...ys)\r\n        };\r\n    }\r\n\r\n    function puntoDentro(x,y,polygon){\r\n        let dentro=false;\r\n\r\n        for(let i=0,j=polygon.length-1;i<polygon.length;j=i++){\r\n            const xi=polygon[i][0];\r\n            const yi=polygon[i][1];\r\n            const xj=polygon[j][0];\r\n            const yj=polygon[j][1];\r\n\r\n            const intersecta =\r\n                ((yi>y)!==(yj>y)) &&\r\n                (x < ((xj-xi)*(y-yi))\/(yj-yi)+xi);\r\n\r\n            if(intersecta) dentro=!dentro;\r\n        }\r\n\r\n        return dentro;\r\n    }\r\n\r\n    function distanciaSegmento(px,py,ax,ay,bx,by){\r\n        const vx=bx-ax;\r\n        const vy=by-ay;\r\n        const wx=px-ax;\r\n        const wy=py-ay;\r\n        const lengthSquared=vx*vx+vy*vy;\r\n\r\n        let t = lengthSquared ? (wx*vx+wy*vy)\/lengthSquared : 0;\r\n        t=Math.max(0,Math.min(1,t));\r\n\r\n        const x=ax+vx*t;\r\n        const y=ay+vy*t;\r\n        const dx=px-x;\r\n        const dy=py-y;\r\n\r\n        return {\r\n            distance:Math.sqrt(dx*dx+dy*dy),\r\n            x,y,t\r\n        };\r\n    }\r\n\r\n    \/* ========================================================\r\n       HABITACIONES\r\n       ======================================================== *\/\r\n\r\n    const rooms = [\r\n        {\r\n            name:'DORMITORIO',\r\n            type:'bedroom',\r\n            getPoints(){\r\n                return [\r\n                    [BASE.left,BASE.top],\r\n                    [state.x1,BASE.top],\r\n                    [state.x1,state.y1],\r\n                    [BASE.left,state.y1]\r\n                ];\r\n            }\r\n        },\r\n        {\r\n            name:'DORMITORIO',\r\n            type:'bedroom',\r\n            getPoints(){\r\n                return [\r\n                    [state.x1,BASE.top],\r\n                    [state.x2,BASE.top],\r\n                    [state.x2,state.y1],\r\n                    [state.x1,state.y1]\r\n                ];\r\n            }\r\n        },\r\n        {\r\n            name:'BA\u00d1O',\r\n            type:'bath',\r\n            getPoints(){\r\n                return [\r\n                    [state.x2,BASE.top],\r\n                    [state.x3,BASE.top],\r\n                    [state.x3,state.y1],\r\n                    [state.x2,state.y1]\r\n                ];\r\n            }\r\n        },\r\n        {\r\n            name:'DORMITORIO',\r\n            type:'bedroom',\r\n            getPoints(){\r\n                return [\r\n                    [state.x3,BASE.top],\r\n                    [BASE.right,BASE.top],\r\n                    [BASE.right,state.y1],\r\n                    [state.x3,state.y1]\r\n                ];\r\n            }\r\n        },\r\n        {\r\n            name:'ESTUDIO',\r\n            type:'flex',\r\n            getPoints(){\r\n                return [\r\n                    [BASE.left,state.y1],\r\n                    [state.x2,state.y1],\r\n                    [state.x2,state.y2],\r\n                    [BASE.left,state.y2]\r\n                ];\r\n            }\r\n        },\r\n        {\r\n            name:'SAL\u00d3N',\r\n            type:'living',\r\n            getPoints(){\r\n                return [\r\n                    [state.x2,state.y1],\r\n                    [BASE.right,state.y1],\r\n                    [BASE.right,state.y2],\r\n                    [state.x3,state.y2],\r\n                    [state.x3,590],\r\n                    [state.x2,590]\r\n                ];\r\n            }\r\n        },\r\n        {\r\n            name:'BA\u00d1O',\r\n            type:'bath',\r\n            getPoints(){\r\n                return [\r\n                    [BASE.left,state.y2],\r\n                    [state.x1,state.y2],\r\n                    [state.x1,BASE.bottom],\r\n                    [BASE.left,BASE.bottom]\r\n                ];\r\n            }\r\n        },\r\n        {\r\n            name:'TERRAZA',\r\n            type:'terrace',\r\n            getPoints(){\r\n                return [\r\n                    [state.x1,state.y2],\r\n                    [state.x3,state.y2],\r\n                    [state.x3,BASE.bottom],\r\n                    [state.x1,BASE.bottom]\r\n                ];\r\n            }\r\n        },\r\n        {\r\n            name:'COCINA',\r\n            type:'kitchen',\r\n            getPoints(){\r\n                return [\r\n                    [state.x3,state.y2],\r\n                    [BASE.right,state.y2],\r\n                    [BASE.right,BASE.bottom],\r\n                    [state.x3,BASE.bottom]\r\n                ];\r\n            }\r\n        }\r\n    ];\r\n\r\n    rooms.forEach(room=>{\r\n        const polygon=crearSVG('polygon','elp-room');\r\n        roomsGroup.appendChild(polygon);\r\n        room.element=polygon;\r\n    });\r\n\r\n    \/* ========================================================\r\n       MOBILIARIO\r\n       Cada estancia tiene su propio grupo.\r\n       ======================================================== *\/\r\n\r\n    function crearGrupoMueble(room){\r\n        const g=crearSVG('g','elp-furniture-room');\r\n        furnitureGroup.appendChild(g);\r\n        room.furnitureGroup=g;\r\n        return g;\r\n    }\r\n\r\n    function rectFurniture(g,cls='elp-furniture'){\r\n        const r=crearSVG('rect',cls);\r\n        g.appendChild(r);\r\n        return r;\r\n    }\r\n\r\n    function lineFurniture(g,cls='elp-furniture-detail'){\r\n        const l=crearSVG('line',cls);\r\n        g.appendChild(l);\r\n        return l;\r\n    }\r\n\r\n    function ellipseFurniture(g,cls='elp-furniture'){\r\n        const e=crearSVG('ellipse',cls);\r\n        g.appendChild(e);\r\n        return e;\r\n    }\r\n\r\n    function circleFurniture(g,cls='elp-furniture'){\r\n        const c=crearSVG('circle',cls);\r\n        g.appendChild(c);\r\n        return c;\r\n    }\r\n\r\n    function pathFurniture(g,cls='elp-furniture-detail'){\r\n        const p=crearSVG('path',cls);\r\n        g.appendChild(p);\r\n        return p;\r\n    }\r\n\r\n    function polylineFurniture(g,cls='elp-furniture-detail'){\r\n        const p=crearSVG('polyline',cls);\r\n        g.appendChild(p);\r\n        return p;\r\n    }\r\n\r\n    function setCircle(el,cx,cy,r){\r\n        el.setAttribute('cx',cx);\r\n        el.setAttribute('cy',cy);\r\n        el.setAttribute('r',r);\r\n    }\r\n\r\n    function setEllipse(el,cx,cy,rx,ry){\r\n        el.setAttribute('cx',cx);\r\n        el.setAttribute('cy',cy);\r\n        el.setAttribute('rx',rx);\r\n        el.setAttribute('ry',ry);\r\n    }\r\n\r\n    rooms.forEach(room=>{\r\n        const g=crearGrupoMueble(room);\r\n\r\n        if(room.type==='bedroom'){\r\n            room.furniture={\r\n                bed:rectFurniture(g,'elp-furniture-fill'),\r\n                headboard:lineFurniture(g,'elp-furniture-accent'),\r\n                pillow1:rectFurniture(g,'elp-furniture'),\r\n                pillow2:rectFurniture(g,'elp-furniture'),\r\n                blanket:polylineFurniture(g,'elp-furniture-detail'),\r\n                duvet:lineFurniture(g,'elp-furniture-detail'),\r\n\r\n                night1:rectFurniture(g,'elp-furniture'),\r\n                night2:rectFurniture(g,'elp-furniture'),\r\n                lamp1:circleFurniture(g,'elp-furniture-accent'),\r\n                lamp2:circleFurniture(g,'elp-furniture-accent'),\r\n\r\n                closet:rectFurniture(g,'elp-furniture'),\r\n                closetLine1:lineFurniture(g,'elp-furniture-detail'),\r\n                closetLine2:lineFurniture(g,'elp-furniture-detail'),\r\n\r\n                plantPot:circleFurniture(g,'elp-furniture-detail'),\r\n                plantA:lineFurniture(g,'elp-furniture-detail'),\r\n                plantB:lineFurniture(g,'elp-furniture-detail'),\r\n                plantC:lineFurniture(g,'elp-furniture-detail')\r\n            };\r\n        }\r\n\r\n        if(room.type==='bath'){\r\n            room.furniture={\r\n                shower:rectFurniture(g,'elp-furniture-fill'),\r\n                showerDiag1:lineFurniture(g,'elp-furniture-detail'),\r\n                showerDiag2:lineFurniture(g,'elp-furniture-detail'),\r\n\r\n                wc:ellipseFurniture(g,'elp-furniture'),\r\n                wcInner:ellipseFurniture(g,'elp-furniture-detail'),\r\n                wcTank:rectFurniture(g,'elp-furniture'),\r\n\r\n                vanity:rectFurniture(g,'elp-furniture-fill'),\r\n                sinkBowl:ellipseFurniture(g,'elp-furniture-accent'),\r\n                tap:lineFurniture(g,'elp-furniture-detail'),\r\n\r\n                mirror:rectFurniture(g,'elp-furniture-detail')\r\n            };\r\n        }\r\n\r\n        if(room.type==='study'){\r\n            room.furniture={\r\n                desk:rectFurniture(g,'elp-furniture-fill'),\r\n                chair:circleFurniture(g,'elp-furniture'),\r\n                chairBack:lineFurniture(g,'elp-furniture-detail'),\r\n                shelf:rectFurniture(g,'elp-furniture'),\r\n                shelf1:lineFurniture(g,'elp-furniture-detail'),\r\n                shelf2:lineFurniture(g,'elp-furniture-detail'),\r\n                monitor:rectFurniture(g,'elp-furniture-accent'),\r\n                keyboard:rectFurniture(g,'elp-furniture-detail'),\r\n                rug:rectFurniture(g,'elp-furniture-detail'),\r\n                plantPot:circleFurniture(g,'elp-furniture-detail'),\r\n                plantA:lineFurniture(g,'elp-furniture-detail'),\r\n                plantB:lineFurniture(g,'elp-furniture-detail')\r\n            };\r\n        }\r\n\r\n        if(room.type==='flex'){\r\n            room.furniture={\r\n                \/* modo sal\u00f3n *\/\r\n                sofa:rectFurniture(g,'elp-furniture-fill'),\r\n                sofaSeat:rectFurniture(g,'elp-furniture-detail'),\r\n                coffee:rectFurniture(g,'elp-furniture'),\r\n                tvUnit:rectFurniture(g,'elp-furniture'),\r\n                tv:rectFurniture(g,'elp-furniture-accent'),\r\n                sideTable:rectFurniture(g,'elp-furniture'),\r\n                console:rectFurniture(g,'elp-furniture'),\r\n\r\n                \/* modo estudio *\/\r\n                desk:rectFurniture(g,'elp-furniture-fill'),\r\n                monitor:rectFurniture(g,'elp-furniture-accent'),\r\n                keyboard:rectFurniture(g,'elp-furniture-detail'),\r\n                chair:circleFurniture(g,'elp-furniture'),\r\n                chairBack:lineFurniture(g,'elp-furniture-detail'),\r\n                shelf:rectFurniture(g,'elp-furniture'),\r\n                shelf1:lineFurniture(g,'elp-furniture-detail'),\r\n                shelf2:lineFurniture(g,'elp-furniture-detail')\r\n            };\r\n        }\r\n\r\n        if(room.type==='living'){\r\n            room.furniture={\r\n                sofaA:rectFurniture(g,'elp-furniture-fill'),\r\n                sofaB:rectFurniture(g,'elp-furniture-fill'),\r\n                cushion1:rectFurniture(g,'elp-furniture-detail'),\r\n                cushion2:rectFurniture(g,'elp-furniture-detail'),\r\n                cushion3:rectFurniture(g,'elp-furniture-detail'),\r\n\r\n                table:rectFurniture(g,'elp-furniture'),\r\n                tableInner:rectFurniture(g,'elp-furniture-detail'),\r\n\r\n                tvUnit:rectFurniture(g,'elp-furniture'),\r\n                tv:rectFurniture(g,'elp-furniture-accent'),\r\n\r\n                armchair:rectFurniture(g,'elp-furniture-fill'),\r\n                armchairSeat:rectFurniture(g,'elp-furniture-detail'),\r\n                sideTable:rectFurniture(g,'elp-furniture'),\r\n                console:rectFurniture(g,'elp-furniture'),\r\n\r\n                plantPot:circleFurniture(g,'elp-furniture-detail'),\r\n                plantA:lineFurniture(g,'elp-furniture-detail'),\r\n                plantB:lineFurniture(g,'elp-furniture-detail'),\r\n                plantC:lineFurniture(g,'elp-furniture-detail')\r\n            };\r\n        }\r\n\r\n        if(room.type==='terrace'){\r\n            room.furniture={\r\n                table:ellipseFurniture(g,'elp-furniture'),\r\n                tableInner:ellipseFurniture(g,'elp-furniture-detail'),\r\n\r\n                chair1:circleFurniture(g),\r\n                chair2:circleFurniture(g),\r\n                chair3:circleFurniture(g),\r\n                chair4:circleFurniture(g),\r\n\r\n                planter1:rectFurniture(g,'elp-furniture-fill'),\r\n                planter2:rectFurniture(g,'elp-furniture-fill'),\r\n\r\n                plant1a:lineFurniture(g,'elp-furniture-detail'),\r\n                plant1b:lineFurniture(g,'elp-furniture-detail'),\r\n                plant2a:lineFurniture(g,'elp-furniture-detail'),\r\n                plant2b:lineFurniture(g,'elp-furniture-detail')\r\n            };\r\n        }\r\n\r\n        if(room.type==='kitchen'){\r\n            room.furniture={\r\n                counterTop:rectFurniture(g,'elp-furniture-fill'),\r\n                counterSide:rectFurniture(g,'elp-furniture-fill'),\r\n\r\n                module1:lineFurniture(g,'elp-furniture-detail'),\r\n                module2:lineFurniture(g,'elp-furniture-detail'),\r\n                module3:lineFurniture(g,'elp-furniture-detail'),\r\n\r\n                sink:rectFurniture(g,'elp-furniture-accent'),\r\n                sinkDivider:lineFurniture(g,'elp-furniture-detail'),\r\n                tap:lineFurniture(g,'elp-furniture-detail'),\r\n\r\n                hob:rectFurniture(g,'elp-furniture-accent'),\r\n                burner1:circleFurniture(g,'elp-furniture-detail'),\r\n                burner2:circleFurniture(g,'elp-furniture-detail'),\r\n                burner3:circleFurniture(g,'elp-furniture-detail'),\r\n                burner4:circleFurniture(g,'elp-furniture-detail'),\r\n\r\n                island:rectFurniture(g,'elp-furniture-fill'),\r\n                islandInner:rectFurniture(g,'elp-furniture-detail'),\r\n                stool1:circleFurniture(g,'elp-furniture-detail'),\r\n                stool2:circleFurniture(g,'elp-furniture-detail'),\r\n                stool3:circleFurniture(g,'elp-furniture-detail'),\r\n\r\n                fridge:rectFurniture(g,'elp-furniture'),\r\n                fridgeLine:lineFurniture(g,'elp-furniture-detail')\r\n            };\r\n        }\r\n    });\r\n\r\n    function updateBedroom(room){\r\n        const b=bbox(room.getPoints());\r\n        const f=room.furniture;\r\n        const idx=rooms.indexOf(room);\r\n\r\n        const m=14;\r\n        const doorBand=70;\r\n\r\n        const usableW=b.width-m*2;\r\n        const usableH=b.height-m*2-doorBand;\r\n\r\n        \/*\r\n         * DOBLE por defecto.\r\n         * Individual solo cuando el dormitorio realmente se estrecha.\r\n         *\/\r\n        const single =\r\n            usableW < 152 ||\r\n            usableH < 108;\r\n\r\n        \/*\r\n         * Eliminamos cualquier s\u00edmbolo decorativo.\r\n         *\/\r\n        [\r\n            f.plantPot,\r\n            f.plantA,\r\n            f.plantB,\r\n            f.plantC\r\n        ].forEach(el=>{\r\n            if(el) el.style.opacity='0';\r\n        });\r\n\r\n        \/*\r\n         * PROPORCI\u00d3N REALISTA:\r\n         * la cama SIEMPRE es claramente m\u00e1s larga que ancha.\r\n         *\/\r\n        const doubleW=clamp(usableW*.48,70,96);\r\n        const doubleH=clamp(Math.max(doubleW*1.28, usableH*.72),92,126);\r\n\r\n        const singleW=clamp(usableW*.30,40,54);\r\n        const singleH=clamp(Math.max(singleW*1.65, usableH*.74),80,112);\r\n\r\n        const night=single ? 13 : 17;\r\n        const gap=single ? 5 : 7;\r\n\r\n        \/*\r\n         * HABITACI\u00d3N CENTRAL IZQUIERDA (idx 1):\r\n         * variedad: cama pegada a pared izquierda.\r\n         *\/\r\n        if(idx===1){\r\n\r\n            \/*\r\n             * SEGUNDO DORMITORIO:\r\n             * cama apoyada REALMENTE en el muro inferior.\r\n             * Cabecero abajo y cama orientada hacia arriba.\r\n             *\/\r\n            const bedW = single\r\n                ? clamp(usableW*.34,44,56)\r\n                : clamp(usableW*.50,72,94);\r\n\r\n            const bedH = single\r\n                ? clamp(Math.max(bedW*1.75, usableH*.78),88,118)\r\n                : clamp(Math.max(bedW*1.42, usableH*.82),108,136);\r\n\r\n            const localNight = single ? 13 : 16;\r\n            const localGap = single ? 5 : 7;\r\n\r\n            \/*\r\n             * La puerta est\u00e1 en el extremo inferior derecho.\r\n             * Reservamos ese sector y pegamos la cama al tramo libre\r\n             * del muro inferior.\r\n             *\/\r\n            const doorReserve=62;\r\n            const bedBottom=b.bottom-8;\r\n\r\n            const totalW = single\r\n                ? bedW+localGap+localNight\r\n                : localNight+localGap+bedW+localGap+localNight;\r\n\r\n            const maxStartX=\r\n                b.right-doorReserve-totalW;\r\n\r\n            const startX=clamp(\r\n                b.left+18,\r\n                b.left+14,\r\n                Math.max(b.left+14,maxStartX)\r\n            );\r\n\r\n            const bedX = single\r\n                ? startX\r\n                : startX+localNight+localGap;\r\n\r\n            const bedY=bedBottom-bedH;\r\n\r\n            setRect(f.bed,bedX,bedY,bedW,bedH,3);\r\n\r\n            \/*\r\n             * Cabecero tocando visualmente el muro inferior.\r\n             *\/\r\n            setLine(\r\n                f.headboard,\r\n                bedX,\r\n                bedBottom-2,\r\n                bedX+bedW,\r\n                bedBottom-2\r\n            );\r\n\r\n            if(single){\r\n                f.pillow2.style.opacity='0';\r\n\r\n                setRect(\r\n                    f.pillow1,\r\n                    bedX+bedW*.20,\r\n                    bedBottom-23,\r\n                    bedW*.60,\r\n                    16,\r\n                    2\r\n                );\r\n            }else{\r\n                f.pillow2.style.opacity='1';\r\n\r\n                const pg=5;\r\n                const pw=(bedW-18-pg)\/2;\r\n\r\n                setRect(f.pillow1,bedX+7,bedBottom-24,pw,18,2);\r\n                setRect(f.pillow2,bedX+7+pw+pg,bedBottom-24,pw,18,2);\r\n            }\r\n\r\n            \/*\r\n             * Manta simple.\r\n             *\/\r\n            setLine(\r\n                f.duvet,\r\n                bedX+6,\r\n                bedY+bedH*.43,\r\n                bedX+bedW-6,\r\n                bedY+bedH*.43\r\n            );\r\n\r\n            f.blanket.setAttribute(\r\n                'points',\r\n                [\r\n                    [bedX+6,bedY+bedH*.31],\r\n                    [bedX+bedW-6,bedY+bedH*.31]\r\n                ].map(p=>p.join(',')).join(' ')\r\n            );\r\n\r\n            \/*\r\n             * Mesillas tambi\u00e9n contra el muro inferior.\r\n             *\/\r\n            if(single){\r\n                f.night1.style.opacity='0';\r\n                f.lamp1.style.opacity='0';\r\n\r\n                setRect(\r\n                    f.night2,\r\n                    bedX+bedW+localGap,\r\n                    bedBottom-localNight,\r\n                    localNight,\r\n                    localNight,\r\n                    2\r\n                );\r\n\r\n                setCircle(\r\n                    f.lamp2,\r\n                    bedX+bedW+localGap+localNight\/2,\r\n                    bedBottom-localNight\/2,\r\n                    4\r\n                );\r\n            }else{\r\n                f.night1.style.opacity='1';\r\n                f.lamp1.style.opacity='1';\r\n                f.night2.style.opacity='1';\r\n                f.lamp2.style.opacity='1';\r\n\r\n                setRect(\r\n                    f.night1,\r\n                    startX,\r\n                    bedBottom-localNight,\r\n                    localNight,\r\n                    localNight,\r\n                    2\r\n                );\r\n\r\n                setRect(\r\n                    f.night2,\r\n                    bedX+bedW+localGap,\r\n                    bedBottom-localNight,\r\n                    localNight,\r\n                    localNight,\r\n                    2\r\n                );\r\n\r\n                setCircle(\r\n                    f.lamp1,\r\n                    startX+localNight\/2,\r\n                    bedBottom-localNight\/2,\r\n                    4\r\n                );\r\n\r\n                setCircle(\r\n                    f.lamp2,\r\n                    bedX+bedW+localGap+localNight\/2,\r\n                    bedBottom-localNight\/2,\r\n                    4\r\n                );\r\n            }\r\n\r\n            \/*\r\n             * Armario arriba a la derecha para no competir con la cama.\r\n             *\/\r\n            const closetW=clamp(usableW*.27,34,48);\r\n            const closetH=17;\r\n            const closetX=b.right-m-closetW;\r\n            const closetY=b.top+m;\r\n\r\n            setRect(f.closet,closetX,closetY,closetW,closetH,1);\r\n            setLine(f.closetLine1,closetX+closetW*.33,closetY,closetX+closetW*.33,closetY+closetH);\r\n            setLine(f.closetLine2,closetX+closetW*.66,closetY,closetX+closetW*.66,closetY+closetH);\r\n\r\n            return;\r\n        }\r\n\r\n        \/*\r\n         * RESTO DE DORMITORIOS:\r\n         * cama con cabecero contra pared superior.\r\n         * Incluso cuando es peque\u00f1a sigue siendo VERTICAL Y LARGA.\r\n         *\/\r\n        const bedW=single ? singleW : doubleW;\r\n        const bedH=single ? singleH : doubleH;\r\n\r\n        const total=\r\n            single\r\n            ? bedW+gap+night\r\n            : night+gap+bedW+gap+night;\r\n\r\n        const shift=idx===3 ? 7 : 0;\r\n\r\n        const startX=\r\n            b.left+\r\n            (b.width-total)\/2+\r\n            shift;\r\n\r\n        const bedX=\r\n            single\r\n            ? startX\r\n            : startX+night+gap;\r\n\r\n        const bedY=b.top+m;\r\n\r\n        setRect(f.bed,bedX,bedY,bedW,bedH,3);\r\n        setLine(f.headboard,bedX,bedY+2,bedX+bedW,bedY+2);\r\n\r\n        if(single){\r\n            f.pillow2.style.opacity='0';\r\n\r\n            setRect(\r\n                f.pillow1,\r\n                bedX+bedW*.20,\r\n                bedY+7,\r\n                bedW*.60,\r\n                16,\r\n                2\r\n            );\r\n        }else{\r\n            f.pillow2.style.opacity='1';\r\n\r\n            const pg=5;\r\n            const pw=(bedW-18-pg)\/2;\r\n\r\n            setRect(f.pillow1,bedX+7,bedY+7,pw,18,2);\r\n            setRect(f.pillow2,bedX+7+pw+pg,bedY+7,pw,18,2);\r\n        }\r\n\r\n        setLine(\r\n            f.duvet,\r\n            bedX+6,\r\n            bedY+bedH*.60,\r\n            bedX+bedW-6,\r\n            bedY+bedH*.60\r\n        );\r\n\r\n        \/*\r\n         * Quitamos por completo el zigzag de manta.\r\n         *\/\r\n        f.blanket.setAttribute(\r\n            'points',\r\n            [\r\n                [bedX+6,bedY+bedH*.70],\r\n                [bedX+bedW-6,bedY+bedH*.70]\r\n            ].map(p=>p.join(',')).join(' ')\r\n        );\r\n\r\n        if(single){\r\n            f.night1.style.opacity='0';\r\n            f.lamp1.style.opacity='0';\r\n\r\n            setRect(\r\n                f.night2,\r\n                bedX+bedW+gap,\r\n                bedY+3,\r\n                night,\r\n                night,\r\n                2\r\n            );\r\n\r\n            setCircle(\r\n                f.lamp2,\r\n                bedX+bedW+gap+night\/2,\r\n                bedY+3+night\/2,\r\n                4\r\n            );\r\n        }else{\r\n            f.night1.style.opacity='1';\r\n            f.lamp1.style.opacity='1';\r\n\r\n            setRect(f.night1,startX,bedY+3,night,night,2);\r\n            setRect(f.night2,bedX+bedW+gap,bedY+3,night,night,2);\r\n\r\n            setCircle(f.lamp1,startX+night\/2,bedY+3+night\/2,4);\r\n            setCircle(f.lamp2,bedX+bedW+gap+night\/2,bedY+3+night\/2,4);\r\n        }\r\n\r\n        \/*\r\n         * Armario contra pared lateral.\r\n         * Dormitorio derecho usa pared izquierda para dar variedad.\r\n         *\/\r\n        const closetW=18;\r\n        const closetH=single ? 34 : 44;\r\n\r\n        const closetX=\r\n            idx===3\r\n            ? b.left+m\r\n            : b.right-m-closetW;\r\n\r\n        const preferredY=b.top+m+bedH+10;\r\n\r\n        const canPlaceBelow=\r\n            preferredY+closetH <=\r\n            b.bottom-doorBand-8;\r\n\r\n        const closetY=\r\n            canPlaceBelow\r\n            ? preferredY\r\n            : b.top+m;\r\n\r\n        setRect(f.closet,closetX,closetY,closetW,closetH,1);\r\n        setLine(f.closetLine1,closetX+6,closetY,closetX+6,closetY+closetH);\r\n        setLine(f.closetLine2,closetX+12,closetY,closetX+12,closetY+closetH);\r\n    }\r\n\r\n    function updateBath(room){\r\n        const b=bbox(room.getPoints());\r\n        const f=room.furniture;\r\n\r\n        const m=12;\r\n        const isUpper=(room===rooms[2]);\r\n\r\n        const doorBand=70;\r\n\r\n        const safeTop=\r\n            isUpper\r\n            ? b.top+m\r\n            : b.top+m+doorBand;\r\n\r\n        const safeBottom=\r\n            isUpper\r\n            ? b.bottom-m-doorBand\r\n            : b.bottom-m;\r\n\r\n        const safeW=b.width-m*2;\r\n        const safeH=safeBottom-safeTop;\r\n\r\n        \/*\r\n         * BA\u00d1ERA por defecto.\r\n         * Solo pasa a ducha si el ba\u00f1o se estrecha.\r\n         *\/\r\n        const useShower=\r\n            safeW < 170 ||\r\n            safeH < 92;\r\n\r\n        let wetX=b.left+m;\r\n        let wetY;\r\n        let wetW;\r\n        let wetH;\r\n\r\n        if(useShower){\r\n\r\n            wetW=clamp(Math.min(safeW,safeH)*.30,36,48);\r\n            wetH=wetW;\r\n\r\n            wetY=\r\n                isUpper\r\n                ? safeTop\r\n                : safeBottom-wetH;\r\n\r\n            setRect(\r\n                f.shower,\r\n                wetX,\r\n                wetY,\r\n                wetW,\r\n                wetH,\r\n                2\r\n            );\r\n\r\n            setLine(\r\n                f.showerDiag1,\r\n                wetX+4,\r\n                wetY+4,\r\n                wetX+wetW-4,\r\n                wetY+wetH-4\r\n            );\r\n\r\n            setLine(\r\n                f.showerDiag2,\r\n                wetX+wetW-4,\r\n                wetY+4,\r\n                wetX+4,\r\n                wetY+wetH-4\r\n            );\r\n\r\n        }else{\r\n\r\n            wetW=clamp(safeW*.46,82,108);\r\n            wetH=42;\r\n\r\n            wetY=\r\n                isUpper\r\n                ? safeTop\r\n                : safeBottom-wetH;\r\n\r\n            setRect(\r\n                f.shower,\r\n                wetX,\r\n                wetY,\r\n                wetW,\r\n                wetH,\r\n                9\r\n            );\r\n\r\n            \/*\r\n             * Ba\u00f1era limpia, sin cruces.\r\n             *\/\r\n            setLine(\r\n                f.showerDiag1,\r\n                wetX+10,\r\n                wetY+wetH*.50,\r\n                wetX+wetW-10,\r\n                wetY+wetH*.50\r\n            );\r\n\r\n            setLine(\r\n                f.showerDiag2,\r\n                wetX+wetW-15,\r\n                wetY+7,\r\n                wetX+wetW-15,\r\n                wetY+wetH-7\r\n            );\r\n        }\r\n\r\n        \/*\r\n         * LAVABO SIEMPRE AL LADO del elemento h\u00famedo.\r\n         * Nunca encima.\r\n         *\/\r\n        const gap=12;\r\n        const vanityW=clamp(safeW*.24,32,44);\r\n        const vanityH=24;\r\n\r\n        const vanityX=wetX+wetW+gap;\r\n\r\n        const vanityY=\r\n            isUpper\r\n            ? wetY\r\n            : wetY+wetH-vanityH;\r\n\r\n        if(\r\n            vanityX+vanityW <=\r\n            b.right-m-48\r\n        ){\r\n            setRect(\r\n                f.vanity,\r\n                vanityX,\r\n                vanityY,\r\n                vanityW,\r\n                vanityH,\r\n                2\r\n            );\r\n\r\n            setEllipse(\r\n                f.sinkBowl,\r\n                vanityX+vanityW\/2,\r\n                vanityY+vanityH\/2,\r\n                vanityW*.25,\r\n                6\r\n            );\r\n\r\n            setLine(\r\n                f.tap,\r\n                vanityX+vanityW\/2,\r\n                vanityY+4,\r\n                vanityX+vanityW\/2,\r\n                vanityY+9\r\n            );\r\n\r\n            \/*\r\n             * Espejo oculto para evitar rect\u00e1ngulos atravesando muros.\r\n             *\/\r\n            f.mirror.style.opacity='0';\r\n\r\n        }else{\r\n\r\n            \/*\r\n             * Si no cabe al lado, lavabo en pared izquierda\r\n             * pero separado verticalmente del plato\/ba\u00f1era.\r\n             *\/\r\n            const vW=24;\r\n            const vH=36;\r\n\r\n            const vX=b.left+m;\r\n            const vY=\r\n                isUpper\r\n                ? wetY+wetH+12\r\n                : wetY-vH-12;\r\n\r\n            setRect(f.vanity,vX,vY,vW,vH,2);\r\n\r\n            setEllipse(\r\n                f.sinkBowl,\r\n                vX+vW\/2,\r\n                vY+vH\/2,\r\n                6.5,\r\n                9\r\n            );\r\n\r\n            setLine(\r\n                f.tap,\r\n                vX+3,\r\n                vY+vH\/2,\r\n                vX+9,\r\n                vY+vH\/2\r\n            );\r\n\r\n            f.mirror.style.opacity='0';\r\n        }\r\n\r\n        \/*\r\n         * WC pegado a pared derecha.\r\n         *\/\r\n        const wcY=\r\n            safeTop+\r\n            safeH*.56;\r\n\r\n        const tankW=13;\r\n        const tankH=27;\r\n        const tankX=b.right-m-tankW;\r\n        const tankY=wcY-tankH\/2;\r\n\r\n        setRect(f.wcTank,tankX,tankY,tankW,tankH,2);\r\n\r\n        setEllipse(\r\n            f.wc,\r\n            tankX-18,\r\n            wcY,\r\n            19,\r\n            12\r\n        );\r\n\r\n        setEllipse(\r\n            f.wcInner,\r\n            tankX-18,\r\n            wcY,\r\n            11,\r\n            7\r\n        );\r\n    }\r\n\r\n    function updateStudy(room){\r\n        const b=bbox(room.getPoints());\r\n        const f=room.furniture;\r\n        const m=18;\r\n\r\n        \/*\r\n         * Puerta en pared derecha: dejamos esa zona libre.\r\n         * Escritorio siempre pegado a pared superior o izquierda.\r\n         *\/\r\n        const safeRight=b.right-84;\r\n        const availableW=safeRight-(b.left+m);\r\n\r\n        if(availableW>=135){\r\n\r\n            const deskW=clamp(availableW*.58,100,170);\r\n            const x=b.left+m;\r\n            const y=b.top+m;\r\n\r\n            setRect(f.desk,x,y,deskW,30,2);\r\n            setRect(f.monitor,x+deskW*.34,y+5,deskW*.30,8,1);\r\n            setRect(f.keyboard,x+deskW*.38,y+17,deskW*.24,5,1);\r\n\r\n            setCircle(f.chair,x+deskW*.5,y+55,13);\r\n            setLine(f.chairBack,x+deskW*.5-10,y+69,x+deskW*.5+10,y+69);\r\n\r\n            \/*\r\n             * Estanter\u00eda pegada a pared inferior.\r\n             *\/\r\n            const shelfW=clamp(availableW*.46,68,112);\r\n            const shelfX=b.left+m;\r\n            const shelfY=b.bottom-m-22;\r\n\r\n            setRect(f.shelf,shelfX,shelfY,shelfW,22,1);\r\n            setLine(f.shelf1,shelfX+shelfW*.33,shelfY,shelfX+shelfW*.33,shelfY+22);\r\n            setLine(f.shelf2,shelfX+shelfW*.66,shelfY,shelfX+shelfW*.66,shelfY+22);\r\n\r\n            setRect(f.rug,x+18,y+78,clamp(availableW*.50,75,125),46,4);\r\n\r\n        }else{\r\n\r\n            \/*\r\n             * Estudio estrecho: escritorio pegado a pared izquierda.\r\n             *\/\r\n            const deskH=clamp((b.height-m*2)*.48,76,108);\r\n            const x=b.left+m;\r\n            const y=b.top+m;\r\n\r\n            setRect(f.desk,x,y,30,deskH,2);\r\n            setRect(f.monitor,x+5,y+deskH*.35,8,deskH*.28,1);\r\n            setRect(f.keyboard,x+17,y+deskH*.39,5,deskH*.20,1);\r\n\r\n            setCircle(f.chair,x+54,y+deskH*.5,13);\r\n            setLine(f.chairBack,x+66,y+deskH*.5-10,x+66,y+deskH*.5+10);\r\n\r\n            const shelfW=clamp(b.width*.42,62,96);\r\n            const shelfY=b.bottom-m-22;\r\n\r\n            setRect(f.shelf,b.left+m,shelfY,shelfW,22,1);\r\n            setLine(f.shelf1,b.left+m+shelfW*.33,shelfY,b.left+m+shelfW*.33,shelfY+22);\r\n            setLine(f.shelf2,b.left+m+shelfW*.66,shelfY,b.left+m+shelfW*.66,shelfY+22);\r\n\r\n            setRect(f.rug,x+66,y+12,42,clamp(deskH*.80,58,84),4);\r\n        }\r\n\r\n        \/*\r\n         * Planta contra esquina inferior izquierda.\r\n         *\/\r\n        const px=b.left+m+10;\r\n        const py=b.bottom-m-34;\r\n\r\n        setCircle(f.plantPot,px,py,5);\r\n        setLine(f.plantA,px,py-3,px-6,py-12);\r\n        setLine(f.plantB,px,py-3,px+6,py-12);\r\n    }\r\n\r\n\r\n    function updateFlexRoom(room){\r\n        const b=bbox(room.getPoints());\r\n        const f=room.furniture;\r\n\r\n        const m=18;\r\n        const doorStrip=92;\r\n\r\n        const safeRight=b.right-doorStrip;\r\n        const usableW=safeRight-(b.left+m);\r\n        const usableH=b.height-m*2;\r\n\r\n        const small=\r\n            usableW < 225 ||\r\n            usableH < 142;\r\n\r\n        if(small){\r\n\r\n            [\r\n                f.sofa,\r\n                f.sofaSeat,\r\n                f.coffee,\r\n                f.tvUnit,\r\n                f.tv,\r\n                f.sideTable,\r\n                f.console\r\n            ].forEach(el=>el.style.opacity='0');\r\n\r\n            [\r\n                f.desk,\r\n                f.monitor,\r\n                f.keyboard,\r\n                f.chair,\r\n                f.chairBack,\r\n                f.shelf,\r\n                f.shelf1,\r\n                f.shelf2\r\n            ].forEach(el=>el.style.opacity='1');\r\n\r\n            const deskH=clamp(usableH*.54,74,104);\r\n\r\n            const deskX=b.left+m;\r\n            const deskY=b.top+m;\r\n\r\n            setRect(f.desk,deskX,deskY,28,deskH,2);\r\n            setRect(f.monitor,deskX+5,deskY+deskH*.34,8,deskH*.28,1);\r\n            setRect(f.keyboard,deskX+16,deskY+deskH*.39,5,deskH*.20,1);\r\n\r\n            setCircle(f.chair,deskX+52,deskY+deskH*.5,11);\r\n            setLine(f.chairBack,deskX+63,deskY+deskH*.5-9,deskX+63,deskY+deskH*.5+9);\r\n\r\n            const shelfW=clamp(usableW*.42,60,92);\r\n            const shelfY=b.bottom-m-20;\r\n\r\n            setRect(f.shelf,b.left+m,shelfY,shelfW,20,1);\r\n            setLine(f.shelf1,b.left+m+shelfW*.33,shelfY,b.left+m+shelfW*.33,shelfY+20);\r\n            setLine(f.shelf2,b.left+m+shelfW*.66,shelfY,b.left+m+shelfW*.66,shelfY+20);\r\n\r\n        }else{\r\n\r\n            [\r\n                f.desk,\r\n                f.monitor,\r\n                f.keyboard,\r\n                f.chair,\r\n                f.chairBack,\r\n                f.shelf,\r\n                f.shelf1,\r\n                f.shelf2\r\n            ].forEach(el=>el.style.opacity='0');\r\n\r\n            [\r\n                f.sofa,\r\n                f.sofaSeat,\r\n                f.coffee,\r\n                f.tvUnit,\r\n                f.tv,\r\n                f.sideTable,\r\n                f.console\r\n            ].forEach(el=>el.style.opacity='1');\r\n\r\n            const tvW=18;\r\n            const tvH=clamp(usableH*.46,64,88);\r\n\r\n            const tvX=b.left+m;\r\n            const tvY=b.top+(b.height-tvH)\/2;\r\n\r\n            setRect(f.tvUnit,tvX,tvY,tvW,tvH,1);\r\n            setRect(f.tv,tvX+4,tvY+8,10,tvH-16,1);\r\n\r\n            const sofaW=34;\r\n            const sofaH=clamp(usableH*.55,84,116);\r\n\r\n            const sofaX=safeRight-sofaW-16;\r\n            const sofaY=b.top+(b.height-sofaH)\/2;\r\n\r\n            setRect(f.sofa,sofaX,sofaY,sofaW,sofaH,5);\r\n            setRect(f.sofaSeat,sofaX+7,sofaY+8,sofaW-14,sofaH-16,3);\r\n\r\n            const gapStart=tvX+tvW;\r\n            const gapEnd=sofaX;\r\n\r\n            const coffeeW=26;\r\n            const coffeeH=clamp(usableH*.28,44,62);\r\n\r\n            const coffeeX=\r\n                gapStart+\r\n                (gapEnd-gapStart-coffeeW)\/2;\r\n\r\n            const coffeeY=\r\n                b.top+\r\n                (b.height-coffeeH)\/2;\r\n\r\n            setRect(f.coffee,coffeeX,coffeeY,coffeeW,coffeeH,4);\r\n\r\n            \/*\r\n             * Mueble auxiliar bajo pegado a pared superior.\r\n             *\/\r\n            const consoleW=clamp(usableW*.30,54,82);\r\n            const consoleX=b.left+m+26;\r\n            const consoleY=b.top+m;\r\n\r\n            setRect(\r\n                f.console,\r\n                consoleX,\r\n                consoleY,\r\n                consoleW,\r\n                16,\r\n                1\r\n            );\r\n\r\n            \/*\r\n             * Mesita auxiliar al lado del sof\u00e1.\r\n             *\/\r\n            const side=20;\r\n            const sx=sofaX-side-10;\r\n            const sy=sofaY+sofaH-side;\r\n\r\n            if(\r\n                sx > coffeeX+coffeeW+8\r\n            ){\r\n                setRect(f.sideTable,sx,sy,side,side,4);\r\n                f.sideTable.style.opacity='1';\r\n            }else{\r\n                f.sideTable.style.opacity='0';\r\n            }\r\n        }\r\n    }\r\n\r\n    function updateLiving(room){\r\n        const f=room.furniture;\r\n\r\n        [\r\n            f.plantPot,\r\n            f.plantA,\r\n            f.plantB,\r\n            f.plantC\r\n        ].forEach(el=>{\r\n            if(el) el.style.opacity='0';\r\n        });\r\n\r\n        const margin=18;\r\n        const doorClear=112;\r\n\r\n        const safe = {\r\n            left: state.x2 + doorClear,\r\n            right: BASE.right - margin,\r\n            top: state.y1 + margin,\r\n            bottom: state.y2 - margin\r\n        };\r\n\r\n        safe.width=Math.max(1,safe.right-safe.left);\r\n        safe.height=Math.max(1,safe.bottom-safe.top);\r\n\r\n        function rectBox(x,y,w,h){\r\n            return {\r\n                x,y,w,h,\r\n                right:x+w,\r\n                bottom:y+h\r\n            };\r\n        }\r\n\r\n        function overlaps(a,b,gap=8){\r\n            return !(\r\n                a.right+gap <= b.x ||\r\n                a.x >= b.right+gap ||\r\n                a.bottom+gap <= b.y ||\r\n                a.y >= b.bottom+gap\r\n            );\r\n        }\r\n\r\n        function insideSafe(box){\r\n            return (\r\n                box.x >= safe.left &&\r\n                box.y >= safe.top &&\r\n                box.right <= safe.right &&\r\n                box.bottom <= safe.bottom\r\n            );\r\n        }\r\n\r\n        \/*\r\n         * TV pegada a pared derecha.\r\n         *\/\r\n        const tvUnitW=18;\r\n        const tvUnitH=clamp(safe.height*.48,62,90);\r\n\r\n        const tvX=safe.right-tvUnitW;\r\n        const tvY=safe.top+(safe.height-tvUnitH)\/2-6;\r\n\r\n        const tvBox=rectBox(tvX,tvY,tvUnitW,tvUnitH);\r\n\r\n        setRect(f.tvUnit,tvBox.x,tvBox.y,tvBox.w,tvBox.h,1);\r\n        setRect(f.tv,tvBox.x+4,tvBox.y+8,tvBox.w-8,tvBox.h-16,1);\r\n\r\n        const narrow=\r\n            safe.width < 255 ||\r\n            safe.height < 145;\r\n\r\n        \/*\r\n         * Sof\u00e1 vertical y M\u00c1S CERCA de la TV.\r\n         * El conjunto se concentra en la mitad derecha del sal\u00f3n.\r\n         *\/\r\n        const sofaW=narrow ? 30 : 36;\r\n        const sofaH=clamp(\r\n            safe.height*(narrow ? .46 : .56),\r\n            narrow ? 64 : 80,\r\n            narrow ? 86 : 112\r\n        );\r\n\r\n        const desiredGapToTV = narrow ? 72 : 92;\r\n\r\n        let sofaX=\r\n            tvBox.x -\r\n            desiredGapToTV -\r\n            sofaW;\r\n\r\n        sofaX=clamp(\r\n            sofaX,\r\n            safe.left+18,\r\n            tvBox.x-sofaW-52\r\n        );\r\n\r\n        let sofaY=\r\n            tvBox.y+\r\n            (tvBox.h-sofaH)\/2;\r\n\r\n        sofaY=clamp(\r\n            sofaY,\r\n            safe.top,\r\n            safe.bottom-sofaH\r\n        );\r\n\r\n        const sofaBox=rectBox(\r\n            sofaX,\r\n            sofaY,\r\n            sofaW,\r\n            sofaH\r\n        );\r\n\r\n        setRect(f.sofaA,sofaBox.x,sofaBox.y,sofaBox.w,sofaBox.h,6);\r\n\r\n        \/*\r\n         * Chaise solo en modo amplio y si cabe sin tocar nada.\r\n         *\/\r\n        f.sofaB.style.opacity='0';\r\n\r\n        if(!narrow && safe.width>320){\r\n            const chaiseW=58;\r\n            const chaiseH=29;\r\n\r\n            const chaiseBox=rectBox(\r\n                sofaBox.x,\r\n                sofaBox.bottom-chaiseH,\r\n                chaiseW,\r\n                chaiseH\r\n            );\r\n\r\n            if(\r\n                insideSafe(chaiseBox) &&\r\n                !overlaps(chaiseBox,tvBox,10)\r\n            ){\r\n                f.sofaB.style.opacity='1';\r\n                setRect(f.sofaB,chaiseBox.x,chaiseBox.y,chaiseBox.w,chaiseBox.h,5);\r\n            }\r\n        }\r\n\r\n        \/*\r\n         * Cojines.\r\n         *\/\r\n        const cushionW=18;\r\n        const cushionH=narrow ? 20 : 24;\r\n\r\n        setRect(f.cushion1,sofaBox.x+6,sofaBox.y+7,cushionW,cushionH,3);\r\n        setRect(f.cushion2,sofaBox.x+6,sofaBox.y+sofaBox.h*.43,cushionW,cushionH,3);\r\n        setRect(f.cushion3,sofaBox.x+6,sofaBox.bottom-cushionH-7,cushionW,cushionH,3);\r\n\r\n        \/*\r\n         * Mesa m\u00e1s cerca del sof\u00e1 y de la TV.\r\n         * Sigue siendo vertical y collision-safe.\r\n         *\/\r\n        const leftLimit=sofaBox.right+10;\r\n        const rightLimit=tvBox.x-10;\r\n        const availableGap=rightLimit-leftLimit;\r\n\r\n        let tableW=narrow ? 20 : 26;\r\n        let tableH=clamp(\r\n            safe.height*(narrow ? .25 : .30),\r\n            narrow ? 36 : 42,\r\n            narrow ? 50 : 60\r\n        );\r\n\r\n        if(availableGap < tableW+10){\r\n            tableW=Math.max(14,availableGap-10);\r\n        }\r\n\r\n        let tableX=\r\n            leftLimit+\r\n            Math.max(0,(availableGap-tableW)\/2);\r\n\r\n        let tableY=\r\n            sofaBox.y+\r\n            (sofaBox.h-tableH)\/2;\r\n\r\n        tableY=clamp(\r\n            tableY,\r\n            safe.top,\r\n            safe.bottom-tableH\r\n        );\r\n\r\n        const tableBox=rectBox(\r\n            tableX,\r\n            tableY,\r\n            tableW,\r\n            tableH\r\n        );\r\n\r\n        const tableLegal=\r\n            tableBox.w>=14 &&\r\n            insideSafe(tableBox) &&\r\n            !overlaps(tableBox,sofaBox,5) &&\r\n            !overlaps(tableBox,tvBox,5);\r\n\r\n        if(tableLegal){\r\n            f.table.style.opacity='1';\r\n\r\n            setRect(\r\n                f.table,\r\n                tableBox.x,\r\n                tableBox.y,\r\n                tableBox.w,\r\n                tableBox.h,\r\n                4\r\n            );\r\n\r\n            if(!narrow && tableBox.w>=20){\r\n                f.tableInner.style.opacity='1';\r\n\r\n                setRect(\r\n                    f.tableInner,\r\n                    tableBox.x+4,\r\n                    tableBox.y+5,\r\n                    Math.max(8,tableBox.w-8),\r\n                    Math.max(10,tableBox.h-10),\r\n                    3\r\n                );\r\n            }else{\r\n                f.tableInner.style.opacity='0';\r\n            }\r\n        }else{\r\n            f.table.style.opacity='0';\r\n            f.tableInner.style.opacity='0';\r\n        }\r\n\r\n        \/*\r\n         * Sill\u00f3n solo si queda hueco real.\r\n         * Se intenta colocar cerca del conjunto, no lejos.\r\n         *\/\r\n        f.armchair.style.opacity='0';\r\n        f.armchairSeat.style.opacity='0';\r\n\r\n        if(!narrow){\r\n            const chairSize=30;\r\n\r\n            const candidates=[\r\n                rectBox(\r\n                    sofaBox.x-42,\r\n                    sofaBox.bottom-chairSize,\r\n                    chairSize,\r\n                    chairSize\r\n                ),\r\n                rectBox(\r\n                    sofaBox.x-42,\r\n                    sofaBox.y,\r\n                    chairSize,\r\n                    chairSize\r\n                )\r\n            ];\r\n\r\n            const occupied=[sofaBox,tvBox];\r\n            if(tableLegal) occupied.push(tableBox);\r\n\r\n            const chairBox=\r\n                candidates.find(candidate=>\r\n                    insideSafe(candidate) &&\r\n                    occupied.every(o=>!overlaps(candidate,o,8))\r\n                );\r\n\r\n            if(chairBox){\r\n                f.armchair.style.opacity='1';\r\n                f.armchairSeat.style.opacity='1';\r\n\r\n                setRect(\r\n                    f.armchair,\r\n                    chairBox.x,\r\n                    chairBox.y,\r\n                    chairBox.w,\r\n                    chairBox.h,\r\n                    6\r\n                );\r\n\r\n                setRect(\r\n                    f.armchairSeat,\r\n                    chairBox.x+6,\r\n                    chairBox.y+6,\r\n                    chairBox.w-12,\r\n                    chairBox.h-12,\r\n                    4\r\n                );\r\n            }\r\n        }\r\n\r\n        \/*\r\n         * Mueble auxiliar bajo, pegado a pared superior.\r\n         * Solo aparece si no interfiere con puerta ni sof\u00e1.\r\n         *\/\r\n        f.console.style.opacity='0';\r\n        f.sideTable.style.opacity='0';\r\n\r\n        const consoleW=clamp(safe.width*.22,52,76);\r\n        const consoleH=15;\r\n        const consoleBox=rectBox(\r\n            safe.left+10,\r\n            safe.top,\r\n            consoleW,\r\n            consoleH\r\n        );\r\n\r\n        if(\r\n            insideSafe(consoleBox) &&\r\n            !overlaps(consoleBox,sofaBox,10) &&\r\n            !overlaps(consoleBox,tvBox,10)\r\n        ){\r\n            f.console.style.opacity='1';\r\n            setRect(\r\n                f.console,\r\n                consoleBox.x,\r\n                consoleBox.y,\r\n                consoleBox.w,\r\n                consoleBox.h,\r\n                1\r\n            );\r\n        }\r\n\r\n        \/*\r\n         * Mesita auxiliar junto al sof\u00e1 si queda hueco real.\r\n         *\/\r\n        const sideSize=18;\r\n        const sideCandidate=rectBox(\r\n            sofaBox.x-sideSize-9,\r\n            sofaBox.bottom-sideSize,\r\n            sideSize,\r\n            sideSize\r\n        );\r\n\r\n        const occupiedForSide=[sofaBox,tvBox];\r\n        if(tableLegal) occupiedForSide.push(tableBox);\r\n\r\n        if(\r\n            insideSafe(sideCandidate) &&\r\n            occupiedForSide.every(\r\n                o=>!overlaps(sideCandidate,o,7)\r\n            )\r\n        ){\r\n            f.sideTable.style.opacity='1';\r\n\r\n            setRect(\r\n                f.sideTable,\r\n                sideCandidate.x,\r\n                sideCandidate.y,\r\n                sideCandidate.w,\r\n                sideCandidate.h,\r\n                4\r\n            );\r\n        }\r\n    }\r\n\r\n    function updateTerrace(room){\r\n        const b=bbox(room.getPoints());\r\n        const f=room.furniture;\r\n\r\n        const m=18;\r\n\r\n        \/*\r\n         * Corredor sagrado hacia puerta de cocina.\r\n         *\/\r\n        const rightDoorStrip=94;\r\n\r\n        const safeLeft=b.left+m;\r\n        const safeRight=b.right-rightDoorStrip;\r\n        const safeTop=b.top+m;\r\n        const safeBottom=b.bottom-m;\r\n\r\n        const innerW=safeRight-safeLeft;\r\n        const innerH=safeBottom-safeTop;\r\n\r\n        const cx=safeLeft+innerW\/2;\r\n        const cy=safeTop+innerH\/2;\r\n\r\n        [\r\n            f.planter1,\r\n            f.planter2,\r\n            f.plant1a,\r\n            f.plant1b,\r\n            f.plant2a,\r\n            f.plant2b\r\n        ].forEach(el=>{\r\n            if(el) el.style.opacity='0';\r\n        });\r\n\r\n        const round=\r\n            Math.abs(innerW-innerH) <\r\n            Math.min(innerW,innerH)*.38;\r\n\r\n        let rx;\r\n        let ry;\r\n\r\n        if(round){\r\n            rx=clamp(Math.min(innerW,innerH)*.20,25,38);\r\n            ry=rx;\r\n        }else{\r\n            rx=clamp(innerW*.20,34,52);\r\n            ry=clamp(innerH*.15,21,28);\r\n        }\r\n\r\n        setEllipse(f.table,cx,cy,rx,ry);\r\n        setEllipse(f.tableInner,cx,cy,Math.max(10,rx-6),Math.max(8,ry-5));\r\n\r\n        \/*\r\n         * Sillas algo m\u00e1s separadas.\r\n         *\/\r\n        const chairDX=rx+26;\r\n        const chairDY=ry+26;\r\n\r\n        const chairs=[\r\n            [cx-chairDX,cy],\r\n            [cx+chairDX,cy],\r\n            [cx,cy-chairDY],\r\n            [cx,cy+chairDY]\r\n        ];\r\n\r\n        [f.chair1,f.chair2,f.chair3,f.chair4].forEach((c,i)=>{\r\n            setCircle(c,chairs[i][0],chairs[i][1],9);\r\n        });\r\n    }\r\n\r\n    function updateKitchen(room){\r\n        const b=bbox(room.getPoints());\r\n        const f=room.furniture;\r\n\r\n        const m=12;\r\n        const counter=24;\r\n        const doorStrip=88;\r\n\r\n        const sideX=b.right-m-counter;\r\n\r\n        \/*\r\n         * Dos distribuciones:\r\n         * - normal: L superior + derecha + barra inferior;\r\n         * - estrecha: derecha + inferior, sin barra.\r\n         *\/\r\n        const narrowKitchen=\r\n            b.width < 185 ||\r\n            b.height < 150;\r\n\r\n        \/*\r\n         * Ocultamos siempre las l\u00edneas de m\u00f3dulos.\r\n         *\/\r\n        f.module1.style.opacity='0';\r\n        f.module2.style.opacity='0';\r\n        f.module3.style.opacity='0';\r\n\r\n        if(narrowKitchen){\r\n\r\n            \/*\r\n             * ================================================\r\n             * COCINA ESTRECHA\r\n             * ================================================\r\n             *\r\n             * PARED DERECHA:\r\n             * solo encimera continua.\r\n             *\r\n             * PARED INFERIOR:\r\n             * nueva encimera con fuegos + frigor\u00edfico en extremo.\r\n             *\r\n             * Nada de barra\/taburetes.\r\n             *\/\r\n\r\n            \/*\r\n             * La encimera superior se reutiliza como tramo INFERIOR.\r\n             *\/\r\n            const bottomY=b.bottom-m-counter;\r\n            const bottomX=b.left+m+doorStrip;\r\n            const bottomW=Math.max(\r\n                40,\r\n                sideX-bottomX+counter\r\n            );\r\n\r\n            setRect(\r\n                f.counterTop,\r\n                bottomX,\r\n                bottomY,\r\n                bottomW,\r\n                counter,\r\n                1\r\n            );\r\n\r\n            \/*\r\n             * Pared derecha: encimera limpia.\r\n             *\/\r\n            setRect(\r\n                f.counterSide,\r\n                sideX,\r\n                b.top+m,\r\n                counter,\r\n                Math.max(54,b.height-m*2),\r\n                1\r\n            );\r\n\r\n            \/*\r\n             * Fregadero integrado en pared derecha,\r\n             * zona superior.\r\n             *\/\r\n            const sinkW=14;\r\n            const sinkH=clamp(b.height*.22,30,42);\r\n\r\n            const sinkX=sideX+5;\r\n            const sinkY=b.top+m+18;\r\n\r\n            setRect(\r\n                f.sink,\r\n                sinkX,\r\n                sinkY,\r\n                sinkW,\r\n                sinkH,\r\n                2\r\n            );\r\n\r\n            setLine(\r\n                f.sinkDivider,\r\n                sinkX,\r\n                sinkY+sinkH*.5,\r\n                sinkX+sinkW,\r\n                sinkY+sinkH*.5\r\n            );\r\n\r\n            setLine(\r\n                f.tap,\r\n                sideX+2,\r\n                sinkY+sinkH*.5,\r\n                sideX+7,\r\n                sinkY+sinkH*.5\r\n            );\r\n\r\n            \/*\r\n             * FUEGOS EN EL MURO INFERIOR.\r\n             *\/\r\n            const hobW=34;\r\n            const hobH=18;\r\n\r\n            const hobX=bottomX+12;\r\n            const hobY=bottomY+3;\r\n\r\n            setRect(\r\n                f.hob,\r\n                hobX,\r\n                hobY,\r\n                hobW,\r\n                hobH,\r\n                2\r\n            );\r\n\r\n            const burners=[\r\n                [hobX+8,hobY+5],\r\n                [hobX+18,hobY+5],\r\n                [hobX+28,hobY+5],\r\n                [hobX+13,hobY+13]\r\n            ];\r\n\r\n            [f.burner1,f.burner2,f.burner3,f.burner4].forEach((c,i)=>{\r\n                c.style.opacity='1';\r\n                setCircle(c,burners[i][0],burners[i][1],3.4);\r\n            });\r\n\r\n            \/*\r\n             * Frigor\u00edfico en extremo inferior derecho.\r\n             *\/\r\n            const fridgeW=20;\r\n            const fridgeH=counter;\r\n\r\n            const fridgeX=b.right-m-fridgeW;\r\n            const fridgeY=bottomY;\r\n\r\n            setRect(\r\n                f.fridge,\r\n                fridgeX,\r\n                fridgeY,\r\n                fridgeW,\r\n                fridgeH,\r\n                1\r\n            );\r\n\r\n            setLine(\r\n                f.fridgeLine,\r\n                fridgeX+fridgeW*.5,\r\n                fridgeY,\r\n                fridgeX+fridgeW*.5,\r\n                fridgeY+fridgeH\r\n            );\r\n\r\n            \/*\r\n             * Sin barra ni taburetes en cocina estrecha.\r\n             *\/\r\n            [\r\n                f.island,\r\n                f.islandInner,\r\n                f.stool1,\r\n                f.stool2,\r\n                f.stool3\r\n            ].forEach(el=>{\r\n                el.style.opacity='0';\r\n            });\r\n\r\n        }else{\r\n\r\n            \/*\r\n             * ================================================\r\n             * COCINA NORMAL\r\n             * ================================================\r\n             *\/\r\n            const topX=b.left+m+doorStrip;\r\n            const topY=b.top+m;\r\n\r\n            const topW=Math.max(\r\n                30,\r\n                sideX-topX+counter\r\n            );\r\n\r\n            setRect(\r\n                f.counterTop,\r\n                topX,\r\n                topY,\r\n                topW,\r\n                counter,\r\n                1\r\n            );\r\n\r\n            setRect(\r\n                f.counterSide,\r\n                sideX,\r\n                topY,\r\n                counter,\r\n                Math.max(56,b.height-m*2),\r\n                1\r\n            );\r\n\r\n            \/*\r\n             * Fregadero m\u00e1s claro.\r\n             *\/\r\n            const sinkW=clamp(topW*.32,34,46);\r\n            const sinkH=15;\r\n\r\n            const sinkX=\r\n                topX+\r\n                clamp(\r\n                    topW*.18,\r\n                    8,\r\n                    24\r\n                );\r\n\r\n            setRect(\r\n                f.sink,\r\n                sinkX,\r\n                topY+4,\r\n                sinkW,\r\n                sinkH,\r\n                2\r\n            );\r\n\r\n            setLine(\r\n                f.sinkDivider,\r\n                sinkX+sinkW*.5,\r\n                topY+4,\r\n                sinkX+sinkW*.5,\r\n                topY+4+sinkH\r\n            );\r\n\r\n            setLine(\r\n                f.tap,\r\n                sinkX+sinkW*.5,\r\n                topY+1,\r\n                sinkX+sinkW*.5,\r\n                topY+6\r\n            );\r\n\r\n            \/*\r\n             * Fuegos en tramo derecho.\r\n             *\/\r\n            const hobSize=34;\r\n            const hobX=sideX-(hobSize-counter)\/2;\r\n\r\n            const hobY=clamp(\r\n                b.top+m+82,\r\n                b.top+m+counter+38,\r\n                b.bottom-m-84\r\n            );\r\n\r\n            setRect(\r\n                f.hob,\r\n                hobX,\r\n                hobY,\r\n                hobSize,\r\n                hobSize,\r\n                2\r\n            );\r\n\r\n            const burners=[\r\n                [hobX+9,hobY+9],\r\n                [hobX+25,hobY+9],\r\n                [hobX+9,hobY+25],\r\n                [hobX+25,hobY+25]\r\n            ];\r\n\r\n            [f.burner1,f.burner2,f.burner3,f.burner4].forEach((c,i)=>{\r\n                c.style.opacity='1';\r\n                setCircle(c,burners[i][0],burners[i][1],4.6);\r\n            });\r\n\r\n            \/*\r\n             * Frigor\u00edfico abajo a la derecha.\r\n             *\/\r\n            const fridgeH=40;\r\n            const fridgeY=b.bottom-m-fridgeH;\r\n\r\n            setRect(\r\n                f.fridge,\r\n                sideX,\r\n                fridgeY,\r\n                counter,\r\n                fridgeH,\r\n                1\r\n            );\r\n\r\n            setLine(\r\n                f.fridgeLine,\r\n                sideX,\r\n                fridgeY+fridgeH\/2,\r\n                sideX+counter,\r\n                fridgeY+fridgeH\/2\r\n            );\r\n\r\n            \/*\r\n             * Barra inferior habitual.\r\n             *\/\r\n            const freeLeft=b.left+m+doorStrip+6;\r\n            const freeRight=sideX-12;\r\n            const freeW=freeRight-freeLeft;\r\n\r\n            const barCanFit=\r\n                freeW >= 66 &&\r\n                b.height >= 126;\r\n\r\n            if(barCanFit){\r\n\r\n                f.island.style.opacity='1';\r\n                f.islandInner.style.opacity='0';\r\n\r\n                const barW=clamp(\r\n                    freeW*.78,\r\n                    62,\r\n                    110\r\n                );\r\n\r\n                const barH=20;\r\n\r\n                const barX=\r\n                    freeLeft+\r\n                    (freeW-barW)\/2;\r\n\r\n                const barY=\r\n                    b.bottom-m-barH;\r\n\r\n                setRect(\r\n                    f.island,\r\n                    barX,\r\n                    barY,\r\n                    barW,\r\n                    barH,\r\n                    2\r\n                );\r\n\r\n                const stoolY=barY-13;\r\n\r\n                if(barW>=82){\r\n                    f.stool1.style.opacity='1';\r\n                    f.stool2.style.opacity='1';\r\n                    f.stool3.style.opacity='1';\r\n\r\n                    const xs=[\r\n                        barX+barW*.22,\r\n                        barX+barW*.50,\r\n                        barX+barW*.78\r\n                    ];\r\n\r\n                    [f.stool1,f.stool2,f.stool3].forEach((c,i)=>{\r\n                        setCircle(c,xs[i],stoolY,6);\r\n                    });\r\n                }else{\r\n                    f.stool1.style.opacity='1';\r\n                    f.stool2.style.opacity='1';\r\n                    f.stool3.style.opacity='0';\r\n\r\n                    setCircle(f.stool1,barX+barW*.34,stoolY,6);\r\n                    setCircle(f.stool2,barX+barW*.66,stoolY,6);\r\n                }\r\n\r\n            }else{\r\n\r\n                [\r\n                    f.island,\r\n                    f.islandInner,\r\n                    f.stool1,\r\n                    f.stool2,\r\n                    f.stool3\r\n                ].forEach(el=>{\r\n                    el.style.opacity='0';\r\n                });\r\n            }\r\n        }\r\n    }\r\n\r\n    function updateFurniture(activeRoom){\r\n        rooms.forEach((room,index)=>{\r\n\r\n            room.furnitureGroup.classList.toggle(\r\n                'active',\r\n                index===activeRoom\r\n            );\r\n\r\n            \/*\r\n             * Muy importante:\r\n             * cada frame parte de la geometr\u00eda limpia.\r\n             * Despu\u00e9s se calcula la distribuci\u00f3n y, al final,\r\n             * entra el motor de colisiones.\r\n             *\/\r\n            Object.values(room.furniture).forEach(el=>{\r\n                if(!el) return;\r\n\r\n                el.removeAttribute('transform');\r\n\r\n                \/*\r\n                 * El motor de colisiones puede ocultar decoraci\u00f3n\r\n                 * o un bloque imposible. En el frame siguiente\r\n                 * vuelve a intentarse desde cero.\r\n                 *\/\r\n                el.style.opacity='';\r\n            });\r\n\r\n            if(room.type==='bedroom') updateBedroom(room);\r\n            if(room.type==='bath'){\r\n                updateBath(room);\r\n                if(room.furniture.mirror){\r\n                    room.furniture.mirror.style.opacity='0';\r\n                }\r\n            }\r\n            if(room.type==='study') updateStudy(room);\r\n            if(room.type==='flex') updateFlexRoom(room);\r\n            if(room.type==='living') updateLiving(room);\r\n            if(room.type==='terrace') updateTerrace(room);\r\n            if(room.type==='kitchen') updateKitchen(room);\r\n\r\n            \/*\r\n             * No permitimos s\u00edmbolos decorativos tipo planta\r\n             * en las zonas donde estaban generando c\u00edrculos\/l\u00edneas\r\n             * visualmente confusos.\r\n             *\/\r\n            if(\r\n                room.type==='bedroom' ||\r\n                room.type==='study' ||\r\n                room.type==='flex' ||\r\n                room.type==='living'\r\n            ){\r\n                [\r\n                    room.furniture.plantPot,\r\n                    room.furniture.plantA,\r\n                    room.furniture.plantB,\r\n                    room.furniture.plantC\r\n                ].forEach(el=>{\r\n                    if(el) el.style.opacity='0';\r\n                });\r\n            }\r\n        });\r\n    }\r\n\r\n    \/* ========================================================\r\n       MOTOR DE COLISIONES DEL MOBILIARIO\r\n\r\n       REGLAS ABSOLUTAS:\r\n       1. ning\u00fan mueble puede solaparse con otro;\r\n       2. ninguna pieza puede invadir una puerta;\r\n       3. ninguna pieza puede atravesar una pared;\r\n       4. si la posici\u00f3n original deja de ser v\u00e1lida,\r\n          se busca otra pared \/ esquina \/ zona libre;\r\n       5. si todav\u00eda no cabe, se reduce de forma progresiva;\r\n       6. la decoraci\u00f3n secundaria puede desaparecer antes\r\n          que invadir una zona prohibida.\r\n       ======================================================== *\/\r\n\r\n    const COLLISION = {\r\n        gap:8,\r\n        wallMargin:7,\r\n        doorRadius:48,\r\n        minScaleMajor:.54,\r\n        minScaleMinor:.44\r\n    };\r\n\r\n    function getVisibleElements(list){\r\n        return list.filter(el=>{\r\n            if(!el) return false;\r\n\r\n            const opacity =\r\n                el.style &&\r\n                el.style.opacity;\r\n\r\n            return opacity !== '0';\r\n        });\r\n    }\r\n\r\n    function unionBBox(elements){\r\n        const els=getVisibleElements(elements);\r\n\r\n        if(!els.length) return null;\r\n\r\n        let minX=Infinity;\r\n        let minY=Infinity;\r\n        let maxX=-Infinity;\r\n        let maxY=-Infinity;\r\n\r\n        els.forEach(el=>{\r\n            const b=el.getBBox();\r\n\r\n            minX=Math.min(minX,b.x);\r\n            minY=Math.min(minY,b.y);\r\n            maxX=Math.max(maxX,b.x+b.width);\r\n            maxY=Math.max(maxY,b.y+b.height);\r\n        });\r\n\r\n        if(\r\n            !Number.isFinite(minX) ||\r\n            !Number.isFinite(minY)\r\n        ){\r\n            return null;\r\n        }\r\n\r\n        return {\r\n            x:minX,\r\n            y:minY,\r\n            width:maxX-minX,\r\n            height:maxY-minY,\r\n            right:maxX,\r\n            bottom:maxY,\r\n            cx:(minX+maxX)\/2,\r\n            cy:(minY+maxY)\/2\r\n        };\r\n    }\r\n\r\n    function transformedBox(base,dx,dy,scale){\r\n        const cx=base.cx;\r\n        const cy=base.cy;\r\n\r\n        const x=\r\n            cx+\r\n            (base.x-cx)*scale+\r\n            dx;\r\n\r\n        const y=\r\n            cy+\r\n            (base.y-cy)*scale+\r\n            dy;\r\n\r\n        const width=\r\n            base.width*scale;\r\n\r\n        const height=\r\n            base.height*scale;\r\n\r\n        return {\r\n            x,\r\n            y,\r\n            width,\r\n            height,\r\n            right:x+width,\r\n            bottom:y+height,\r\n            cx:x+width\/2,\r\n            cy:y+height\/2\r\n        };\r\n    }\r\n\r\n    function boxesOverlap(a,b,gap=COLLISION.gap){\r\n        return !(\r\n            a.right+gap <= b.x ||\r\n            a.x >= b.right+gap ||\r\n            a.bottom+gap <= b.y ||\r\n            a.y >= b.bottom+gap\r\n        );\r\n    }\r\n\r\n    function pointInsideRoom(room,x,y){\r\n        return puntoDentro(\r\n            x,\r\n            y,\r\n            room.getPoints()\r\n        );\r\n    }\r\n\r\n    function boxInsideRoom(room,box){\r\n        \/*\r\n         * Probamos cuatro esquinas y centro.\r\n         * Las esquinas se meten unos p\u00edxeles hacia dentro\r\n         * para no penalizar muebles apoyados visualmente en pared.\r\n         *\/\r\n        const m=COLLISION.wallMargin;\r\n\r\n        const x1=box.x+m;\r\n        const x2=box.right-m;\r\n        const y1=box.y+m;\r\n        const y2=box.bottom-m;\r\n\r\n        if(x2<=x1 || y2<=y1){\r\n            return false;\r\n        }\r\n\r\n        const points=[\r\n            [x1,y1],\r\n            [x2,y1],\r\n            [x2,y2],\r\n            [x1,y2],\r\n            [box.cx,box.cy]\r\n        ];\r\n\r\n        return points.every(\r\n            p=>pointInsideRoom(\r\n                room,\r\n                p[0],\r\n                p[1]\r\n            )\r\n        );\r\n    }\r\n\r\n    function doorSafeZones(room){\r\n        const roomBox=bbox(room.getPoints());\r\n\r\n        \/*\r\n         * En vez de codificar cada puerta a mano,\r\n         * usamos las puertas reales del SVG.\r\n         *\r\n         * Se crea un cuadrado de seguridad alrededor\r\n         * de cada bisagra. Si toca esta habitaci\u00f3n,\r\n         * es zona sagrada para su mobiliario.\r\n         *\/\r\n        return doors\r\n            .map(door=>{\r\n                const x=door.getX();\r\n                const y=door.getY();\r\n                const r=COLLISION.doorRadius;\r\n\r\n                return {\r\n                    x:x-r,\r\n                    y:y-r,\r\n                    width:r*2,\r\n                    height:r*2,\r\n                    right:x+r,\r\n                    bottom:y+r,\r\n                    cx:x,\r\n                    cy:y\r\n                };\r\n            })\r\n            .filter(zone=>{\r\n                return !(\r\n                    zone.right < roomBox.left ||\r\n                    zone.x > roomBox.right ||\r\n                    zone.bottom < roomBox.top ||\r\n                    zone.y > roomBox.bottom\r\n                );\r\n            });\r\n    }\r\n\r\n    function clusterDefinitions(room){\r\n        const f=room.furniture;\r\n\r\n        if(room.type==='bedroom'){\r\n            return [\r\n                {\r\n                    id:'bed-system',\r\n                    major:true,\r\n                    elements:[\r\n                        f.bed,\r\n                        f.headboard,\r\n                        f.pillow1,\r\n                        f.pillow2,\r\n                        f.blanket,\r\n                        f.duvet,\r\n                        f.night1,\r\n                        f.night2,\r\n                        f.lamp1,\r\n                        f.lamp2\r\n                    ]\r\n                },\r\n                {\r\n                    id:'closet',\r\n                    major:true,\r\n                    elements:[\r\n                        f.closet,\r\n                        f.closetLine1,\r\n                        f.closetLine2\r\n                    ]\r\n                },\r\n                {\r\n                    id:'plant',\r\n                    major:false,\r\n                    elements:[\r\n                        f.plantPot,\r\n                        f.plantA,\r\n                        f.plantB,\r\n                        f.plantC\r\n                    ]\r\n                }\r\n            ];\r\n        }\r\n\r\n        if(room.type==='bath'){\r\n            return [\r\n                {\r\n                    id:'shower-tub',\r\n                    major:true,\r\n                    elements:[\r\n                        f.shower,\r\n                        f.showerDiag1,\r\n                        f.showerDiag2\r\n                    ]\r\n                },\r\n                {\r\n                    id:'vanity',\r\n                    major:true,\r\n                    elements:[\r\n                        f.vanity,\r\n                        f.sinkBowl,\r\n                        f.tap,\r\n                        f.mirror\r\n                    ]\r\n                },\r\n                {\r\n                    id:'wc',\r\n                    major:true,\r\n                    elements:[\r\n                        f.wc,\r\n                        f.wcInner,\r\n                        f.wcTank\r\n                    ]\r\n                }\r\n            ];\r\n        }\r\n\r\n        if(room.type==='study'){\r\n            return [\r\n                {\r\n                    id:'desk',\r\n                    major:true,\r\n                    elements:[\r\n                        f.desk,\r\n                        f.monitor,\r\n                        f.keyboard,\r\n                        f.chair,\r\n                        f.chairBack\r\n                    ]\r\n                },\r\n                {\r\n                    id:'shelf',\r\n                    major:true,\r\n                    elements:[\r\n                        f.shelf,\r\n                        f.shelf1,\r\n                        f.shelf2\r\n                    ]\r\n                },\r\n                {\r\n                    id:'rug',\r\n                    major:false,\r\n                    elements:[f.rug]\r\n                },\r\n                {\r\n                    id:'plant',\r\n                    major:false,\r\n                    elements:[\r\n                        f.plantPot,\r\n                        f.plantA,\r\n                        f.plantB\r\n                    ]\r\n                }\r\n            ];\r\n        }\r\n\r\n        if(room.type==='living'){\r\n            return [\r\n                {\r\n                    id:'sofa',\r\n                    major:true,\r\n                    elements:[\r\n                        f.sofaA,\r\n                        f.sofaB,\r\n                        f.cushion1,\r\n                        f.cushion2,\r\n                        f.cushion3\r\n                    ]\r\n                },\r\n                {\r\n                    id:'coffee-table',\r\n                    major:true,\r\n                    elements:[\r\n                        f.table,\r\n                        f.tableInner\r\n                    ]\r\n                },\r\n                {\r\n                    id:'tv',\r\n                    major:true,\r\n                    elements:[\r\n                        f.tvUnit,\r\n                        f.tv\r\n                    ]\r\n                },\r\n                {\r\n                    id:'armchair',\r\n                    major:true,\r\n                    elements:[\r\n                        f.armchair,\r\n                        f.armchairSeat\r\n                    ]\r\n                },\r\n                {\r\n                    id:'plant',\r\n                    major:false,\r\n                    elements:[\r\n                        f.plantPot,\r\n                        f.plantA,\r\n                        f.plantB,\r\n                        f.plantC\r\n                    ]\r\n                }\r\n            ];\r\n        }\r\n\r\n        if(room.type==='terrace'){\r\n            return [\r\n                {\r\n                    id:'table-chairs',\r\n                    major:true,\r\n                    elements:[\r\n                        f.table,\r\n                        f.tableInner,\r\n                        f.chair1,\r\n                        f.chair2,\r\n                        f.chair3,\r\n                        f.chair4\r\n                    ]\r\n                },\r\n                {\r\n                    id:'planter-1',\r\n                    major:false,\r\n                    elements:[\r\n                        f.planter1,\r\n                        f.plant1a,\r\n                        f.plant1b\r\n                    ]\r\n                },\r\n                {\r\n                    id:'planter-2',\r\n                    major:false,\r\n                    elements:[\r\n                        f.planter2,\r\n                        f.plant2a,\r\n                        f.plant2b\r\n                    ]\r\n                }\r\n            ];\r\n        }\r\n\r\n        if(room.type==='kitchen'){\r\n            return [\r\n                {\r\n                    id:'top-counter',\r\n                    major:true,\r\n                    fixed:true,\r\n                    elements:[\r\n                        f.counterTop,\r\n                        f.module1,\r\n                        f.module2,\r\n                        f.module3,\r\n                        f.sink,\r\n                        f.sinkDivider,\r\n                        f.tap\r\n                    ]\r\n                },\r\n                {\r\n                    id:'side-counter',\r\n                    major:true,\r\n                    fixed:true,\r\n                    elements:[\r\n                        f.counterSide,\r\n                        f.hob,\r\n                        f.burner1,\r\n                        f.burner2,\r\n                        f.burner3,\r\n                        f.burner4,\r\n                        f.fridge,\r\n                        f.fridgeLine\r\n                    ]\r\n                },\r\n                {\r\n                    id:'island',\r\n                    major:true,\r\n                    elements:[\r\n                        f.island,\r\n                        f.islandInner,\r\n                        f.stool1,\r\n                        f.stool2,\r\n                        f.stool3\r\n                    ]\r\n                }\r\n            ];\r\n        }\r\n\r\n        return [];\r\n    }\r\n\r\n    function candidatePlacements(base,roomBox,scale){\r\n        const scaled=\r\n            transformedBox(\r\n                base,\r\n                0,\r\n                0,\r\n                scale\r\n            );\r\n\r\n        const m=12;\r\n\r\n        const left=\r\n            roomBox.left+m;\r\n\r\n        const right=\r\n            roomBox.right-m-scaled.width;\r\n\r\n        const top=\r\n            roomBox.top+m;\r\n\r\n        const bottom=\r\n            roomBox.bottom-m-scaled.height;\r\n\r\n        const centerX=\r\n            roomBox.left+\r\n            (roomBox.width-scaled.width)\/2;\r\n\r\n        const centerY=\r\n            roomBox.top+\r\n            (roomBox.height-scaled.height)\/2;\r\n\r\n        \/*\r\n         * Primero intentamos conservar la posici\u00f3n.\r\n         * Despu\u00e9s probamos paredes, esquinas y centro.\r\n         *\/\r\n        const targets=[\r\n            [scaled.x,scaled.y],\r\n\r\n            [left,top],\r\n            [centerX,top],\r\n            [right,top],\r\n\r\n            [left,centerY],\r\n            [centerX,centerY],\r\n            [right,centerY],\r\n\r\n            [left,bottom],\r\n            [centerX,bottom],\r\n            [right,bottom]\r\n        ];\r\n\r\n        \/*\r\n         * Tambi\u00e9n hacemos una b\u00fasqueda local progresiva.\r\n         * Sirve para peque\u00f1os choques sin cambiar toda\r\n         * la composici\u00f3n de pared.\r\n         *\/\r\n        const shifts=[\r\n            14,28,42,58,74,92\r\n        ];\r\n\r\n        shifts.forEach(s=>{\r\n            targets.push(\r\n                [scaled.x+s,scaled.y],\r\n                [scaled.x-s,scaled.y],\r\n                [scaled.x,scaled.y+s],\r\n                [scaled.x,scaled.y-s],\r\n                [scaled.x+s,scaled.y+s],\r\n                [scaled.x-s,scaled.y+s],\r\n                [scaled.x+s,scaled.y-s],\r\n                [scaled.x-s,scaled.y-s]\r\n            );\r\n        });\r\n\r\n        \/*\r\n         * Eliminamos candidatos repetidos.\r\n         *\/\r\n        const seen=new Set();\r\n\r\n        return targets\r\n            .map(t=>{\r\n                const key=\r\n                    Math.round(t[0])+\r\n                    ':'+\r\n                    Math.round(t[1]);\r\n\r\n                if(seen.has(key)){\r\n                    return null;\r\n                }\r\n\r\n                seen.add(key);\r\n\r\n                const dx=t[0]-scaled.x;\r\n                const dy=t[1]-scaled.y;\r\n\r\n                return {\r\n                    dx,\r\n                    dy,\r\n                    box:transformedBox(\r\n                        base,\r\n                        dx,\r\n                        dy,\r\n                        scale\r\n                    )\r\n                };\r\n            })\r\n            .filter(Boolean);\r\n    }\r\n\r\n    function validPlacement(\r\n        room,\r\n        candidateBox,\r\n        placedBoxes,\r\n        doorZones\r\n    ){\r\n        if(\r\n            !boxInsideRoom(\r\n                room,\r\n                candidateBox\r\n            )\r\n        ){\r\n            return false;\r\n        }\r\n\r\n        if(\r\n            doorZones.some(\r\n                zone=>\r\n                    boxesOverlap(\r\n                        candidateBox,\r\n                        zone,\r\n                        4\r\n                    )\r\n            )\r\n        ){\r\n            return false;\r\n        }\r\n\r\n        if(\r\n            placedBoxes.some(\r\n                placed=>\r\n                    boxesOverlap(\r\n                        candidateBox,\r\n                        placed,\r\n                        COLLISION.gap\r\n                    )\r\n            )\r\n        ){\r\n            return false;\r\n        }\r\n\r\n        return true;\r\n    }\r\n\r\n    function applyClusterTransform(\r\n        cluster,\r\n        base,\r\n        dx,\r\n        dy,\r\n        scale\r\n    ){\r\n        const els=\r\n            getVisibleElements(\r\n                cluster.elements\r\n            );\r\n\r\n        els.forEach(el=>{\r\n            el.setAttribute(\r\n                'transform',\r\n                [\r\n                    `translate(${dx} ${dy})`,\r\n                    `translate(${base.cx} ${base.cy})`,\r\n                    `scale(${scale})`,\r\n                    `translate(${-base.cx} ${-base.cy})`\r\n                ].join(' ')\r\n            );\r\n        });\r\n    }\r\n\r\n    function hideCluster(cluster){\r\n        cluster.elements.forEach(el=>{\r\n            if(!el) return;\r\n            el.style.opacity='0';\r\n        });\r\n    }\r\n\r\n    function solveRoomCollisions(room){\r\n        const clusters=\r\n            clusterDefinitions(room);\r\n\r\n        if(!clusters.length){\r\n            return;\r\n        }\r\n\r\n        const roomBox=\r\n            bbox(\r\n                room.getPoints()\r\n            );\r\n\r\n        const doorZones=\r\n            doorSafeZones(room);\r\n\r\n        const placedBoxes=[];\r\n\r\n        clusters.forEach(cluster=>{\r\n\r\n            const visible=\r\n                getVisibleElements(\r\n                    cluster.elements\r\n                );\r\n\r\n            if(!visible.length){\r\n                return;\r\n            }\r\n\r\n            const base=\r\n                unionBBox(visible);\r\n\r\n            if(!base){\r\n                return;\r\n            }\r\n\r\n            \/*\r\n             * Los elementos estructurales de cocina ya se\r\n             * generan pegados correctamente a sus paredes.\r\n             * Aun as\u00ed deben respetar puertas y l\u00edmites.\r\n             *\/\r\n            const minScale=\r\n                cluster.major\r\n                ? COLLISION.minScaleMajor\r\n                : COLLISION.minScaleMinor;\r\n\r\n            const scales=\r\n                cluster.fixed\r\n                ? [1,.94,.88,.82,.74,.66,.58]\r\n                : [1,.94,.88,.82,.76,.70,.64,.58,minScale];\r\n\r\n            let solved=null;\r\n\r\n            for(const scale of scales){\r\n\r\n                const candidates=\r\n                    candidatePlacements(\r\n                        base,\r\n                        roomBox,\r\n                        scale\r\n                    );\r\n\r\n                for(const candidate of candidates){\r\n\r\n                    if(\r\n                        validPlacement(\r\n                            room,\r\n                            candidate.box,\r\n                            placedBoxes,\r\n                            doorZones\r\n                        )\r\n                    ){\r\n                        solved={\r\n                            ...candidate,\r\n                            scale\r\n                        };\r\n\r\n                        break;\r\n                    }\r\n                }\r\n\r\n                if(solved){\r\n                    break;\r\n                }\r\n            }\r\n\r\n            if(solved){\r\n\r\n                applyClusterTransform(\r\n                    cluster,\r\n                    base,\r\n                    solved.dx,\r\n                    solved.dy,\r\n                    solved.scale\r\n                );\r\n\r\n                placedBoxes.push(\r\n                    solved.box\r\n                );\r\n\r\n            }else if(!cluster.major){\r\n\r\n                \/*\r\n                 * Decoraci\u00f3n: si no hay un sitio legal,\r\n                 * desaparece. Nunca se permite que invada\r\n                 * puerta, pared o un mueble importante.\r\n                 *\/\r\n                hideCluster(cluster);\r\n\r\n            }else{\r\n\r\n                \/*\r\n                 * \u00daltimo recurso para mobiliario principal:\r\n                 * seguimos reduciendo y buscando. La prioridad\r\n                 * es cumplir las reglas geom\u00e9tricas.\r\n                 *\/\r\n                const emergencyScales=[\r\n                    .50,.46,.42,.38\r\n                ];\r\n\r\n                outer:\r\n                for(const scale of emergencyScales){\r\n\r\n                    const candidates=\r\n                        candidatePlacements(\r\n                            base,\r\n                            roomBox,\r\n                            scale\r\n                        );\r\n\r\n                    for(const candidate of candidates){\r\n\r\n                        if(\r\n                            validPlacement(\r\n                                room,\r\n                                candidate.box,\r\n                                placedBoxes,\r\n                                doorZones\r\n                            )\r\n                        ){\r\n                            solved={\r\n                                ...candidate,\r\n                                scale\r\n                            };\r\n\r\n                            break outer;\r\n                        }\r\n                    }\r\n                }\r\n\r\n                if(solved){\r\n\r\n                    applyClusterTransform(\r\n                        cluster,\r\n                        base,\r\n                        solved.dx,\r\n                        solved.dy,\r\n                        solved.scale\r\n                    );\r\n\r\n                    placedBoxes.push(\r\n                        solved.box\r\n                    );\r\n\r\n                }else{\r\n\r\n                    \/*\r\n                     * Si matem\u00e1ticamente no existe hueco,\r\n                     * ocultamos el bloque antes que romper\r\n                     * cualquiera de las tres reglas sagradas.\r\n                     *\/\r\n                    hideCluster(cluster);\r\n                }\r\n            }\r\n        });\r\n    }\r\n\r\n    function solveAllFurnitureCollisions(){\r\n        rooms.forEach(room=>{\r\n            solveRoomCollisions(room);\r\n        });\r\n    }\r\n\r\n    \/* ========================================================\r\n       MUROS\r\n       ======================================================== *\/\r\n\r\n    const walls=[];\r\n\r\n    function crearMuro(getA,getB,exterior=false){\r\n        const line=crearSVG(\r\n            'line',\r\n            exterior ? 'elp-outer-wall' : 'elp-wall'\r\n        );\r\n\r\n        wallsGroup.appendChild(line);\r\n\r\n        walls.push({\r\n            getA,\r\n            getB,\r\n            exterior,\r\n            element:line\r\n        });\r\n    }\r\n\r\n    \/* Exterior *\/\r\n    crearMuro(()=>[BASE.left,BASE.top],()=>[BASE.right,BASE.top],true);\r\n    crearMuro(()=>[BASE.right,BASE.top],()=>[BASE.right,BASE.bottom],true);\r\n    crearMuro(()=>[BASE.right,BASE.bottom],()=>[state.x3,BASE.bottom],true);\r\n    crearMuro(()=>[state.x3,BASE.bottom],()=>[state.x3,690],true);\r\n    crearMuro(()=>[state.x3,690],()=>[state.x1,690],true);\r\n    crearMuro(()=>[state.x1,690],()=>[state.x1,BASE.bottom],true);\r\n    crearMuro(()=>[state.x1,BASE.bottom],()=>[BASE.left,BASE.bottom],true);\r\n    crearMuro(()=>[BASE.left,BASE.bottom],()=>[BASE.left,BASE.top],true);\r\n\r\n    \/* Verticales superiores *\/\r\n    crearMuro(()=>[state.x1,BASE.top],()=>[state.x1,state.y1]);\r\n    crearMuro(()=>[state.x2,BASE.top],()=>[state.x2,state.y1]);\r\n    crearMuro(()=>[state.x3,BASE.top],()=>[state.x3,state.y1]);\r\n\r\n    \/*\r\n     * MURO HORIZONTAL SUPERIOR CON 4 HUECOS REALES DE PUERTA.\r\n     * Hay 4 estancias arriba: dormitorio, dormitorio, ba\u00f1o,\r\n     * dormitorio derecho.\r\n     *\/\r\n    crearMuro(\r\n        ()=>[BASE.left,state.y1],\r\n        ()=>[state.x1-44,state.y1]\r\n    );\r\n\r\n    crearMuro(\r\n        ()=>[state.x1,state.y1],\r\n        ()=>[state.x2-44,state.y1]\r\n    );\r\n\r\n    crearMuro(\r\n        ()=>[state.x2,state.y1],\r\n        ()=>[state.x3-44,state.y1]\r\n    );\r\n\r\n    \/*\r\n     * Habitaci\u00f3n derecha:\r\n     * hueco propio dentro de SU tramo, no en el ba\u00f1o.\r\n     *\/\r\n    crearMuro(\r\n        ()=>[state.x3,state.y1],\r\n        ()=>[state.x3+24,state.y1]\r\n    );\r\n\r\n    crearMuro(\r\n        ()=>[state.x3+68,state.y1],\r\n        ()=>[BASE.right,state.y1]\r\n    );\r\n\r\n    \/* Central *\/\r\n    crearMuro(()=>[state.x2,state.y1],()=>[state.x2,385]);\r\n    crearMuro(()=>[state.x2,430],()=>[state.x2,state.y2]);\r\n\r\n    \/*\r\n     * Puerta del ba\u00f1o inferior:\r\n     * hueco homog\u00e9neo de 50 px en muro y2.\r\n     *\/\r\n    crearMuro(()=>[BASE.left,state.y2],()=>[285,state.y2]);\r\n    crearMuro(()=>[335,state.y2],()=>[state.x1,state.y2]);\r\n\r\n    crearMuro(()=>[state.x1,state.y2],()=>[state.x1,BASE.bottom]);\r\n\r\n    \/*\r\n     * Puerta de cocina:\r\n     * el tabique vertical x3 SE PARTE para que la puerta tenga\r\n     * un hueco real. Nunca vuelve a pasar una l\u00ednea por encima.\r\n     *\/\r\n    crearMuro(\r\n        ()=>[state.x3,state.y2],\r\n        ()=>[state.x3,state.y2+44]\r\n    );\r\n\r\n    crearMuro(\r\n        ()=>[state.x3,state.y2+92],\r\n        ()=>[state.x3,BASE.bottom]\r\n    );\r\n\r\n    crearMuro(()=>[state.x3,state.y2],()=>[BASE.right,state.y2]);\r\n\r\n    \/* ========================================================\r\n       PUERTAS\r\n       ======================================================== *\/\r\n\r\n    const doors=[];\r\n\r\n   function crearPuerta(\r\n    getX,\r\n    getY,\r\n    rotacion,\r\n    size=40,\r\n    orientacion='arriba-derecha'\r\n){\r\n    const groupDoor=crearSVG('g');\r\n\r\n    const hoja=crearSVG('line','elp-door');\r\n    const arco=crearSVG('path','elp-door');\r\n\r\n    let hojaX=0;\r\n    let hojaY=0;\r\n    let path='';\r\n\r\n    switch(orientacion){\r\n\r\n        case 'arriba-derecha':\r\n            hojaX=0;\r\n            hojaY=-size;\r\n\r\n            path=\r\n                `M 0 ${-size} A ${size} ${size} 0 0 1 ${size} 0`;\r\n        break;\r\n\r\n       case 'arriba-izquierda':\r\n\r\n    \/* Hoja abierta vertical hacia arriba *\/\r\n    hojaX=0;\r\n    hojaY=-size;\r\n\r\n    \/* Barrido desde la izquierda hasta la hoja vertical *\/\r\n    path=\r\n        `M ${-size} 0 A ${size} ${size} 0 0 1 0 ${-size}`;\r\n\r\nbreak;\r\n        case 'abajo-derecha':\r\n            hojaX=0;\r\n            hojaY=size;\r\n\r\n            path=\r\n                `M 0 ${size} A ${size} ${size} 0 0 0 ${size} 0`;\r\n        break;\r\n\r\n        case 'abajo-izquierda':\r\n            hojaX=0;\r\n            hojaY=size;\r\n\r\n            path=\r\n                `M 0 ${size} A ${size} ${size} 0 0 1 ${-size} 0`;\r\n        break;\r\n        \r\n        case 'izquierda-abajo':\r\n\r\n    hojaX=-size;\r\n    hojaY=0;\r\n\r\n    path=\r\n        `M ${-size} 0 A ${size} ${size} 0 0 0 0 ${size}`;\r\n\r\nbreak;\r\n    }\r\n\r\n    hoja.setAttribute('x1',0);\r\n    hoja.setAttribute('y1',0);\r\n    hoja.setAttribute('x2',hojaX);\r\n    hoja.setAttribute('y2',hojaY);\r\n\r\n    arco.setAttribute(\r\n        'd',\r\n        path\r\n    );\r\n\r\n    groupDoor.append(\r\n        hoja,\r\n        arco\r\n    );\r\n\r\n    doorsGroup.appendChild(\r\n        groupDoor\r\n    );\r\n\r\n    doors.push({\r\n        group:groupDoor,\r\n        getX,\r\n        getY,\r\n        rotacion\r\n    });\r\n}\r\n   \r\n    \/* PUERTA 1 \u2014 BISAGRA DERECHA *\/\r\ncrearPuerta(\r\n    ()=>state.x1,\r\n    ()=>state.y1,\r\n    0,\r\n    40,\r\n    'arriba-izquierda'\r\n);\r\n\r\n\r\n\/* PUERTA 2 \u2014 BISAGRA DERECHA *\/\r\ncrearPuerta(\r\n    ()=>state.x2,\r\n    ()=>state.y1,\r\n    0,\r\n    40,\r\n    'arriba-izquierda'\r\n);\r\n\r\n\r\n\/* PUERTA 3 \u2014 BISAGRA DERECHA *\/\r\ncrearPuerta(\r\n    ()=>state.x3,\r\n    ()=>state.y1,\r\n    0,\r\n    40,\r\n    'arriba-izquierda'\r\n);\r\n\r\n\r\n\/* PUERTA 4 \u2014 BISAGRA IZQUIERDA *\/\r\ncrearPuerta(\r\n    ()=>state.x3+28,\r\n    ()=>state.y1,\r\n    0,\r\n    40,\r\n    'arriba-derecha'\r\n);\r\n\r\n    crearPuerta(\r\n    ()=>state.x2,\r\n    ()=>385,\r\n    0,\r\n    40,\r\n    'izquierda-abajo'\r\n);\r\n\r\n    crearPuerta(\r\n    ()=>330,\r\n    ()=>state.y2,\r\n    0,\r\n    40,\r\n    'abajo-izquierda'\r\n);\r\n\r\n    crearPuerta(\r\n    ()=>state.x3,\r\n    ()=>state.y2+48,\r\n    90,\r\n    -40,\r\n    'abajo-izquierda'\r\n);\r\n\r\n    \/* ========================================================\r\n       VENTANAS\r\n       ======================================================== *\/\r\n\r\n    const windows=[];\r\n\r\n    function crearVentana(getX1,getY1,getX2,getY2){\r\n        const line1=crearSVG('line','elp-window');\r\n        const line2=crearSVG('line','elp-window');\r\n\r\n        windowsGroup.append(line1,line2);\r\n\r\n        windows.push({\r\n            line1,\r\n            line2,\r\n            getX1,\r\n            getY1,\r\n            getX2,\r\n            getY2\r\n        });\r\n    }\r\n\r\n    crearVentana(()=>215,()=>BASE.top+3,()=>310,()=>BASE.top+3);\r\n    crearVentana(()=>405,()=>BASE.top+3,()=>500,()=>BASE.top+3);\r\n    crearVentana(()=>790,()=>BASE.top+3,()=>920,()=>BASE.top+3);\r\n    crearVentana(()=>BASE.left+3,()=>330,()=>BASE.left+3,()=>420);\r\n    crearVentana(()=>BASE.right-3,()=>330,()=>BASE.right-3,()=>430);\r\n    crearVentana(()=>BASE.right-3,()=>525,()=>BASE.right-3,()=>605);\r\n\r\n    \/* ========================================================\r\n       NODOS\r\n       ======================================================== *\/\r\n\r\n    const nodeElements=[];\r\n\r\n    for(let i=0;i<18;i++){\r\n        const circle=crearSVG('circle','elp-node');\r\n        circle.setAttribute('r',2);\r\n        nodesGroup.appendChild(circle);\r\n        nodeElements.push(circle);\r\n    }\r\n\r\n    function nodePositions(){\r\n        return [\r\n            [BASE.left,BASE.top],\r\n            [state.x1,BASE.top],\r\n            [state.x2,BASE.top],\r\n            [state.x3,BASE.top],\r\n            [BASE.right,BASE.top],\r\n\r\n            [BASE.left,state.y1],\r\n            [state.x1,state.y1],\r\n            [state.x2,state.y1],\r\n            [state.x3,state.y1],\r\n            [BASE.right,state.y1],\r\n\r\n            [BASE.left,state.y2],\r\n            [state.x1,state.y2],\r\n            [state.x2,state.y2],\r\n            [state.x3,state.y2],\r\n            [BASE.right,state.y2],\r\n\r\n            [BASE.left,BASE.bottom],\r\n            [state.x1,BASE.bottom],\r\n            [BASE.right,BASE.bottom]\r\n        ];\r\n    }\r\n\r\n    \/* ========================================================\r\n       CURSOR LOCAL\r\n       ======================================================== *\/\r\n\r\n    const pointer={\r\n        x:-1000,\r\n        y:-1000,\r\n        targetX:-1000,\r\n        targetY:-1000,\r\n        active:false\r\n    };\r\n\r\n    function convertirCursor(clientX,clientY){\r\n        const point=svg.createSVGPoint();\r\n        point.x=clientX;\r\n        point.y=clientY;\r\n\r\n        const matrix=group.getScreenCTM();\r\n        if(!matrix) return;\r\n\r\n        const local=point.matrixTransform(matrix.inverse());\r\n\r\n        pointer.targetX=local.x;\r\n        pointer.targetY=local.y;\r\n\r\n        pointer.active =\r\n            local.x>BASE.left-120 &&\r\n            local.x<BASE.right+120 &&\r\n            local.y>BASE.top-120 &&\r\n            local.y<BASE.bottom+150;\r\n    }\r\n\r\n    function getActiveRoom(){\r\n        if(!pointer.active) return -1;\r\n\r\n        for(let i=0;i<rooms.length;i++){\r\n            if(\r\n                puntoDentro(\r\n                    pointer.x,\r\n                    pointer.y,\r\n                    rooms[i].getPoints()\r\n                )\r\n            ){\r\n                return i;\r\n            }\r\n        }\r\n\r\n        return -1;\r\n    }\r\n\r\n    \/* ========================================================\r\n       EXPANSI\u00d3N DE ESTANCIAS\r\n       ======================================================== *\/\r\n\r\n    function applyRoomExpansion(activeRoom){\r\n        if(activeRoom===-1) return;\r\n\r\n        const amount=CONFIG.roomExpansion;\r\n\r\n        \/*\r\n         * Ahora la habitaci\u00f3n activa cambia de tama\u00f1o de forma\r\n         * claramente perceptible. Las adyacentes ceden espacio.\r\n         *\/\r\n        switch(activeRoom){\r\n\r\n            \/* dormitorio superior izquierdo *\/\r\n            case 0:\r\n                state.targetX1 += amount*.95;\r\n                state.targetY1 += amount*.42;\r\n            break;\r\n\r\n            \/* dormitorio superior central *\/\r\n            case 1:\r\n                state.targetX1 -= amount*.55;\r\n                state.targetX2 += amount*.62;\r\n                state.targetY1 += amount*.42;\r\n            break;\r\n\r\n            \/* ba\u00f1o superior *\/\r\n            case 2:\r\n                state.targetX2 -= amount*.52;\r\n                state.targetX3 += amount*.54;\r\n                state.targetY1 += amount*.38;\r\n            break;\r\n\r\n            \/* dormitorio superior derecho *\/\r\n            case 3:\r\n                state.targetX3 -= amount*.92;\r\n                state.targetY1 += amount*.42;\r\n            break;\r\n\r\n            \/* estudio *\/\r\n            case 4:\r\n                state.targetX2 += amount*.66;\r\n                state.targetY1 -= amount*.52;\r\n                state.targetY2 += amount*.52;\r\n            break;\r\n\r\n            \/* sal\u00f3n *\/\r\n            case 5:\r\n                state.targetX2 -= amount*.62;\r\n                state.targetX3 += amount*.48;\r\n                state.targetY1 -= amount*.58;\r\n                state.targetY2 += amount*.52;\r\n            break;\r\n\r\n            \/* ba\u00f1o inferior *\/\r\n            case 6:\r\n                state.targetX1 += amount*.78;\r\n                state.targetY2 -= amount*.58;\r\n            break;\r\n\r\n            \/* terraza \/ comedor *\/\r\n            case 7:\r\n                state.targetX1 -= amount*.46;\r\n                state.targetX3 += amount*.46;\r\n                state.targetY2 -= amount*.62;\r\n            break;\r\n\r\n            \/* cocina *\/\r\n            case 8:\r\n                state.targetX3 -= amount*.78;\r\n                state.targetY2 -= amount*.62;\r\n            break;\r\n        }\r\n    }\r\n\r\n    \/* ========================================================\r\n       REPULSI\u00d3N DE MUROS\r\n       ======================================================== *\/\r\n\r\n    function applyWallRepulsion(){\r\n        if(!pointer.active) return;\r\n\r\n        const props=[\r\n            ['x1','targetX1','x',CONFIG.wallMovement],\r\n            ['x2','targetX2','x',CONFIG.wallMovement],\r\n            ['x3','targetX3','x',CONFIG.wallMovement],\r\n            ['y1','targetY1','y',CONFIG.wallMovement],\r\n            ['y2','targetY2','y',CONFIG.wallMovement]\r\n        ];\r\n\r\n        props.forEach(([key,targetKey,axis,movement])=>{\r\n            const pos=state[key];\r\n            const mouse=axis==='x' ? pointer.x : pointer.y;\r\n            const distance=Math.abs(mouse-pos);\r\n\r\n            if(distance<CONFIG.wallRadius){\r\n                let power=1-distance\/CONFIG.wallRadius;\r\n                power=power*power*(3-2*power);\r\n\r\n                const direction=mouse<pos ? 1 : -1;\r\n\r\n                state[targetKey] +=\r\n                    direction *\r\n                    movement *\r\n                    power;\r\n            }\r\n        });\r\n    }\r\n\r\n    function constrain(){\r\n        state.targetX1=Math.max(\r\n            LIMITS.x1min,\r\n            Math.min(LIMITS.x1max,state.targetX1)\r\n        );\r\n\r\n        state.targetX2=Math.max(\r\n            state.targetX1+LIMITS.minGapX,\r\n            Math.min(LIMITS.x2max,state.targetX2)\r\n        );\r\n\r\n        state.targetX3=Math.max(\r\n            state.targetX2+LIMITS.minGapX,\r\n            Math.min(LIMITS.x3max,state.targetX3)\r\n        );\r\n\r\n        state.targetY1=Math.max(\r\n            LIMITS.y1min,\r\n            Math.min(LIMITS.y1max,state.targetY1)\r\n        );\r\n\r\n        state.targetY2=Math.max(\r\n            state.targetY1+LIMITS.minGapY,\r\n            Math.min(LIMITS.y2max,state.targetY2)\r\n        );\r\n    }\r\n\r\n    \/* ========================================================\r\n       ACTUALIZACIONES VISUALES\r\n       ======================================================== *\/\r\n\r\n    function updateRooms(activeRoom){\r\n        rooms.forEach((room,index)=>{\r\n            const points=room.getPoints();\r\n\r\n            room.element.setAttribute(\r\n                'points',\r\n                points.map(p=>p[0]+','+p[1]).join(' ')\r\n            );\r\n\r\n            room.element.classList.toggle(\r\n                'active',\r\n                index===activeRoom\r\n            );\r\n        });\r\n    }\r\n\r\n    function updateWalls(){\r\n        walls.forEach(wall=>{\r\n            const a=wall.getA();\r\n            const b=wall.getB();\r\n\r\n            setLine(\r\n                wall.element,\r\n                a[0],a[1],\r\n                b[0],b[1]\r\n            );\r\n\r\n            if(pointer.active){\r\n                const nearest=distanciaSegmento(\r\n                    pointer.x,\r\n                    pointer.y,\r\n                    a[0],a[1],\r\n                    b[0],b[1]\r\n                );\r\n\r\n                wall.element.classList.toggle(\r\n                    'active',\r\n                    nearest.distance<65\r\n                );\r\n            }else{\r\n                wall.element.classList.remove('active');\r\n            }\r\n        });\r\n    }\r\n\r\n    function updateDoors(){\r\n        doors.forEach(door=>{\r\n            door.group.setAttribute(\r\n                'transform',\r\n                `translate(${door.getX()} ${door.getY()}) rotate(${door.rotacion})`\r\n            );\r\n        });\r\n    }\r\n\r\n    function updateWindows(){\r\n        windows.forEach(w=>{\r\n            const x1=w.getX1();\r\n            const y1=w.getY1();\r\n            const x2=w.getX2();\r\n            const y2=w.getY2();\r\n\r\n            setLine(w.line1,x1,y1,x2,y2);\r\n\r\n            const horizontal =\r\n                Math.abs(x2-x1) >\r\n                Math.abs(y2-y1);\r\n\r\n            setLine(\r\n                w.line2,\r\n                horizontal ? x1 : x1+5,\r\n                horizontal ? y1+5 : y1,\r\n                horizontal ? x2 : x2+5,\r\n                horizontal ? y2+5 : y2\r\n            );\r\n        });\r\n    }\r\n\r\n    function updateNodes(){\r\n        const positions=nodePositions();\r\n\r\n        nodeElements.forEach((circle,index)=>{\r\n            const p=positions[index];\r\n\r\n            if(!p){\r\n                circle.style.opacity=0;\r\n                return;\r\n            }\r\n\r\n            circle.style.opacity=.16;\r\n            circle.setAttribute('cx',p[0]);\r\n            circle.setAttribute('cy',p[1]);\r\n        });\r\n    }\r\n\r\n    function updatePointer(time){\r\n        if(!pointer.active){\r\n            pointerCircle.style.opacity=0;\r\n            pointerRing.style.opacity=0;\r\n            pointerGlow.style.opacity=0;\r\n            return;\r\n        }\r\n\r\n        [pointerCircle,pointerRing,pointerGlow].forEach(el=>{\r\n            el.setAttribute('cx',pointer.x);\r\n            el.setAttribute('cy',pointer.y);\r\n        });\r\n\r\n        pointerCircle.style.opacity=.92;\r\n        pointerRing.style.opacity=.4;\r\n        pointerGlow.style.opacity=.5;\r\n\r\n        pointerRing.setAttribute(\r\n            'r',\r\n            11+Math.sin(time*.004)*2\r\n        );\r\n    }\r\n\r\n    \/* ========================================================\r\n       POINTER GLOBAL\r\n       ======================================================== *\/\r\n\r\n    \/* ========================================================\r\n   POINTER GLOBAL \u00b7 PLANO + SEGUNDA SECCI\u00d3N\r\n   ======================================================== *\/\r\n\r\nfunction estaEnZonaCursor(event){\r\n\r\n    const elemento = document.elementFromPoint(\r\n        event.clientX,\r\n        event.clientY\r\n    );\r\n\r\n    if(!elemento){\r\n        return false;\r\n    }\r\n\r\n    \/*\r\n     * El cursor funciona:\r\n     * 1. en la secci\u00f3n original del plano\r\n     * 2. en cualquier secci\u00f3n con clase cursor-zone\r\n     *\/\r\n    return (\r\n        section.contains(elemento) ||\r\n        !!elemento.closest('.cursor-zone')\r\n    );\r\n}\r\n\r\n\r\nfunction actualizarCursorGlobal(event){\r\n\r\n    \/*\r\n     * Como ahora el cursor es FIXED,\r\n     * usamos directamente las coordenadas de pantalla.\r\n     *\/\r\n    globalTargetX = event.clientX;\r\n    globalTargetY = event.clientY;\r\n\r\n    if(!globalCursorActivo){\r\n\r\n        globalCursorX = globalTargetX;\r\n        globalCursorY = globalTargetY;\r\n\r\n        globalCursorActivo = true;\r\n    }\r\n\r\n    const dentro =\r\n        estaEnZonaCursor(event);\r\n\r\n    globalCursor.style.opacity =\r\n        dentro ? '1' : '0';\r\n\r\n\r\n    \/*\r\n     * La casa SOLO reacciona cuando estamos\r\n     * dentro de la primera secci\u00f3n.\r\n     *\/\r\n    const elemento = document.elementFromPoint(\r\n        event.clientX,\r\n        event.clientY\r\n    );\r\n\r\n    if(\r\n        elemento &&\r\n        section.contains(elemento)\r\n    ){\r\n\r\n        convertirCursor(\r\n            event.clientX,\r\n            event.clientY\r\n        );\r\n\r\n    }else{\r\n\r\n        \/*\r\n         * En la segunda secci\u00f3n sigue el punto,\r\n         * pero la casa deja de reaccionar.\r\n         *\/\r\n        pointer.active = false;\r\n    }\r\n}\r\n\r\n\r\n\/*\r\n * Escuchamos el rat\u00f3n en toda la p\u00e1gina,\r\n * no solamente dentro de la casa.\r\n *\/\r\ndocument.addEventListener(\r\n    'pointermove',\r\n    actualizarCursorGlobal,\r\n    {passive:true}\r\n);\r\n\r\ndocument.addEventListener(\r\n    'pointerdown',\r\n    actualizarCursorGlobal,\r\n    {passive:true}\r\n);\r\n\r\nlet scrollTimer;\r\n\r\nwindow.addEventListener(\r\n    'scroll',\r\n    function(){\r\n\r\n        globalCursor.style.opacity = '0';\r\n        pointer.active = false;\r\n\r\n        clearTimeout(scrollTimer);\r\n\r\n        scrollTimer = setTimeout(function(){\r\n\r\n            const fakeEvent = {\r\n                clientX: globalTargetX,\r\n                clientY: globalTargetY\r\n            };\r\n\r\n            const elemento = document.elementFromPoint(\r\n                globalTargetX,\r\n                globalTargetY\r\n            );\r\n\r\n            const dentro =\r\n                elemento &&\r\n                (\r\n                    section.contains(elemento) ||\r\n                    !!elemento.closest('.cursor-zone')\r\n                );\r\n\r\n            globalCursor.style.opacity =\r\n                dentro ? '1' : '0';\r\n\r\n        },120);\r\n\r\n    },\r\n    {passive:true}\r\n);\r\n    window.addEventListener(\r\n        'resize',\r\n        actualizarVistaPlano,\r\n        {passive:true}\r\n    );\r\n\r\n    \/* ========================================================\r\n       ANIMACI\u00d3N\r\n       ======================================================== *\/\r\n\r\n    function animate(time){\r\n\r\n        if(globalCursorActivo){\r\n            globalCursorX +=\r\n                (globalTargetX-globalCursorX)*.20;\r\n\r\n            globalCursorY +=\r\n                (globalTargetY-globalCursorY)*.20;\r\n\r\n            globalCursor.style.left=\r\n                globalCursorX+'px';\r\n\r\n            globalCursor.style.top=\r\n                globalCursorY+'px';\r\n        }\r\n\r\n        pointer.x +=\r\n            (pointer.targetX-pointer.x)*.18;\r\n\r\n        pointer.y +=\r\n            (pointer.targetY-pointer.y)*.18;\r\n\r\n        state.targetX1=ORIGINAL.x1;\r\n        state.targetX2=ORIGINAL.x2;\r\n        state.targetX3=ORIGINAL.x3;\r\n        state.targetY1=ORIGINAL.y1;\r\n        state.targetY2=ORIGINAL.y2;\r\n\r\n        const activeRoom=getActiveRoom();\r\n\r\n        applyRoomExpansion(activeRoom);\r\n        applyWallRepulsion();\r\n        constrain();\r\n\r\n        state.x1 +=\r\n            (state.targetX1-state.x1)*CONFIG.easing;\r\n\r\n        state.x2 +=\r\n            (state.targetX2-state.x2)*CONFIG.easing;\r\n\r\n        state.x3 +=\r\n            (state.targetX3-state.x3)*CONFIG.easing;\r\n\r\n        state.y1 +=\r\n            (state.targetY1-state.y1)*CONFIG.easing;\r\n\r\n        state.y2 +=\r\n            (state.targetY2-state.y2)*CONFIG.easing;\r\n\r\n        updateRooms(activeRoom);\r\n\r\n        \/*\r\n         * IMPORTANTE:\r\n         * el mobiliario se recalcula DESPU\u00c9S de mover los muros.\r\n         * Por eso sigue pegado a las paredes y puede girar.\r\n         *\/\r\n        updateFurniture(activeRoom);\r\n\r\n        \/*\r\n         * La distribuci\u00f3n ya se resuelve con reglas por estancia:\r\n         * muebles de pared en paredes, puertas libres y piezas\r\n         * centrales solo donde tiene sentido.\r\n         *\/\r\n        updateWalls();\r\n        updateDoors();\r\n        updateWindows();\r\n        updateNodes();\r\n        updatePointer(time);\r\n\r\n        requestAnimationFrame(animate);\r\n    }\r\n\r\n    requestAnimationFrame(animate);\r\n}\r\n\r\n\/* ============================================================\r\n   INICIO ELEMENTOR\r\n   ============================================================ *\/\r\n\r\nif(document.readyState==='loading'){\r\n    document.addEventListener(\r\n        'DOMContentLoaded',\r\n        ()=>setTimeout(iniciarPlanoEditeca,300)\r\n    );\r\n}else{\r\n    setTimeout(iniciarPlanoEditeca,300);\r\n}\r\n\r\n})();\r\n<\/script>\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-b47476c e-con e-atomic-element e-flexbox-base e-b47476c-7fe40b4 \" data-id=\"b47476c\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"b47476c\">\n    <div class=\"elementor-element elementor-element-fe3c4d9 e-con e-atomic-element e-flexbox-base e-fe3c4d9-fe68c3f \" data-id=\"fe3c4d9\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"fe3c4d9\">\n    \t\t<div class=\"elementor-element elementor-element-eb25586 elementor-widget__width-initial elementor-widget elementor-widget-image\" data-id=\"eb25586\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" width=\"1\" height=\"1\" src=\"https:\/\/mcpbim.com\/wp-content\/uploads\/2026\/09\/logo-en-narnaja-edibim.svg\" class=\"attachment-large size-large wp-image-1662\" alt=\"\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-225e23c e-con e-atomic-element e-flexbox-base e-225e23c-cae8696 \" data-id=\"225e23c\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"225e23c\">\n    \t\t<div class=\"elementor-element elementor-element-650b213 elementor-widget elementor-widget-text-editor\" data-id=\"650b213\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p style=\"text-align: right;\">PR\u00d3XIMAMENTE EN EDIBIM<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5df9d9b elementor-widget elementor-widget-text-editor\" data-id=\"5df9d9b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<h2 class=\"edibim-title\" style=\"text-align: right;\"><strong>NAVISWORKS \u00b7 CIVIL 3D<\/strong><\/h2>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5c2d7e5 elementor-widget elementor-widget-text-editor\" data-id=\"5c2d7e5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p class=\"PDq2pG_selectionAnchorContainer\" dir=\"auto\" style=\"text-align: right;\" data-start=\"1091\" data-end=\"1197\">Estamos ampliando la familia EdiBIM con nuevas conexiones y flujos de trabajo para otras aplicaciones BIM.<\/p><p dir=\"auto\" style=\"text-align: right;\" data-start=\"1199\" data-end=\"1269\"><strong data-start=\"1199\" data-end=\"1269\">Navisworks y Civil 3D ser\u00e1n las pr\u00f3ximas incorporaciones a EdiBIM.<\/strong><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n\n<\/div>\n\n<\/div>\n<div class=\"elementor-element elementor-element-5468b87 e-con e-atomic-element e-flexbox-base e-5468b87-192ca6a edibim-franja \" data-id=\"5468b87\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"5468b87\">\n    <div class=\"elementor-element elementor-element-4da91e0 e-con e-atomic-element e-flexbox-base \" data-id=\"4da91e0\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"4da91e0\">\n    \t\t\t<button class=\"e-3c3ebae-1127a92 edibim-open-contact e-button-base\" data-interaction-id=\"3c3ebae\">\n\t\tCONT\u00c1CTANOS\n\t<\/button>\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-e6290a8 e-con e-atomic-element e-flexbox-base e-e6290a8-69ff3ae \" data-id=\"e6290a8\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"e6290a8\">\n    \t\t<div class=\"elementor-element elementor-element-5fbf412 elementor-widget elementor-widget-text-editor\" data-id=\"5fbf412\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p><span style=\"font-weight: 400;\">EdiBIM para Revit operativo \u00b7 Desarrollado por Editeca \u00b7 Incluido en Curso Revit + IA de Editeca \u00b7 No se vende por separado<\/span><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-a171c46 e-con e-atomic-element e-flexbox-base e-a171c46-19ef18b \" data-id=\"a171c46\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"a171c46\">\n    \t\t<div class=\"elementor-element elementor-element-cbc8186 edibim-links-glitch elementor-icon-list--layout-traditional elementor-list-item-link-full_width elementor-widget elementor-widget-icon-list\" data-id=\"cbc8186\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"icon-list.default\">\n\t\t\t\t\t\t\t<ul class=\"elementor-icon-list-items\">\n\t\t\t\t\t\t\t<li class=\"elementor-icon-list-item\">\n\t\t\t\t\t\t\t\t\t\t\t<a href=\"https:\/\/editeca.com\/aviso-legal\/\" target=\"_blank\" rel=\"noopener\">\n\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"elementor-icon-list-text\">Aviso legal<\/span>\n\t\t\t\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t<\/li>\n\t\t\t\t\t\t\t\t<li class=\"elementor-icon-list-item\">\n\t\t\t\t\t\t\t\t\t\t\t<a href=\"https:\/\/editeca.com\/politica-de-privacidad\/\" target=\"_blank\" rel=\"noopener\">\n\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"elementor-icon-list-text\">Pol\u00edtica de privacidad<\/span>\n\t\t\t\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t<\/li>\n\t\t\t\t\t\t\t\t<li class=\"elementor-icon-list-item\">\n\t\t\t\t\t\t\t\t\t\t\t<a href=\"https:\/\/editeca.com\/politica-de-cookies\/\" target=\"_blank\" rel=\"noopener\">\n\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"elementor-icon-list-text\">Pol\u00edtica de Cookies<\/span>\n\t\t\t\t\t\t\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t\t<\/li>\n\t\t\t\t\t\t<\/ul>\n\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n<div class=\"elementor-element elementor-element-30bdff4 e-con e-atomic-element e-flexbox-base e-30bdff4-4b14d1c \" data-id=\"30bdff4\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"30bdff4\">\n    \t\t<div class=\"elementor-element elementor-element-5c9fc6f elementor-widget elementor-widget-image\" data-id=\"5c9fc6f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" width=\"1\" height=\"1\" src=\"https:\/\/mcpbim.com\/wp-content\/uploads\/2026\/09\/logo-en-blanco-edibim.svg\" class=\"attachment-large size-large wp-image-1661\" alt=\"\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\n<\/div>\n\n<\/div>\n\n<\/div>\n\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>EDIBIM PARA REVIT Convierte instrucciones en acciones reales dentro de tu modelo: consulta, crea, modifica y documenta con inteligencia artificial. T\u00fa decides qu\u00e9 hacer. EdiBIM lo ejecuta. T\u00fa compruebas el resultado. VER EDIBIM EN ACCI\u00d3N ACCEDER A EDIBIM TU CRITERIO BIM. EDIBIM LO EJECUTA EN REVIT. CONT\u00c1CTANOS EdiBIM para Revit operativo \u00b7 Desarrollado por Editeca [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"content-type":"","footnotes":""},"class_list":["post-8","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/mcpbim.com\/index.php?rest_route=\/wp\/v2\/pages\/8","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mcpbim.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/mcpbim.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/mcpbim.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mcpbim.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=8"}],"version-history":[{"count":1611,"href":"https:\/\/mcpbim.com\/index.php?rest_route=\/wp\/v2\/pages\/8\/revisions"}],"predecessor-version":[{"id":2224,"href":"https:\/\/mcpbim.com\/index.php?rest_route=\/wp\/v2\/pages\/8\/revisions\/2224"}],"wp:attachment":[{"href":"https:\/\/mcpbim.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=8"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}