1<!DOCTYPE html> 2<html lang="en-US"> 3<head> 4 <meta charset="UTF-8"> 5 <meta name='robots' content='index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1' />
6<script>Object.defineProperty(window,"angiePlugin",{value:{"schemaVersion":1,"available":true,"version":"1.1.19"},writable:false,configurable:false});</script>
6 7 <!-- This site is optimized with the Yoast SEO plugin v28.6 - https://yoast.com/product/yoast-seo-wordpress/ --> 8 <title>CONTACT US - A+UD</title> 9 <link rel="canonical" href="https://audpr.com/contact-us/" /> 10 <meta property="og:locale" content="en_US" /> 11 <meta property="og:type" content="article" /> 12 <meta property="og:title" content="CONTACT US - A+UD" /> 13 <meta property="og:url" content="https://audpr.com/contact-us/" /> 14 <meta property="og:site_name" content="A+UD" /> 15 <meta property="article:publisher" content="https://www.facebook.com/infoarchud/" /> 16 <meta property="article:modified_time" content="2026-09-16T21:37:38+00:00" /> 17 <meta property="og:image" content="https://audpr.com/wp-content/uploads/2025/09/mapa_2.jpg" /> 18 <meta property="og:image:width" content="641" /> 19 <meta property="og:image:height" content="855" /> 20 <meta property="og:image:type" content="image/jpeg" /> 21 <meta name="twitter:card" content="summary_large_image" /> 22 <meta name="twitter:label1" content="Est. reading time" /> 23 <meta name="twitter:data1" content="1 minute" /> 24
24<script type="application/ld+json" class="yoast-schema-graph">{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/audpr.com\/contact-us\/","url":"https:\/\/audpr.com\/contact-us\/","name":"CONTACT US - A+UD","isPartOf":{"@id":"https:\/\/audpr.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/audpr.com\/contact-us\/#primaryimage"},"image":{"@id":"https:\/\/audpr.com\/contact-us\/#primaryimage"},"thumbnailUrl":"https:\/\/audpr.com\/wp-content\/uploads\/2025\/09\/mapa_2.jpg","datePublished":"2025-08-23T16:04:06+00:00","dateModified":"2026-09-16T21:37:38+00:00","breadcrumb":{"@id":"https:\/\/audpr.com\/contact-us\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/audpr.com\/contact-us\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/audpr.com\/contact-us\/#primaryimage","url":"https:\/\/audpr.com\/wp-content\/uploads\/2025\/09\/mapa_2.jpg","contentUrl":"https:\/\/audpr.com\/wp-content\/uploads\/2025\/09\/mapa_2.jpg","width":641,"height":855},{"@type":"BreadcrumbList","@id":"https:\/\/audpr.com\/contact-us\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/audpr.com\/"},{"@type":"ListItem","position":2,"name":"CONTACT US"}]},{"@type":"WebSite","@id":"https:\/\/audpr.com\/#website","url":"https:\/\/audpr.com\/","name":"A+UD","description":"Architecture Urban Design","publisher":{"@id":"https:\/\/audpr.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/audpr.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/audpr.com\/#organization","name":"A+UD","url":"https:\/\/audpr.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/audpr.com\/#\/schema\/logo\/image\/","url":"https:\/\/audpr.com\/wp-content\/uploads\/2025\/07\/logo.png","contentUrl":"https:\/\/audpr.com\/wp-content\/uploads\/2025\/07\/logo.png","width":1240,"height":392,"caption":"A+UD"},"image":{"@id":"https:\/\/audpr.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/infoarchud\/"]}]}</script>
24 25 <!-- / Yoast SEO plugin. --> 26 27 28<link rel="alternate" type="application/rss+xml" title="A+UD » Feed" href="https://audpr.com/feed/" /> 29<link rel="alternate" type="application/rss+xml" title="A+UD » Comments Feed" href="https://audpr.com/comments/feed/" /> 30<link rel="alternate" title="oEmbed (JSON)" type="application/json+oembed" href="https://audpr.com/wp-json/oembed/1.0/embed?url=https%3A%2F%2Faudpr.com%2Fcontact-us%2F" /> 31<link rel="alternate" title="oEmbed (XML)" type="text/xml+oembed" href="https://audpr.com/wp-json/oembed/1.0/embed?url=https%3A%2F%2Faudpr.com%2Fcontact-us%2F&format=xml" /> 32<style id="wp-img-auto-sizes-contain-inline-css"> 33img:is([sizes=auto i],[sizes^="auto," i]){contain-intrinsic-size:3000px 1500px} 34/*# sourceURL=wp-img-auto-sizes-contain-inline-css */ 35</style> 36<style id="cf-frontend-style-inline-css"> 37@font-face { 38 font-family: 'GT America Bold Italic'; 39 font-style: italic; 40 font-weight: 400; 41 src: url('https://audpr.com/wp-content/uploads/2025/07/GT-America-Bold-Italic.otf') format('OpenType'); 42} 43@font-face { 44 font-family: 'GT-America-Regular'; 45 font-style: normal; 46 font-weight: 400; 47 src: url('https://audpr.com/wp-content/uploads/2025/07/GT-America-Regular.otf') format('OpenType'); 48} 49@font-face { 50 font-family: 'GT America Bold'; 51 font-style: normal; 52 font-weight: 700; 53 src: url('https://audpr.com/wp-content/uploads/2025/07/GT-America-Bold.otf') format('OpenType'); 54} 55/*# sourceURL=cf-frontend-style-inline-css */ 56</style> 57<style id="wp-emoji-styles-inline-css"> 58 59 img.wp-smiley, img.emoji { 60 display: inline !important; 61 border: none !important; 62 box-shadow: none !important; 63 height: 1em !important; 64 width: 1em !important; 65 margin: 0 0.07em !important; 66 vertical-align: -0.1em !important; 67 background: none !important; 68 padding: 0 !important; 69 } 70/*# sourceURL=wp-emoji-styles-inline-css */ 71</style>
72<style id="site-designer-shared-pattern-classes-inline-css"> 73/* Dark cover/section overlay + on-dark text. */ 74body .wp-site-blocks .is-style-overlay-dark .wp-block-cover__background { background-color: var(--wp--preset--color--base-3); color: var(--wp--preset--color--contrast-3); } 75.wp-block-cover.is-style-overlay-dark .wp-block-cover__background.has-background-dim { opacity: 0.50 !important; } 76:is(.wp-block-designsetgo-section, .wp-block-designsetgo-scroll-slides).is-style-overlay-dark { --dsgo-overlay-color: var(--wp--preset--color--base-3); --dsgo-overlay-opacity: 0.50; color: var(--wp--preset--color--contrast-3); } 77.dsgo-image-accordion-item.is-style-overlay-dark { --dsgo-overlay-color: var(--wp--preset--color--base-3); } 78.wp-block-group.has-background.is-style-overlay-dark { box-shadow: inset 0 0 0 9999px color-mix(in srgb, var(--wp--preset--color--base-3) 50%, transparent); } 79body .wp-site-blocks .is-style-overlay-dark, body .wp-site-blocks .is-style-on-dark { --dsgo-text-color: var(--wp--preset--color--contrast-3); color: var(--wp--preset--color--contrast-3) !important; } 80body .wp-site-blocks .is-style-overlay-dark :is(h1,h2,h3,h4,h5,h6,p,li,blockquote,cite), body .wp-site-blocks .is-style-on-dark :is(h1,h2,h3,h4,h5,h6,p,li,blockquote,cite) { color: var(--wp--preset--color--contrast-3) !important; } 81 82/* Solid dark section background + light text. */ 83.is-style-bg-dark { background-color: var(--wp--preset--color--contrast); color: var(--wp--preset--color--base); } 84.is-style-bg-dark a { color: var(--wp--preset--color--base); }
85/*# sourceURL=site-designer-shared-pattern-classes-inline-css */ 86</style> 87<style id="global-styles-inline-css"> 88:root{--wp--preset--aspect-ratio--square: 1;--wp--preset--aspect-ratio--4-3: 4/3;--wp--preset--aspect-ratio--3-4: 3/4;--wp--preset--aspect-ratio--3-2: 3/2;--wp--preset--aspect-ratio--2-3: 2/3;--wp--preset--aspect-ratio--16-9: 16/9;--wp--preset--aspect-ratio--9-16: 9/16;--wp--preset--color--black: #000000;--wp--preset--color--cyan-bluish-gray: #abb8c3;--wp--preset--color--white: #ffffff;--wp--preset--color--pale-pink: #f78da7;--wp--preset--color--vivid-red: #cf2e2e;--wp--preset--color--luminous-vivid-orange: #ff6900;--wp--preset--color--luminous-vivid-amber: #fcb900;--wp--preset--color--light-green-cyan: #7bdcb5;--wp--preset--color--vivid-green-cyan: #00d084;--wp--preset--color--pale-cyan-blue: #8ed1fc;--wp--preset--color--vivid-cyan-blue: #0693e3;--wp--preset--color--vivid-purple: #9b51e0;--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple: linear-gradient(135deg,rgb(6,147,227) 0%,rgb(155,81,224) 100%);--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan: linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%);--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange: linear-gradient(135deg,rgb(252,185,0) 0%,rgb(255,105,0) 100%);--wp--preset--gradient--luminous-vivid-orange-to-vivid-red: linear-gradient(135deg,rgb(255,105,0) 0%,rgb(207,46,46) 100%);--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray: linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%);
88--wp--preset--gradient--cool-to-warm-spectrum: linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%);--wp--preset--gradient--blush-light-purple: linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%);--wp--preset--gradient--blush-bordeaux: linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%);--wp--preset--gradient--luminous-dusk: linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%);--wp--preset--gradient--pale-ocean: linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%);--wp--preset--gradient--electric-grass: linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%);--wp--preset--gradient--midnight: linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%);--wp--preset--font-size--small: 13px;--wp--preset--font-size--medium: 20px;--wp--preset--font-size--large: 36px;--wp--preset--font-size--x-large: 42px;--wp--preset--spacing--20: 0.44rem;--wp--preset--spacing--30: 0.67rem;--wp--preset--spacing--40: 1rem;--wp--preset--spacing--50: 1.5rem;--wp--preset--spacing--60: 2.25rem;--wp--preset--spacing--70: 3.38rem;--wp--preset--spacing--80: 5.06rem;--wp--preset--shadow--natural: 6px 6px 9px rgba(0, 0, 0, 0.2);--wp--preset--shadow--deep: 12px 12px 50px rgba(0, 0, 0, 0.4);--wp--preset--shadow--sharp: 6px 6px 0px rgba(0, 0, 0, 0.2);--wp--preset--shadow--outlined: 6px 6px 0px -3px rgb(255, 255, 255), 6px 6px rgb(0, 0, 0);--wp--preset--shadow--crisp: 6px 6px 0px rgb(0, 0, 0);}.wp-block-button{--wp--preset--dimension--25: 25%;--wp--preset--dimension--50: 50%;--wp--preset--dimension--75: 75%;--wp--preset--dimension--100: 100%;}:root { --wp--style--global--content-size: 800px;--wp--style--global--wide-size: 1200px; }:where(body) { margin: 0; }.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }:where(.wp-site-blocks) > * { margin-block-start: 24px; margin-block-end: 0; }:where(.wp-site-blocks) > :first-child { margin-block-start: 0; }:where(.wp-site-blocks) > :last-child { margin-block-end: 0; }:root { --wp--style--block-gap: 24px; }:root :where(.is-layout-flow) > :first-child{margin-block-start: 0;}:root :where(.is-layout-flow) > :last-child{margin-block-end: 0;}:root :where(.is-layout-flow) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-constrained) > :first-child{margin-block-start: 0;}:root :where(.is-layout-constrained) > :last-child{margin-block-end: 0;}:root :where(.is-layout-constrained) > *{margin-block-start: 24px;margin-block-end: 0;}:root :where(.is-layout-flex){gap: 24px;}:root :where(.is-layout-grid){gap: 24px;}.is-layout-flow > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-flow > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-flow > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignleft{float: left;margin-inline-start: 0;margin-inline-end: 2em;}.is-layout-constrained > .alignright{float: right;margin-inline-start: 2em;margin-inline-end: 0;}.is-layout-constrained > .aligncenter{margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){max-width: var(--wp--style--global--content-size);margin-left: auto !important;margin-right: auto !important;}.is-layout-constrained > .alignwide{max-width: var(--wp--style--global--wide-size);}body .is-layout-flex{display: flex;}.is-layout-flex{flex-wrap: wrap;align-items: center;}.is-layout-flex > :is(*, div){margin: 0;}body .is-layout-grid{display: grid;}.is-layout-grid > :is(*, div){margin: 0;}body{padding-top: 0px;padding-right: 0px;padding-bottom: 0px;padding-left: 0px;}:root :where(.wp-element-button, .wp-block-button__link){background-color: #32373c;border-width: 0;color: #fff;font-family: inherit;font-size: inherit;font-style: inherit;font-weight: inherit;letter-spacing: inherit;line-height: inherit;padding-top: calc(0.667em + 2px);padding-right: calc(1.333em + 2px);padding-bottom: calc(0.667em + 2px);padding-left: calc(1.333em + 2px);text-decoration: none;text-transform: inherit;}.has-black-color{color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-color{color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-color{color: var(--wp--preset--color--white) !important;}.has-pale-pink-color{color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-color{color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-color{color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-color{color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-color{color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-color{color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-color{color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-color{color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-color{color: var(--wp--preset--color--vivid-purple) !important;}.has-black-background-color{background-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-background-color{background-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-background-color{background-color: var(--wp--preset--color--white) !important;}.has-pale-pink-background-color{background-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-background-color{background-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-background-color{background-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-background-color{background-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-background-color{background-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-background-color{background-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-background-color{background-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-background-color{background-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-background-color{background-color: var(--wp--preset--color--vivid-purple) !important;}.has-black-border-color{border-color: var(--wp--preset--color--black) !important;}.has-cyan-bluish-gray-border-color{border-color: var(--wp--preset--color--cyan-bluish-gray) !important;}.has-white-border-color{border-color: var(--wp--preset--color--white) !important;}.has-pale-pink-border-color{border-color: var(--wp--preset--color--pale-pink) !important;}.has-vivid-red-border-color{border-color: var(--wp--preset--color--vivid-red) !important;}.has-luminous-vivid-orange-border-color{border-color: var(--wp--preset--color--luminous-vivid-orange) !important;}.has-luminous-vivid-amber-border-color{border-color: var(--wp--preset--color--luminous-vivid-amber) !important;}.has-light-green-cyan-border-color{border-color: var(--wp--preset--color--light-green-cyan) !important;}.has-vivid-green-cyan-border-color{border-color: var(--wp--preset--color--vivid-green-cyan) !important;}.has-pale-cyan-blue-border-color{border-color: var(--wp--preset--color--pale-cyan-blue) !important;}.has-vivid-cyan-blue-border-color{border-color: var(--wp--preset--color--vivid-cyan-blue) !important;}.has-vivid-purple-border-color{border-color: var(--wp--preset--color--vivid-purple) !important;}.has-vivid-cyan-blue-to-vivid-purple-gradient-background{background: var(--wp--preset--gradient--vivid-cyan-blue-to-vivid-purple) !important;}.has-light-green-cyan-to-vivid-green-cyan-gradient-background{background: var(--wp--preset--gradient--light-green-cyan-to-vivid-green-cyan) !important;}.has-luminous-vivid-amber-to-luminous-vivid-orange-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-amber-to-luminous-vivid-orange) !important;}.has-luminous-vivid-orange-to-vivid-red-gradient-background{background: var(--wp--preset--gradient--luminous-vivid-orange-to-vivid-red) !important;}
88.has-very-light-gray-to-cyan-bluish-gray-gradient-background{background: var(--wp--preset--gradient--very-light-gray-to-cyan-bluish-gray) !important;}.has-cool-to-warm-spectrum-gradient-background{background: var(--wp--preset--gradient--cool-to-warm-spectrum) !important;}.has-blush-light-purple-gradient-background{background: var(--wp--preset--gradient--blush-light-purple) !important;}.has-blush-bordeaux-gradient-background{background: var(--wp--preset--gradient--blush-bordeaux) !important;}.has-luminous-dusk-gradient-background{background: var(--wp--preset--gradient--luminous-dusk) !important;}.has-pale-ocean-gradient-background{background: var(--wp--preset--gradient--pale-ocean) !important;}.has-electric-grass-gradient-background{background: var(--wp--preset--gradient--electric-grass) !important;}.has-midnight-gradient-background{background: var(--wp--preset--gradient--midnight) !important;}.has-small-font-size{font-size: var(--wp--preset--font-size--small) !important;}.has-medium-font-size{font-size: var(--wp--preset--font-size--medium) !important;}.has-large-font-size{font-size: var(--wp--preset--font-size--large) !important;}.has-x-large-font-size{font-size: var(--wp--preset--font-size--x-large) !important;} 89:root :where(.wp-block-icon svg){width: 24px;} 90:root :where(.wp-block-pullquote){font-size: 1.5em;line-height: 1.6;} 91/*# sourceURL=global-styles-inline-css */ 92</style> 93<link rel='stylesheet' id='wp-theme-css' href='https://audpr.com/wp-includes/css/dist/theme/design-tokens.min.css?ver=7.1.3' media='all' /> 94<link rel='stylesheet' id='wp-components-css' href='https://audpr.com/wp-includes/css/dist/components/style.min.css?ver=7.1.3' media='all' /> 95<link rel='stylesheet' id='godaddy-styles-css' href='https://audpr.com/wp-content/mu-plugins/vendor/wpex/godaddy-launch/includes/Dependencies/GoDaddy/Styles/build/latest.css?ver=2.0.2' media='all' /> 96<link rel='stylesheet' id='hello-elementor-css' href='https://audpr.com/wp-content/themes/hello-elementor/assets/css/reset.css?ver=3.5.1' media='all' /> 97<link rel='stylesheet' id='hello-elementor-theme-style-css' href='https://audpr.com/wp-content/themes/hello-elementor/assets/css/theme.css?ver=3.5.1' media='all' /> 98<link rel='stylesheet' id='hello-elementor-header-footer-css' href='https://audpr.com/wp-content/themes/hello-elementor/assets/css/header-footer.css?ver=3.5.1' media='all' /> 99<link rel='stylesheet' id='elementor-frontend-css' href='https://audpr.com/wp-content/plugins/elementor/assets/css/frontend.min.css?ver=4.3.4' media='all' /> 100<link rel='stylesheet' id='elementor-post-6-css' href='https://audpr.com/wp-content/uploads/elementor/css/post-6.css?ver=1791532205' media='all' /> 101<link rel='stylesheet' id='widget-spacer-css' href='https://audpr.com/wp-content/plugins/elementor/assets/css/widget-spacer.min.css?ver=4.3.4' media='all' /> 102<link rel='stylesheet' id='widget-heading-css' href='https://audpr.com/wp-content/plugins/elementor/assets/css/widget-heading.min.css?ver=4.3.4' media='all' /> 103<link rel='stylesheet' id='e-animation-fadeInRight-css' href='https://audpr.com/wp-content/plugins/elementor/assets/lib/animations/styles/fadeInRight.min.css?ver=4.3.4' media='all' /> 104<link rel='stylesheet' id='e-animation-fadeInLeft-css' href='https://audpr.com/wp-content/plugins/elementor/assets/lib/animations/styles/fadeInLeft.min.css?ver=4.3.4' media='all' /> 105<link rel='stylesheet' id='e-popup-css' href='https://audpr.com/wp-content/plugins/elementor-pro/assets/css/conditionals/popup.min.css?ver=4.3.1' media='all' /> 106<link rel='stylesheet' id='elementor-post-1271-css' href='https://audpr.com/wp-content/uploads/elementor/css/post-1271.css?ver=1791533034' media='all' /> 107<link rel='stylesheet' id='elementor-post-672-css' href='https://audpr.com/wp-content/uploads/elementor/css/post-672.css?ver=1791532205' media='all' /> 108<link rel='stylesheet' id='tablepress-default-css' href='https://audpr.com/wp-content/plugins/tablepress/css/build/default.css?ver=3.4' media='all' /> 109<link rel='stylesheet' id='elementor-gf-local-roboto-css' href='https://audpr.com/wp-content/uploads/elementor/google-fonts/css/roboto.css?ver=1751413717' media='all' /> 110<link rel='stylesheet' id='elementor-gf-local-robotoslab-css' href='https://audpr.com/wp-content/uploads/elementor/google-fonts/css/robotoslab.css?ver=1751413731' media='all' />
111<script id="page-transitions-js" src="https://audpr.com/wp-content/plugins/elementor-pro/assets/js/page-transitions.min.js?ver=4.3.1"></script>
vendor: 1 bytes, line 111
111
112<script id="jquery-core-js" src="https://audpr.com/wp-includes/js/jquery/jquery.min.js?ver=3.7.1"></script>
vendor: 1 bytes, line 112
112
113<script id="jquery-migrate-js" src="https://audpr.com/wp-includes/js/jquery/jquery-migrate.min.js?ver=3.4.1"></script>
113 114<link rel="https://api.w.org/" href="https://audpr.com/wp-json/" /><link rel="alternate" title="JSON" type="application/json" href="https://audpr.com/wp-json/wp/v2/pages/1271" /><link rel="EditURI" type="application/rsd+xml" title="RSD" href="https://audpr.com/xmlrpc.php?rsd" /> 115<meta name="generator" content="WordPress 7.1.3" /> 116<link rel='shortlink' href='https://audpr.com/?p=1271' /> 117<!-- start Simple Custom CSS and JS --> 118<style> 119/* ð¹ Tablas planas en todo el sitio */ 120table, th, td { 121 border: none !important; /* elimina bordes */ 122 box-shadow: none !important; /* elimina sombras */ 123 background: transparent !important;/* fondo transparente */ 124 padding: 4px 0 !important; /* menos espacio vertical entre filas */ 125 margin: 0 !important; 126 font-family: inherit !important; 127 font-size: inherit !important; 128 color: inherit !important; 129 text-align: left; 130} 131 132/* ð¹ Columnas del mismo tamaño */ 133table { 134 border-collapse: collapse; 135 width: 100%; /* ocupa todo el ancho del contenedor */ 136 table-layout: fixed; /* columnas del mismo tamaño */ 137} 138 139/* ð¹ LÃnea tenue opcional entre filas (puedes quitar si no quieres) */ 140tr + tr td { 141 border-top: 1px solid rgba(0,0,0,0.05); /* lÃnea muy sutil */ 142} 143 144/* ð¹ Encabezados legibles */ 145th { 146 font-weight: bold; 147} 148 149 150 151 152</style> 153<!-- end Simple Custom CSS and JS --> 154<meta name="generator" content="Elementor 4.3.4; features: e_font_icon_svg, additional_custom_breakpoints; settings: css_print_method-external, google_font-enabled, font_display-swap"> 155 <style> 156 .e-con.e-parent:nth-of-type(n+4):not(.e-lazyloaded):not(.e-no-lazyload), 157 .e-con.e-parent:nth-of-type(n+4):not(.e-lazyloaded):not(.e-no-lazyload) * { 158 background-image: none !important; 159 } 160 @media screen and (max-height: 1024px) { 161 .e-con.e-parent:nth-of-type(n+3):not(.e-lazyloaded):not(.e-no-lazyload), 162 .e-con.e-parent:nth-of-type(n+3):not(.e-lazyloaded):not(.e-no-lazyload) * { 163 background-image: none !important; 164 } 165 } 166 @media screen and (max-height: 640px) { 167 .e-con.e-parent:nth-of-type(n+2):not(.e-lazyloaded):not(.e-no-lazyload), 168 .e-con.e-parent:nth-of-type(n+2):not(.e-lazyloaded):not(.e-no-lazyload) * { 169 background-image: none !important; 170 } 171 } 172 </style> 173 <link rel="icon" href="https://audpr.com/wp-content/uploads/2025/09/cropped-cruz_ico-32x32.png" sizes="32x32" /> 174<link rel="icon" href="https://audpr.com/wp-content/uploads/2025/09/cropped-cruz_ico-192x192.png" sizes="192x192" /> 175<link rel="apple-touch-icon" href="https://audpr.com/wp-content/uploads/2025/09/cropped-cruz_ico-180x180.png" /> 176<meta name="msapplication-TileImage" content="https://audpr.com/wp-content/uploads/2025/09/cropped-cruz_ico-270x270.png" /> 177<style id="wp-site-designer-contrast-fallback"> .wp-block-group:not([data-dsgo-inline-bg]),.wp-block-column:not([data-dsgo-inline-bg]),.wp-block-columns:not([data-dsgo-inline-bg]){--dsgo-text-color:initial;} :is(.wp-block-designsetgo-section,.wp-block-group,.wp-block-column).has-background:not([class*="-background-color"]):not(.has-text-color):not(.is-style-footer-section):not(.is-style-header-section),.wp-block-cover:not(.has-text-color){color:var(--wp--preset--color--contrast-3) !important;--dsgo-text-color:var(--wp--preset--color--contrast-3);} body .wp-site-blocks :is(:is(.wp-block-designsetgo-section,.wp-block-group,.wp-block-column).has-background:not([class*="-background-color"]):not(.has-text-color):not(.is-style-footer-section):not(.is-style-header-section),.wp-block-cover) h1:not(.has-text-color):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(:is(.wp-block-designsetgo-section,.wp-block-group,.wp-block-column).has-background:not([class*="-background-color"]):not(.has-text-color):not(.is-style-footer-section):not(.is-style-header-section),.wp-block-cover) h2:not(.has-text-color):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(:is(.wp-block-designsetgo-section,.wp-block-group,.wp-block-column).has-background:not([class*="-background-color"]):not(.has-text-color):not(.is-style-footer-section):not(.is-style-header-section),.wp-block-cover) h3:not(.has-text-color):where(:not
177(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(:is(.wp-block-designsetgo-section,.wp-block-group,.wp-block-column).has-background:not([class*="-background-color"]):not(.has-text-color):not(.is-style-footer-section):not(.is-style-header-section),.wp-block-cover) h4:not(.has-text-color):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(:is(.wp-block-designsetgo-section,.wp-block-group,.wp-block-column).has-background:not([class*="-background-color"]):not(.has-text-color):not(.is-style-footer-section):not(.is-style-header-section),.wp-block-cover) h5:not(.has-text-color):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(:is(.wp-block-designsetgo-section,.wp-block-group,.wp-block-column).has-background:not([class*="-background-color"]):not(.has-text-color):not(.is-style-footer-section):not(.is-style-header-section),.wp-block-cover) h6:not(.has-text-color):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(:is(.wp-block-designsetgo-section,.wp-block-group,.wp-block-column).has-background:not([class*="-background-color"]):not(.has-text-color):not(.is-style-footer-section):not(.is-style-header-section),.wp-block-cover) p:not(.has-text-color):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(:is(.wp-block-designsetgo-section,.wp-block-group,.wp-block-column).has-background:not([class*="-background-color"]):not(.has-text-color):not(.is-style-footer-section):not(.is-style-header-section),.wp-block-cover) a:where(:not(.wp-element-button):not(.wp-block-social-link-anchor)):not(.has-text-color):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(:is(.wp-block-designsetgo-section,.wp-block-group,.wp-block-column).has-background:not([class*="-background-color"]):not(.has-text-color):not(.is-style-footer-section):not(.is-style-header-section),.wp-block-cover) .wp-element-caption:not(.has-text-color):where(:not(.elementor *):not([data-elementor-type] *)){color:var(--dsgo-text-color,inherit) !important;} body .wp-site-blocks :is(.wp-block-designsetgo-section,.wp-block-group,.wp-block-column)[data-dsgo-inline-bg] .wp-block-button:is([class*="is-style-outline"],[class*="is-style-secondary"]) .wp-element-button{color:var(--dsgo-text-color,inherit) !important;border-color:currentColor !important;}body .wp-site-blocks :is(.wp-block-designsetgo-section,.wp-block-group,.wp-block-column)[data-dsgo-inline-bg] .wp-block-button:is([class*="is-style-outline"],[class*="is-style-secondary"]) .wp-element-button:hover{opacity:0.75;} body .wp-site-blocks [data-dsgo-inline-bg] h1:not(.has-text-color):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks [data-dsgo-inline-bg] h2:not(.has-text-color):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks [data-dsgo-inline-bg] h3:not(.has-text-color):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks [data-dsgo-inline-bg] h4:not(.has-text-color):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks [data-dsgo-inline-bg] h5:not(.has-text-color):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks [data-dsgo-inline-bg] h6:not(.has-text-color):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks [data-dsgo-inline-bg] p:not(.has-text-color):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks [data-dsgo-inline-bg] a:where(:not(.wp-element-button):not(.wp-block-social-link-anchor)):not(.has-text-color):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks [data-dsgo-inline-bg] .wp-element-caption:not(.has-text-color):where(:not(.elementor *):not([data-elementor-type] *)){color:var(--dsgo-text-color,inherit) !important;} .wp-block-designsetgo-section.has-background:not([data-dsgo-inline-bg]):not(.has-text-color){color:var(--wp--preset--color--contrast);--dsgo-text-color:var(--wp--preset--color--contrast);} :is(.wp-block-group,.wp-block-column).is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)),.has-background:is(.wp-block-group,.wp-block-column).is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)),.has-text-color:is(.wp-block-group,.wp-block-column).is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)){background-color:var(--wp--preset--color--accent-5);color:var(--wp--preset--color--contrast);} body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)) h1:where(:not
177(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)) h2:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)) h3:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)) h4:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)) h5:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)) h6:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)) p:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)) a:where(:not(.wp-element-button):not(.wp-block-social-link-anchor)):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)) .wp-element-caption:where(:not(.elementor *):not([data-elementor-type] *)){color:var(--wp--preset--color--contrast);} :is(.wp-block-group,.wp-block-column).is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)),.has-background:is(.wp-block-group,.wp-block-column).is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)),.has-text-color:is(.wp-block-group,.wp-block-column).is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)){background-color:var(--wp--preset--color--accent-2);color:var(--wp--preset--color--contrast);} body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)) h1:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)) h2:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)) h3:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)) h4:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)) h5:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)) h6:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)) p:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)) a:where(:not(.wp-element-button):not(.wp-block-social-link-anchor)):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)) .wp-element-caption:where(:not
177(.elementor *):not([data-elementor-type] *)){color:var(--wp--preset--color--contrast);} :is(.wp-block-group,.wp-block-column).is-style-section-3:not(:is(.has-white-background-color,.has-black-background-color)),.has-background:is(.wp-block-group,.wp-block-column).is-style-section-3:not(:is(.has-white-background-color,.has-black-background-color)),.has-text-color:is(.wp-block-group,.wp-block-column).is-style-section-3:not(:is(.has-white-background-color,.has-black-background-color)){background-color:var(--wp--preset--color--accent-1);color:var(--wp--preset--color--contrast);} body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-3:not(:is(.has-white-background-color,.has-black-background-color)) h1:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-3:not(:is(.has-white-background-color,.has-black-background-color)) h2:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-3:not(:is(.has-white-background-color,.has-black-background-color)) h3:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-3:not(:is(.has-white-background-color,.has-black-background-color)) h4:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-3:not(:is(.has-white-background-color,.has-black-background-color)) h5:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-3:not(:is(.has-white-background-color,.has-black-background-color)) h6:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-3:not(:is(.has-white-background-color,.has-black-background-color)) p:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-3:not(:is(.has-white-background-color,.has-black-background-color)) a:where(:not(.wp-element-button):not(.wp-block-social-link-anchor)):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-3:not(:is(.has-white-background-color,.has-black-background-color)) .wp-element-caption:where(:not(.elementor *):not([data-elementor-type] *)){color:var(--wp--preset--color--contrast);} :is(.wp-block-group,.wp-block-column).is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)),.has-background:is(.wp-block-group,.wp-block-column).is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)),.has-text-color:is(.wp-block-group,.wp-block-column).is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)){background-color:var(--wp--preset--color--accent-3);color:var(--wp--preset--color--accent-2);} body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)) h1:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)) h2:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)) h3:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)) h4:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)) h5:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)) h6:where(:not
177(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)) p:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)) a:where(:not(.wp-element-button):not(.wp-block-social-link-anchor)):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)) .wp-element-caption:where(:not(.elementor *):not([data-elementor-type] *)){color:var(--wp--preset--color--accent-2);} :is(.wp-block-group,.wp-block-column).is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)),.has-background:is(.wp-block-group,.wp-block-column).is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)),.has-text-color:is(.wp-block-group,.wp-block-column).is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)){background-color:var(--wp--preset--color--contrast);color:var(--wp--preset--color--base);} body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)) h1:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)) h2:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)) h3:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)) h4:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)) h5:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)) h6:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)) p:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)) a:where(:not(.wp-element-button):not(.wp-block-social-link-anchor)):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks :is(.wp-block-group,.wp-block-column).is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)) .wp-element-caption:where(:not(.elementor *):not([data-elementor-type] *)){color:var(--wp--preset--color--base);} .wp-block-designsetgo-section.is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)),.has-background.wp-block-designsetgo-section.is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)),.has-text-color.wp-block-designsetgo-section.is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)){background-color:var(--wp--preset--color--base);color:var(--wp--preset--color--contrast);} body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)) h1:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)) h2:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)) h3:where(:not
177(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)) h4:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)) h5:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)) h6:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)) p:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)) a:where(:not(.wp-element-button):not(.wp-block-social-link-anchor)):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-1:not(:is(.has-white-background-color,.has-black-background-color)) .wp-element-caption:where(:not(.elementor *):not([data-elementor-type] *)){color:var(--wp--preset--color--contrast);} .wp-block-designsetgo-section.is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)),.has-background.wp-block-designsetgo-section.is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)),.has-text-color.wp-block-designsetgo-section.is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)){background-color:var(--wp--preset--color--base-2);color:var(--wp--preset--color--contrast-2);} body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)) h1:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)) h2:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)) h3:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)) h4:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)) h5:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)) h6:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)) p:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)) a:where(:not(.wp-element-button):not(.wp-block-social-link-anchor)):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-2:not(:is(.has-white-background-color,.has-black-background-color)) .wp-element-caption:where(:not(.elementor *):not([data-elementor-type] *)){color:var(--wp--preset--color--contrast-2);} .wp-block-designsetgo-section.is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)),.has-background.wp-block-designsetgo-section.is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)),.has-text-color.wp-block-designsetgo-section.is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)){background-color:var(--wp--preset--color--base-3);color:var(--wp--preset--color--contrast);} body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)) h1:where(:not
177(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)) h2:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)) h3:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)) h4:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)) h5:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)) h6:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)) p:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)) a:where(:not(.wp-element-button):not(.wp-block-social-link-anchor)):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-4:not(:is(.has-white-background-color,.has-black-background-color)) .wp-element-caption:where(:not(.elementor *):not([data-elementor-type] *)){color:var(--wp--preset--color--contrast);} .wp-block-designsetgo-section.is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)),.has-background.wp-block-designsetgo-section.is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)),.has-text-color.wp-block-designsetgo-section.is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)){background-color:var(--wp--preset--color--contrast);color:var(--wp--preset--color--base);} body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)) h1:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)) h2:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)) h3:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)) h4:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)) h5:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)) h6:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)) p:where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)) a:where(:not(.wp-element-button):not(.wp-block-social-link-anchor)):where(:not(.elementor *):not([data-elementor-type] *)),body .wp-site-blocks .wp-block-designsetgo-section.is-style-section-5:not(:is(.has-white-background-color,.has-black-background-color)) .wp-element-caption:where(:not(.elementor *):not([data-elementor-type] *)){color:var(--wp--preset--color--base);} body .wp-site-blocks input:not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="image"]):not([type="hidden"]),body .wp-site-blocks textarea,body .wp-site-blocks select{background-color:var(--wp--preset--color--base);color:var(--wp--preset--color--contrast);border:1px solid color-mix(in srgb, var(--wp--preset--color--contrast) 30%, transparent);} .wp-site-blocks input[type="submit"]:not(.wp-element-button),.wp-site-blocks button[type="submit"]:not(.wp-element-button){background-color:var(--wp--preset--color--accent-2);color:var(--wp--preset--color--base);border-width:0;padding:12px 30px;font-family:inherit;font-size:var(--wp--preset--font-size--medium, 1rem);line-height:inherit;cursor:pointer;}</style> 178<style id="aud-territory-map-v10-css"> 179/* A+UD TERRITORY MAP v0.7C 180 FIXED: exact SVG municipality binding; restored v0.3 interaction logic. 181 Safe customization variables remain at the top of this stylesheet. */ 182 183#audTerritoryMap{--bg:#000;--panel:#050606;--map:#252b28;--line:#343a38;--line-bright:#69716d;--text:#d9dfda;--muted:#858d88;--blue:#3da9ff;--blue-soft:rgba(61,169,255,.17);--glow:rgba(61,169,255,.48);--scanline:rgba(110,140,125,.12);--map-width:78%;--map-height:74%;--map-top:18%;--map-bottom:8%;--panel-width:300px;--page-padding:14px 20px 12px;--topbar-height:66px;--footer-height:78px;width:100%;height:100vh;min-height:650px;background:var(--bg);color:var(--text);font-family:Arial,Helvetica,sans-serif;letter-spacing:.055em;line-height:1.2;overflow:hidden;position:relative;isolation:isolate}#audTerritoryMap[data-theme=light]{--bg:#f2f0eb;
183--panel:#e8e6df;--map:#d2d1ca;--line:#c4c5bf;--line-bright:#8b8f89;--text:#252925;--muted:#777c76;--blue:#3c78a8;--blue-soft:rgba(60,120,168,.16);--glow:rgba(60,120,168,.34);--scanline:rgba(80,100,90,.07)}#audTerritoryMap *,#audTerritoryMap *:before,#audTerritoryMap *:after{box-sizing:border-box}#audTerritoryMap button,#audTerritoryMap a{font:inherit}#audTerritoryMap .aud-map-app{width:100%;height:100%;display:grid;grid-template-rows:var(--topbar-height) minmax(0,1fr) var(--footer-height);background:var(--bg);position:relative}#audTerritoryMap .aud-map-app:after{content:"";pointer-events:none;position:absolute;inset:0;z-index:50;background:repeating-linear-gradient(to bottom,var(--scanline) 0,var(--scanline) 1px,transparent 1px,transparent 4px);opacity:.42}#audTerritoryMap .topbar{display:grid;grid-template-columns:250px minmax(0,1fr) auto;align-items:center;border-bottom:1px solid var(--line);padding:0 24px;position:relative;z-index:10}#audTerritoryMap .brand{display:flex;align-items:baseline;gap:22px;white-space:nowrap}#audTerritoryMap .brand-main{font-size:23px;font-weight:700;letter-spacing:.06em}#audTerritoryMap .brand-sub{color:var(--muted);font-size:10px;letter-spacing:.12em}#audTerritoryMap .filters{display:flex;justify-content:center;align-items:stretch;gap:38px;height:100%;min-width:0}#audTerritoryMap .filter{border:0;background:none;color:var(--muted);font-size:10px;letter-spacing:.11em;cursor:pointer;position:relative;padding:0;white-space:nowrap}#audTerritoryMap .filter.active,#audTerritoryMap .filter:hover{color:var(--text)}#audTerritoryMap .filter.active:after{content:"";position:absolute;left:0;right:0;bottom:16px;height:2px;background:var(--blue);box-shadow:0 0 8px var(--glow)}#audTerritoryMap .top-actions{display:flex;align-items:center;gap:16px}.project-total{color:var(--text);font-size:10px;letter-spacing:.11em;white-space:nowrap}#audTerritoryMap .theme-toggle{border:1px solid var(--line-bright);background:transparent;color:var(--muted);padding:7px 9px;cursor:pointer;font-size:8px;letter-spacing:.1em;white-space:nowrap}#audTerritoryMap .theme-toggle:hover{color:var(--text);border-color:var(--blue)}#audTerritoryMap .content{min-height:0;display:grid;grid-template-columns:minmax(0,1fr) var(--panel-width);padding:var(--page-padding);position:relative;z-index:2}#audTerritoryMap .map-frame{min-width:0;min-height:0;position:relative;border:1px solid var(--line);border-right:0;border-radius:5px 0 0 5px;overflow:hidden;background:radial-gradient(circle at 50% 45%,rgba(35,50,42,.13),transparent 60%),var(--bg)}#audTerritoryMap[data-theme=light] .map-frame{background:radial-gradient(circle at 50% 45%,rgba(80,100,90,.08),transparent 60%),var(--bg)}#audTerritoryMap .map-frame:before{content:"";position:absolute;inset:0;background-image:radial-gradient(rgba(170,190,180,.11) .6px,transparent .6px);background-size:5px 5px;opacity:.12;pointer-events:none}#audTerritoryMap .map-svg{position:absolute;left:50%;top:var(--map-top);width:var(--map-width);height:var(--map-height);transform:translateX(-50%);overflow:visible;z-index:1}#audTerritoryMap .map-svg>svg{display:block;width:100%;height:100%;background:transparent!important;overflow:visible}#audTerritoryMap .map-svg .municipality{fill:var(--map);stroke:var(--line-bright);stroke-width:.7;vector-effect:non-scaling-stroke;transition:fill .12s ease,stroke .12s ease,filter .12s ease,opacity .12s ease;cursor:crosshair}#audTerritoryMap .map-svg .municipality:hover,#audTerritoryMap .map-svg .municipality.active{fill:var(--blue-soft);stroke:var(--blue);filter:drop-shadow(0 0 6px var(--glow))}#audTerritoryMap .map-svg.dim .municipality:not(.active){opacity:.35}#audTerritoryMap .map-labels{position:absolute;inset:0;pointer-events:none;z-index:4}#audTerritoryMap .municipality-label{ 184pointer-events:auto; 185cursor:pointer; 186position:absolute; 187transform:translate(-50%,-100%); 188border:0; 189background:transparent; 190padding:0; 191margin:0; 192appearance:none; 193-webkit-appearance:none; 194color:var(--blue); 195font-family:inherit; 196font-size:9px; 197line-height:1.3; 198letter-spacing:.1em; 199text-align:center; 200white-space:nowrap; 201text-shadow:0 0 8px var(--bg); 202} 203#audTerritoryMap .municipality-label small{ 204display:block; 205color:var(--muted);
206font-size:7px; 207letter-spacing:.08em; 208margin-top:3px; 209} 210#audTerritoryMap .plus-marker{ 211pointer-events:auto; 212cursor:pointer; 213position:absolute; 214transform:translate(-50%,-50%); 215border:0; 216background:transparent; 217padding:0; 218margin:0; 219appearance:none; 220-webkit-appearance:none; 221color:var(--blue); 222font-family:inherit; 223font-size:21px; 224font-weight:400; 225line-height:1; 226text-shadow:0 0 8px var(--glow); 227}#audTerritoryMap .coord{position:absolute;color:var(--muted);font-size:8px;letter-spacing:.1em;z-index:5}#audTerritoryMap .coord.tl{top:17px;left:20px}#audTerritoryMap .coord.bl{bottom:18px;left:20px}#audTerritoryMap .coord.br{bottom:18px;right:20px;text-align:right}#audTerritoryMap .map-title{position:absolute;left:50%;top:18px;transform:translateX(-50%);color:var(--blue);text-align:center;pointer-events:none;z-index:6;white-space:nowrap}#audTerritoryMap .map-title strong{display:block;font-size:12px;letter-spacing:.12em}#audTerritoryMap .map-title span{display:block;margin-top:5px;color:var(--muted);font-size:8px;letter-spacing:.1em}#audTerritoryMap .side{min-width:0;border:1px solid var(--line);border-radius:0 5px 5px 0;padding:28px 26px 22px;background:var(--panel);overflow:auto}#audTerritoryMap .side-head{display:flex;justify-content:space-between;align-items:flex-start;gap:15px}#audTerritoryMap .side h1{margin:0;color:var(--blue);font-size:23px;font-weight:500;letter-spacing:.08em}#audTerritoryMap .close{border:0;background:none;color:var(--muted);font-size:25px;line-height:1;cursor:pointer;padding:0}#audTerritoryMap .close:hover{color:var(--text)}#audTerritoryMap .eyebrow{margin-top:25px;color:var(--muted);font-size:8px;letter-spacing:.11em}#audTerritoryMap .big-number{margin-top:10px;font-size:31px;font-weight:400;letter-spacing:.02em}#audTerritoryMap .big-number-label{color:var(--muted);font-size:8px;letter-spacing:.1em;margin-top:3px}#audTerritoryMap .stats{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin-top:24px}#audTerritoryMap .stat+.stat{border-left:1px solid var(--line);padding-left:18px}.stat-number{font-size:21px}.stat-label{margin-top:4px;color:var(--muted);font-size:7px;letter-spacing:.08em}#audTerritoryMap .rule{height:1px;background:var(--line);margin:25px 0}#audTerritoryMap .period{font-size:18px;letter-spacing:.06em}#audTerritoryMap .period-label{margin-top:5px;color:var(--muted);font-size:7px;letter-spacing:.1em}#audTerritoryMap .recent-title{color:var(--muted);font-size:8px;letter-spacing:.11em;margin-bottom:12px}#audTerritoryMap .projects{display:flex;flex-direction:column}#audTerritoryMap .project{display:flex;justify-content:space-between;gap:12px;padding:9px 0;border-bottom:1px solid var(--line);color:var(--text);font-size:9px;line-height:1.4;cursor:pointer;text-decoration:none}#audTerritoryMap .project:hover{color:var(--blue)}#audTerritoryMap .project .project-meta{display:block;color:var(--muted);font-size:7px;margin-top:3px;letter-spacing:.08em}#audTerritoryMap .project .arrow{color:var(--blue);flex:0 0 auto}#audTerritoryMap .view-all{width:100%;margin-top:20px;padding:12px 13px;border:1px solid var(--blue);background:transparent;color:var(--blue);font-size:8px;letter-spacing:.1em;text-align:left;cursor:pointer}#audTerritoryMap .view-all:hover{background:var(--blue-soft);box-shadow:0 0 14px var(--glow)}#audTerritoryMap .bottom{border-top:1px solid var(--line);display:grid;grid-template-columns:1.1fr 1.2fr .8fr;align-items:center;padding:0 24px;position:relative;z-index:10;min-width:0}#audTerritoryMap .bottom-block{min-height:42px;display:flex;align-items:center;gap:15px;padding-right:22px;border-right:1px solid var(--line);min-width:0}#audTerritoryMap .bottom-block:last-child{border-right:0;justify-content:flex-end;padding-right:0}#audTerritoryMap .bottom-title{color:var(--muted);font-size:7px;letter-spacing:.1em;margin-bottom:6px}#audTerritoryMap .bottom-value{color:var(--text);font-size:8px;letter-spacing:.07em}#audTerritoryMap .legend{display:flex;gap:15px}#audTerritoryMap .legend-item{color:var(--muted);font-size:7px;letter-spacing:.05em}#audTerritoryMap .legend-plus{color:var(--blue);font-size:14px;vertical-align:-2px;margin-right:3px}#audTerritoryMap .controls{display:flex;gap:13px;flex-wrap:wrap}#audTerritoryMap .key{border:1px solid var(--line-bright);padding:3px 5px;color:var(--text);
227font-size:7px;margin-right:4px}#audTerritoryMap .prototype-note{position:absolute;left:50%;bottom:4px;transform:translateX(-50%);z-index:60;color:var(--muted);opacity:.55;font-size:6px;letter-spacing:.08em;pointer-events:none}@media(max-width:1100px){#audTerritoryMap .topbar{grid-template-columns:190px minmax(0,1fr) auto;padding:0 16px}#audTerritoryMap .brand-sub{display:none}#audTerritoryMap .filters{gap:22px}#audTerritoryMap .content{grid-template-columns:minmax(0,1fr) 270px}#audTerritoryMap .legend{gap:9px}#audTerritoryMap .controls{gap:8px}}@media(max-width:800px){#audTerritoryMap{height:auto;min-height:100vh;overflow:visible}#audTerritoryMap .aud-map-app{height:auto;min-height:100vh;grid-template-rows:auto auto auto}#audTerritoryMap .topbar{grid-template-columns:1fr auto;grid-template-rows:54px 42px;padding:0 14px}#audTerritoryMap .filters{grid-column:1/-1;justify-content:flex-start;gap:22px;overflow-x:auto}#audTerritoryMap .filter.active:after{bottom:7px}#audTerritoryMap .content{grid-template-columns:1fr;padding:10px;gap:10px}#audTerritoryMap .map-frame{height:62vh;min-height:430px;border-right:1px solid var(--line);border-radius:5px}#audTerritoryMap .side{min-height:480px;border-radius:5px}#audTerritoryMap .bottom{grid-template-columns:1fr;gap:10px;padding:15px}#audTerritoryMap .bottom-block{border-right:0;border-bottom:1px solid var(--line);padding:0 0 10px}#audTerritoryMap .bottom-block:last-child{border-bottom:0}#audTerritoryMap .top-actions .project-total{display:none}} 228 229/* ============================================================ 230 V0.4.3 â SVG-NATIVE MUNICIPALITY MARKERS 231 Markers live inside the actual SVG coordinate system. 232 They are NOT HTML overlays. 233 ============================================================ */ 234 235#audTerritoryMap .aud-svg-marker { 236 pointer-events: all; 237} 238 239#audTerritoryMap .aud-svg-marker-name, 240#audTerritoryMap .aud-svg-marker-count, 241#audTerritoryMap .aud-svg-marker-plus { 242 font-family: inherit !important; 243 font-weight: 400 !important; 244 fill: var(--accent, #2f6f9f) !important; 245 stroke: none !important; 246 paint-order: normal !important; 247 pointer-events: none; 248 user-select: none; 249} 250 251#audTerritoryMap .aud-svg-marker-name { 252 font-size: 8px !important; 253 letter-spacing: 0.8px !important; 254} 255 256#audTerritoryMap .aud-svg-marker-count { 257 font-size: 5px !important; 258 letter-spacing: 0.45px !important; 259 opacity: .8; 260} 261 262#audTerritoryMap .aud-svg-marker-plus { 263 font-size: 16px !important; 264 font-weight: 300 !important; 265 opacity: 1; 266} 267 268#audTerritoryMap .aud-svg-marker:hover .aud-svg-marker-name, 269#audTerritoryMap .aud-svg-marker:hover .aud-svg-marker-plus { 270 fill: var(--hover, #3d7fae) !important; 271} 272 273 274/* ============================================================ 275 V0.6.3 â LIGHT DEFAULT / MAYAGÃEZ 1IN OFFSET 276 + remains visible. 277 Municipality name + project count are hidden until hover/select. 278 ============================================================ */ 279 280#audTerritoryMap .aud-svg-marker-name, 281#audTerritoryMap .aud-svg-marker-count { 282 opacity: 0 !important; 283 transition: opacity .16s ease; 284} 285 286#audTerritoryMap .aud-svg-marker[data-state="hover"] .aud-svg-marker-name, 287#audTerritoryMap .aud-svg-marker[data-state="hover"] .aud-svg-marker-count, 288#audTerritoryMap .aud-svg-marker[data-state="selected"] .aud-svg-marker-name, 289#audTerritoryMap .aud-svg-marker[data-state="selected"] .aud-svg-marker-count { 290 opacity: 1 !important; 291} 292 293#audTerritoryMap .aud-svg-marker-plus { 294 opacity: 1 !important; 295} 296 297 298/* ============================================================ 299 A+UD TERRITORY MAP V0.5 300 EXPANDABLE PROJECT CARDS 301 Uses existing scraped data only. 302 ============================================================ */ 303 304#audTerritoryMap .aud-project-card { 305 border-top: 1px solid var(--line, rgba(40,40,40,.16)); 306} 307 308#audTerritoryMap .aud-project-row { 309 width: 100%; 310 display: grid; 311 grid-template-columns: minmax(0, 1fr) 78px 18px; 312 column-gap: 9px; 313 align-items: start; 314 padding: 10px 0; 315 margin: 0; 316 border: 0 !important; 317 border-radius: 0 !important; 318 background: transparent !important; 319 box-shadow: none !important; 320 color: inherit; 321 text-align: left; 322 font: inherit; 323 cursor: pointer; 324 appearance: none; 325 -webkit-appearance: none; 326} 327 328#audTerritoryMap .aud-project-row:hover { 329 background: transparent !important; 330} 331 332#audTerritoryMap .aud-project-title { 333 min-width: 0; 334 max-width: 100%;
335 overflow-wrap: anywhere; 336 word-break: normal; 337 font-size: 12px; 338 line-height: 1.28; 339 letter-spacing: .15px; 340} 341 342#audTerritoryMap .aud-project-meta { 343 min-width: 0; 344 width: 78px; 345 max-width: 78px; 346 overflow: hidden; 347 text-overflow: ellipsis; 348 white-space: nowrap; 349 text-align: right; 350 font-size: 7px; 351 line-height: 1.2; 352 letter-spacing: .85px; 353 text-transform: uppercase; 354 opacity: .58; 355} 356 357#audTerritoryMap .aud-project-toggle { 358 width: 18px; 359 text-align: center; 360 font-size: 14px; 361 line-height: 1; 362 font-weight: 300; 363 color: var(--accent, #2f6f9f); 364} 365 366#audTerritoryMap .aud-project-detail { 367 padding: 0 18px 13px 0; 368 overflow: hidden; 369} 370 371#audTerritoryMap .aud-project-location { 372 margin-bottom: 7px; 373 font-size: 8px; 374 letter-spacing: 1.1px; 375 text-transform: uppercase; 376 opacity: .62; 377} 378 379#audTerritoryMap .aud-project-placeholder { 380 margin: 0 0 9px; 381 font-size: 9px; 382 line-height: 1.5; 383 letter-spacing: .7px; 384 text-transform: uppercase; 385 opacity: .42; 386} 387 388#audTerritoryMap .aud-project-open { 389 display: inline-block; 390 font-size: 8px; 391 letter-spacing: 1px; 392 text-transform: uppercase; 393 color: var(--accent, #2f6f9f) !important; 394 text-decoration: none !important; 395 border-bottom: 1px solid currentColor; 396 padding-bottom: 2px; 397} 398 399#audTerritoryMap .aud-project-open:hover { 400 opacity: .7; 401} 402 403#audTerritoryMap .aud-project-empty { 404 padding: 16px 0; 405 font-size: 8px; 406 letter-spacing: 1px; 407 opacity: .5; 408} 409 410/* Dark-mode compatibility without altering the map itself. */ 411#audTerritoryMap[data-theme="dark"] .aud-project-card { 412 border-top-color: rgba(255,255,255,.14); 413} 414 415 416/* V0.6 â LIVE PROJECT PREVIEWS 417 Fetches only when a card is opened; map/SVG data are untouched. */ 418#audTerritoryMap .aud-project-detail{ 419 display:grid; 420 grid-template-columns:76px minmax(0,1fr); 421 gap:12px; 422 padding:0 0 14px; 423 overflow:hidden; 424} 425#audTerritoryMap .aud-project-thumb-wrap{ 426 width:76px;height:76px;overflow:hidden; 427 background:var(--line,#d5d8d5); 428 display:flex;align-items:center;justify-content:center; 429} 430#audTerritoryMap .aud-project-thumb{ 431 display:block;width:100%;height:100%;object-fit:cover;filter:saturate(.78); 432} 433#audTerritoryMap .aud-project-loading, 434#audTerritoryMap .aud-project-no-image{ 435 font-size:6px;letter-spacing:1px;opacity:.45;text-align:center; 436} 437#audTerritoryMap .aud-project-copy{min-width:0} 438#audTerritoryMap .aud-project-description{ 439 margin:0 0 9px;font-size:9px;line-height:1.45;letter-spacing:.02em;opacity:.78; 440} 441#audTerritoryMap .aud-project-location{ 442 margin:0 0 6px;font-size:7px;line-height:1.2;letter-spacing:1px;text-transform:uppercase;opacity:.6; 443} 444#audTerritoryMap .aud-project-open{ 445 display:inline-block;font-size:8px;letter-spacing:1px;text-transform:uppercase; 446 color:var(--accent,#2f6f9f)!important;text-decoration:none!important; 447 border-bottom:1px solid currentColor;padding-bottom:2px; 448} 449@media(max-width:520px){ 450#audTerritoryMap .aud-project-detail{grid-template-columns:64px minmax(0,1fr);gap:10px} 451#audTerritoryMap .aud-project-thumb-wrap{width:64px;height:64px} 452} 453 454 455/* V0.6.3 â CARD OVERFLOW / IMAGE FILTER FIXES */ 456#audTerritoryMap .aud-project-detail{ 457 display:grid; 458 grid-template-columns:76px minmax(0,1fr); 459 align-items:start; 460 gap:12px; 461 padding:0 0 15px; 462 overflow:visible; 463} 464#audTerritoryMap .aud-project-copy{ 465 min-width:0; 466 overflow:visible; 467} 468#audTerritoryMap .aud-project-description{ 469 margin:0 0 10px; 470 font-size:9px; 471 line-height:1.5; 472 letter-spacing:.01em; 473 overflow-wrap:anywhere; 474 word-break:normal; 475 white-space:normal; 476} 477#audTerritoryMap .aud-project-open{ 478 display:inline-block; 479 position:relative; 480 clear:both; 481 margin-top:2px; 482 font-size:8px; 483 line-height:1.25; 484 letter-spacing:1px; 485 text-transform:uppercase; 486} 487#audTerritoryMap .aud-project-thumb-wrap{ 488 flex:0 0 76px; 489 width:76px; 490 height:76px; 491 overflow:hidden; 492} 493#audTerritoryMap .aud-project-thumb{ 494 width:100%; 495 height:100%; 496 object-fit:cover; 497 display:block; 498} 499 500 501/* V0.6.3 â project-card robustness only */ 502#audTerritoryMap .aud-project-card, 503#audTerritoryMap .aud-project-row, 504#audTerritoryMap .aud-project-detail, 505#audTerritoryMap .aud-project-copy { 506 min-width:0; 507 max-width:100%; 508} 509 510#audTerritoryMap .aud-project-description { 511 overflow-wrap:anywhere; 512 word-break:normal; 513} 514 515#audTerritoryMap .aud-project-no-image { 516 display:flex; 517 width:100%; 518 height:100%; 519 align-items:center; 520 justify-content:center; 521 padding:6px; 522 text-align:center; 523} 524 525/* ============================================================ 526 A+UD TERRITORY MAP v0.7A 527 STATIC MUNICIPALITY OVERVIEW + SCROLLING PROJECT INDEX 528 MUNICIPALITY SVG FOOTPRINT 529 ============================================================ */ 530 531#audTerritoryMap .side{ 532 display:flex; 533 flex-direction:column; 534 min-height:0; 535 overflow:hidden; 536} 537 538#audTerritoryMap .side-static{ 539 flex:0 0 auto; 540 min-width:0; 541} 542 543#audTerritoryMap .side-scroll{ 544 flex:1 1 auto; 545 min-height:0; 546 overflow-y:auto; 547 overflow-x:hidden; 548 padding-right:4px; 549 scrollbar-width:thin; 550 scrollbar-color:var(--line-bright) transparent; 551} 552 553#audTerritoryMap .side-scroll::-webkit-scrollbar{ 554 width:6px; 555} 556 557#audTerritoryMap .side-scroll::-webkit-scrollbar-track{ 558 background:transparent; 559} 560 561#audTerritoryMap .side-scroll::-webkit-scrollbar-thumb{ 562 background:var(--line-bright); 563 border-radius:999px; 564} 565 566#audTerritoryMap .side-scroll-rule{ 567 margin-top:25px; 568} 569 570#audTerritoryMap .side-summary-grid{ 571 display:grid; 572 grid-template-columns:minmax(0,1fr) 108px; 573 gap:16px; 574 align-items:end; 575 min-width:0; 576} 577 578#audTerritoryMap .side-summary-copy{ 579 min-width:0; 580} 581 582#audTerritoryMap .municipality-footprint{ 583 width:108px; 584 height:92px; 585 align-self:end; 586 display:flex; 587 align-items:center; 588 justify-content:center; 589 border:1px solid var(--line); 590 overflow:hidden; 591 position:relative; 592 background:rgba(127,127,127,.025); 593} 594 595#audTerritoryMap .municipality-footprint svg{ 596 display:block; 597 width:88%; 598 height:82%; 599 overflow:visible; 600} 601 602#audTerritoryMap .municipality-footprint path{
603 fill:var(--blue-soft); 604 stroke:var(--blue); 605 stroke-width:1.15; 606 vector-effect:non-scaling-stroke; 607 filter:drop-shadow(0 0 4px var(--glow)); 608} 609 610#audTerritoryMap .municipality-footprint-empty{ 611 color:var(--muted); 612 opacity:.55; 613 font-size:6px; 614 line-height:1.45; 615 letter-spacing:.11em; 616 text-align:center; 617} 618 619@media(max-width:1100px){ 620 #audTerritoryMap .side-summary-grid{ 621 grid-template-columns:minmax(0,1fr) 88px; 622 gap:12px; 623 } 624 625 #audTerritoryMap .municipality-footprint{ 626 width:88px; 627 height:78px; 628 } 629} 630 631@media(max-width:800px){ 632 #audTerritoryMap .side{ 633 overflow:hidden; 634 max-height:720px; 635 } 636 637 #audTerritoryMap .side-scroll{ 638 max-height:420px; 639 } 640} 641 642 643/* ============================================================ 644 A+UD TERRITORY MAP v0.7B 645 FULL-BLEED VIEWPORT + NON-OVERLAPPING CARD HEADERS 646 ============================================================ */ 647 648/* Break cleanly out of an Elementor boxed/content-width container. */ 649#audTerritoryMap{ 650 width:100vw !important; 651 max-width:none !important; 652 margin-left:calc(50% - 50vw) !important; 653 margin-right:calc(50% - 50vw) !important; 654} 655 656/* Title owns the first row; category gets its own line below it. */ 657#audTerritoryMap .aud-project-row{ 658 grid-template-columns:minmax(0,1fr) 18px !important; 659 grid-template-rows:auto auto !important; 660 column-gap:9px !important; 661 row-gap:4px !important; 662 align-items:start !important; 663} 664#audTerritoryMap .aud-project-title{ 665 grid-column:1; 666 grid-row:1; 667 min-width:0; 668 overflow:hidden; 669 overflow-wrap:anywhere; 670 word-break:normal; 671 white-space:normal; 672 padding-right:2px; 673} 674#audTerritoryMap .aud-project-meta{ 675 grid-column:1; 676 grid-row:2; 677 width:auto !important; 678 max-width:100% !important; 679 min-width:0; 680 overflow:hidden; 681 text-overflow:ellipsis; 682 white-space:nowrap; 683 text-align:left !important; 684 font-size:7px; 685 line-height:1.2; 686 letter-spacing:.85px; 687 text-transform:uppercase; 688 opacity:.58; 689} 690#audTerritoryMap .aud-project-toggle{ 691 grid-column:2; 692 grid-row:1 / span 2; 693 align-self:start; 694 justify-self:end; 695} 696 697/* Preview data is intentionally compact and never allowed to escape the card. */ 698#audTerritoryMap .aud-project-detail{ 699 min-width:0; 700 max-width:100%; 701} 702#audTerritoryMap .aud-project-copy, 703#audTerritoryMap .aud-project-description{ 704 min-width:0; 705 max-width:100%; 706} 707#audTerritoryMap .aud-project-description{ 708 overflow-wrap:anywhere; 709 white-space:normal; 710} 711 712 713/* ============================================================ 714 v0.9A â ACCORDION COLLAPSE + STATIC HEADER CLEANUP 715 The project detail uses display:grid when open, so explicitly 716 honor the hidden attribute when a card is collapsed. 717 ============================================================ */ 718#audTerritoryMap .aud-project-detail[hidden]{ 719 display:none !important; 720} 721 722 723/* ============================================================ 724 v0.9A â THUMBNAIL ALWAYS VISIBLE 725 Thumbnail is independent of the collapsible description. 726 ============================================================ */ 727 728#audTerritoryMap .aud-project-card > .aud-project-thumb-wrap{ 729 display:block !important; 730 width:76px; 731 height:76px; 732 margin:0 0 10px 0; 733 overflow:hidden; 734 flex:none; 735} 736 737#audTerritoryMap .aud-project-card > .aud-project-thumb-wrap .aud-project-thumb{ 738 display:block; 739 width:100%; 740 height:100%; 741 object-fit:cover; 742} 743 744/* Only the textual/detail portion collapses. */ 745#audTerritoryMap .aud-project-detail[hidden]{ 746 display:none !important; 747} 748 749/* Since the thumbnail now lives outside detail, detail itself is text-only. */ 750#audTerritoryMap .aud-project-detail:not([hidden]){ 751 display:block; 752 padding:0 0 14px 0; 753} 754 755/* Keep preview image compact on mobile too. */ 756@media(max-width:520px){ 757 #audTerritoryMap .aud-project-card > .aud-project-thumb-wrap{ 758 width:64px; 759 height:64px; 760 } 761} 762 763 764/* ============================================================ 765 v0.9A â ISLANDWIDE / REGIONAL ACTIVE SCOPE 766 ============================================================ */ 767#audTerritoryMap .recent-title .scope-label{ 768 color:var(--muted); 769 opacity:.55; 770 transition:color .15s ease,opacity .15s ease,text-shadow .15s ease; 771} 772 773#audTerritoryMap .recent-title .scope-label.is-active{ 774 color:var(--blue); 775 opacity:1; 776 text-shadow:0 0 6px var(--glow); 777} 778 779#audTerritoryMap .recent-title .scope-sep{ 780 color:var(--muted); 781 opacity:.45; 782} 783 784 785/* ============================================================ 786 A+UD TERRITORY MAP v0.9A â PROJECT TYPE + LABEL CARDS 787 ============================================================ */ 788#audTerritoryMap .project-scope-bar{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0;margin-bottom:10px} 789#audTerritoryMap .scope-mode{min-width:0;white-space:nowrap} 790#audTerritoryMap .project-type-control{display:flex;align-items:center;gap:6px;margin-left:auto;min-width:0;color:var(--muted);font-size:6px;letter-spacing:.09em;white-space:nowrap} 791#audTerritoryMap .project-type-control>span{opacity:.65} 792#audTerritoryMap .project-type-control select{width:126px;max-width:126px;height:24px;padding:0 20px 0 7px;border:1px solid var(--line);border-radius:0;background:transparent;color:var(--blue);font-family:inherit;font-size:6px;letter-spacing:.06em;text-transform:uppercase;outline:none;cursor:pointer} 793#audTerritoryMap .project-type-control select:hover,#audTerritoryMap .project-type-control select:focus{border-color:var(--blue)} 794#audTerritoryMap .project-type-control select option{color:#222;background:#fff} 795 796#audTerritoryMap .aud-project-card{border-top:1px solid var(--line);padding:0;min-width:0} 797#audTerritoryMap .aud-project-row{width:100%;min-width:0;display:grid;grid-template-columns:58px minmax(0,1fr) 18px;gap:10px;align-items:center;padding:8px 0;margin:0;border:0!important;background:transparent!important;box-shadow:none!important;color:inherit;text-align:left;cursor:pointer} 798#audTerritoryMap .aud-project-row:hover .aud-project-title{color:var(--blue)} 799#audTerritoryMap .aud-project-label{min-width:0;align-self:center} 800#audTerritoryMap .aud-project-title{display:block;min-width:0;margin:0;color:var(--text);font-size:9px;line-height:1.28;letter-spacing:.025em;white-space:normal;overflow-wrap:anywhere} 801#audTerritoryMap .aud-project-meta{display:block;width:auto;max-width:100%;margin-top:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:left;color:var(--blue);font-size:6px;line-height:1.2;letter-spacing:.08em;text-transform:uppercase;opacity:.78} 802#audTerritoryMap .aud-project-row .aud-project-thumb-wrap{grid-column:1;grid-row:1;width:58px;height:58px;margin:0;overflow:hidden;border:1px solid var(--line);background:rgba(127,127,127,.08);display:flex!important;align-items:center;justify-content:center} 803#audTerritoryMap .aud-project-row .aud-project-thumb{width:100%;height:100%;display:block;object-fit:cover} 804#audTerritoryMap .aud-project-toggle{align-self:center;width:18px;min-width:18px;text-align:center;font-size:14px;line-height:1;color:var(--blue)} 805#audTerritoryMap .aud-project-card>.aud-project-thumb-wrap{display:none!important} 806#audTerritoryMap .aud-project-detail{display:block;padding:0 0 12px 68px;min-width:0} 807#audTerritoryMap .aud-project-detail[hidden]{display:none!important} 808#audTerritoryMap .aud-project-copy{min-width:0;max-width:100%} 809#audTerritoryMap .aud-project-description{margin:0 0 8px;font-size:8px;line-height:1.5;overflow-wrap:anywhere}
810#audTerritoryMap .aud-project-open{font-size:7px} 811@media(max-width:1100px){#audTerritoryMap .project-type-control>span{display:none}#audTerritoryMap .project-type-control select{width:108px;max-width:108px}} 812@media(max-width:800px){#audTerritoryMap .project-scope-bar{align-items:flex-start}#audTerritoryMap .aud-project-row{grid-template-columns:54px minmax(0,1fr) 18px;gap:9px}#audTerritoryMap .aud-project-row .aud-project-thumb-wrap{width:54px;height:54px}#audTerritoryMap .aud-project-detail{padding-left:63px}} 813 814 815/* ============================================================ 816 A+UD TERRITORY MAP v0.9A 817 REFINED PROJECT TYPE PICKER + LABEL CARDS 818 ============================================================ */ 819 820/* ---------- scope bar ---------- */ 821#audTerritoryMap .project-scope-bar{ 822 position:relative; 823 display:flex; 824 align-items:center; 825 justify-content:space-between; 826 gap:10px; 827 min-width:0; 828 margin-bottom:9px; 829 overflow:visible; 830} 831 832#audTerritoryMap .scope-mode{ 833 flex:0 0 auto; 834 min-width:0; 835} 836 837/* ---------- PROJECT TYPE control ---------- */ 838#audTerritoryMap .project-type-picker{ 839 position:relative; 840 flex:0 0 auto; 841 z-index:25; 842} 843 844#audTerritoryMap .project-type-trigger{ 845 width:104px; 846 height:25px; 847 display:grid; 848 grid-template-columns:minmax(0,1fr) 14px; 849 align-items:center; 850 gap:4px; 851 padding:0 6px 0 8px; 852 border:1px solid var(--line)!important; 853 border-radius:0!important; 854 background:transparent!important; 855 box-shadow:none!important; 856 color:var(--blue)!important; 857 font-family:inherit; 858 cursor:pointer; 859} 860 861#audTerritoryMap .project-type-trigger:hover, 862#audTerritoryMap .project-type-picker.is-open .project-type-trigger{ 863 border-color:var(--blue)!important; 864 box-shadow:0 0 10px rgba(60,120,168,.08)!important; 865} 866 867#audTerritoryMap .project-type-trigger-label{ 868 overflow:hidden; 869 text-overflow:ellipsis; 870 white-space:nowrap; 871 text-align:left; 872 font-size:6px; 873 line-height:1; 874 letter-spacing:.1em; 875} 876 877#audTerritoryMap .project-type-trigger-mark{ 878 color:var(--blue); 879 font-size:10px; 880 line-height:1; 881 text-align:right; 882 transition:transform .18s ease; 883} 884 885#audTerritoryMap .project-type-picker.is-open .project-type-trigger-mark{ 886 transform:rotate(180deg); 887} 888 889/* popup */ 890#audTerritoryMap .project-type-menu{ 891 position:absolute; 892 top:calc(100% + 5px); 893 right:0; 894 width:218px; 895 max-height:188px; 896 overflow-y:auto; 897 overflow-x:hidden; 898 padding:4px; 899 border:1px solid var(--blue); 900 background:var(--panel); 901 box-shadow: 902 0 10px 24px rgba(0,0,0,.12), 903 0 0 18px rgba(60,120,168,.08); 904 opacity:0; 905 visibility:hidden; 906 pointer-events:none; 907 transform:translateY(-5px); 908 transition: 909 opacity .16s ease, 910 transform .16s ease, 911 visibility .16s linear; 912 scrollbar-width:thin; 913 scrollbar-color:var(--line-bright) transparent; 914} 915 916#audTerritoryMap .project-type-picker.is-open .project-type-menu{ 917 opacity:1; 918 visibility:visible; 919 pointer-events:auto; 920 transform:translateY(0); 921} 922 923#audTerritoryMap .project-type-menu:before{ 924 content:"PROJECT TYPE / INDEX"; 925 display:block; 926 padding:6px 7px 7px; 927 margin-bottom:2px; 928 border-bottom:1px solid var(--line); 929 color:var(--muted); 930 font-size:6px; 931 letter-spacing:.12em; 932} 933 934#audTerritoryMap .project-type-option{ 935 width:100%; 936 min-height:27px; 937 display:grid; 938 grid-template-columns:22px minmax(0,1fr) 12px; 939 gap:5px; 940 align-items:center; 941 padding:5px 7px; 942 border:0!important; 943 border-bottom:1px solid rgba(127,127,127,.10)!important; 944 border-radius:0!important; 945 background:transparent!important; 946 box-shadow:none!important; 947 color:var(--muted)!important; 948 text-align:left; 949 cursor:pointer; 950} 951 952#audTerritoryMap .project-type-option:hover, 953#audTerritoryMap .project-type-option[aria-selected="true"]{ 954 color:var(--blue)!important; 955 background:var(--blue-soft)!important; 956} 957 958#audTerritoryMap .project-type-option-index{ 959 opacity:.45; 960 font-size:6px; 961 letter-spacing:.08em; 962} 963 964#audTerritoryMap .project-type-option-label{ 965 min-width:0; 966 overflow-wrap:anywhere; 967 font-size:6px; 968 line-height:1.25; 969 letter-spacing:.055em; 970 text-transform:uppercase; 971} 972 973#audTerritoryMap .project-type-option-active{ 974 text-align:right; 975 font-size:5px; 976 color:var(--blue); 977} 978 979/* ---------- compact project labels ---------- */ 980#audTerritoryMap .aud-project-card{ 981 position:relative; 982 min-width:0; 983 padding:0!important; 984 border-top:1px solid var(--line)!important; 985 background:transparent!important; 986} 987 988#audTerritoryMap .aud-project-row{ 989 width:100%!important; 990 min-width:0!important; 991 display:grid!important; 992 grid-template-columns:48px 1px minmax(0,1fr) 18px!important; 993 gap:9px!important; 994 align-items:center!important; 995 min-height:64px; 996 padding:7px 0!important; 997 margin:0!important; 998 border:0!important; 999 border-radius:0!important; 1000 background:transparent!important; 1001 box-shadow:none!important; 1002 color:inherit!important; 1003 text-align:left!important; 1004 cursor:pointer; 1005} 1006 1007#audTerritoryMap .aud-project-row:before{ 1008 content:""; 1009 grid-column:2; 1010 width:1px; 1011 height:28px; 1012 align-self:center; 1013 background:var(--line); 1014} 1015 1016#audTerritoryMap .aud-project-row:hover{ 1017 background:transparent!important; 1018} 1019 1020#audTerritoryMap .aud-project-row:hover .aud-project-title{ 1021 color:var(--blue); 1022} 1023 1024#audTerritoryMap .aud-project-row .aud-project-thumb-wrap{ 1025 grid-column:1!important; 1026 grid-row:1!important; 1027 width:48px!important; 1028 height:48px!important; 1029 margin:0!important; 1030 display:flex!important; 1031 align-items:center!important; 1032 justify-content:center!important; 1033 overflow:hidden!important; 1034 border:1px solid var(--line)!important; 1035 background:rgba(127,127,127,.05)!important; 1036} 1037 1038#audTerritoryMap .aud-project-row .aud-project-thumb{ 1039 display:block!important; 1040 width:100%!important; 1041 height:100%!important; 1042 object-fit:cover!important; 1043} 1044 1045#audTerritoryMap .aud-project-label{ 1046 grid-column:3!important; 1047 min-width:0!important; 1048 align-self:center!important; 1049} 1050 1051#audTerritoryMap .aud-project-title{ 1052 display:block!important; 1053 min-width:0!important; 1054 width:100%!important; 1055 margin:0!important; 1056 color:var(--text)!important; 1057 font-size:9px!important; 1058 line-height:1.28!important; 1059 letter-spacing:.018em!important; 1060 white-space:normal!important; 1061 overflow-wrap:anywhere!important; 1062 word-break:normal!important; 1063} 1064 1065#audTerritoryMap .aud-project-meta{ 1066 display:block!important; 1067 width:100%!important; 1068 max-width:100%!important; 1069 margin:4px 0 0!important; 1070 color:var(--blue)!important; 1071 opacity:.66!important; 1072 overflow:hidden!important; 1073 text-overflow:ellipsis!important; 1074 white-space:nowrap!important; 1075 text-align:left!important; 1076 font-size:5.5px!important; 1077 line-height:1.2!important; 1078 letter-spacing:.075em!important; 1079 text-transform:uppercase!important; 1080} 1081 1082#audTerritoryMap .aud-project-toggle{ 1083 grid-column:4!important; 1084 width:18px!important; 1085 min-width:18px!important; 1086 align-self:center!important; 1087 text-align:center!important; 1088 color:var(--blue)!important; 1089 font-size:13px!important; 1090 font-weight:300!important; 1091 line-height:1!important; 1092} 1093 1094/* expanded content: a quiet technical drawer, not a blank card */ 1095#audTerritoryMap .aud-project-detail{ 1096 display:block!important; 1097 max-height:0; 1098 min-width:0!important; 1099 overflow:hidden!important; 1100 opacity:0; 1101 transform:translateY(-4px); 1102 padding:0 0 0 59px!important; 1103 margin:0!important; 1104 border-left:1px solid transparent; 1105 transition: 1106 max-height .28s cubic-bezier(.22,.61,.36,1), 1107 opacity .18s ease, 1108 transform .22s ease, 1109 padding .22s ease; 1110} 1111 1112#audTerritoryMap .aud-project-card.is-open .aud-project-detail{ 1113 max-height:560px; 1114 opacity:1; 1115 transform:translateY(0); 1116 padding:2px 0 13px 59px!important; 1117} 1118 1119#audTerritoryMap .aud-project-copy{ 1120 position:relative; 1121 min-width:0!important; 1122 max-width:100%!important; 1123 padding:9px 9px 8px 12px; 1124 border-left:1px solid var(--blue); 1125 background: 1126 linear-gradient(90deg,var(--blue-soft),transparent 72%); 1127} 1128 1129#audTerritoryMap .aud-project-copy:before{ 1130 content:""; 1131 position:absolute; 1132 left:-1px; 1133 top:0; 1134 width:11px; 1135 height:1px; 1136 background:var(--blue); 1137} 1138 1139#audTerritoryMap .aud-project-description{ 1140 margin:0 0 8px!important; 1141 color:var(--text)!important; 1142 opacity:.76!important; 1143 font-size:8px!important; 1144 line-height:1.5!important; 1145 letter-spacing:.01em!important; 1146 overflow-wrap:anywhere!important; 1147} 1148 1149#audTerritoryMap .aud-project-open{ 1150 display:inline-block!important; 1151 margin-top:1px!important; 1152 color:var(--blue)!important; 1153 font-size:6px!important; 1154 line-height:1.2!important; 1155 letter-spacing:.1em!important; 1156} 1157 1158/* kill old persistent external-thumb rules */ 1159#audTerritoryMap .aud-project-card > .aud-project-thumb-wrap{ 1160 display:none!important; 1161} 1162 1163@media(max-width:1100px){ 1164 #audTerritoryMap .project-type-trigger{width:94px}
1165 #audTerritoryMap .project-type-menu{width:200px} 1166} 1167 1168@media(max-width:800px){ 1169 #audTerritoryMap .aud-project-row{ 1170 grid-template-columns:46px 1px minmax(0,1fr) 18px!important; 1171 gap:8px!important; 1172 } 1173 #audTerritoryMap .aud-project-row .aud-project-thumb-wrap{ 1174 width:46px!important; 1175 height:46px!important; 1176 } 1177 #audTerritoryMap .aud-project-detail, 1178 #audTerritoryMap .aud-project-card.is-open .aud-project-detail{ 1179 padding-left:55px!important; 1180 } 1181} 1182 1183 1184/* ============================================================ 1185 v0.9A â PROJECT TYPE FIX + QUIETER DETAILS 1186 ============================================================ */ 1187 1188/* Quiet expanded details: no blue wash/gradient. */ 1189#audTerritoryMap .aud-project-copy{ 1190 background:transparent!important; 1191 border-left:1px solid var(--line-bright)!important; 1192 box-shadow:none!important; 1193} 1194 1195#audTerritoryMap .aud-project-copy:before{ 1196 background:var(--blue)!important; 1197 opacity:.55; 1198} 1199 1200/* Keep picker above the scrolling project list and allow its menu to escape. */ 1201#audTerritoryMap .side-scroll{ 1202 position:relative; 1203 overflow-y:auto; 1204 overflow-x:visible!important; 1205} 1206 1207#audTerritoryMap .project-scope-bar{ 1208 position:sticky; 1209 top:0; 1210 z-index:80; 1211 overflow:visible!important; 1212 background:var(--panel); 1213} 1214 1215#audTerritoryMap .project-type-picker{ 1216 position:relative; 1217 z-index:100; 1218} 1219 1220#audTerritoryMap .project-type-menu{ 1221 z-index:110; 1222} 1223 1224 1225/* ============================================================ 1226 A+UD TERRITORY MAP v0.9A â COMPACT PAGE FIT 1227 Tune ONLY these two values if the site header/footer heights change. 1228 ============================================================ */ 1229#audTerritoryMap{ 1230 --site-chrome-reserve:150px; /* combined space reserved for site header + footer */ 1231 --map-shell-max:760px; 1232 1233 height:min(var(--map-shell-max), calc(100svh - var(--site-chrome-reserve)))!important; 1234 min-height:540px!important; 1235 max-height:var(--map-shell-max)!important; 1236 overflow:hidden!important; 1237} 1238 1239/* Slightly tighter internal chrome; map/sidebar proportions remain intact. */ 1240#audTerritoryMap{ 1241 --topbar-height:56px; 1242 --footer-height:62px; 1243 --page-padding:10px 16px 9px; 1244} 1245 1246#audTerritoryMap .side{ 1247 padding:20px 22px 16px!important; 1248} 1249 1250#audTerritoryMap .eyebrow{ 1251 margin-top:18px!important; 1252} 1253 1254#audTerritoryMap .rule{ 1255 margin:18px 0!important; 1256} 1257 1258#audTerritoryMap .stats{ 1259 margin-top:18px!important; 1260} 1261 1262@media(max-width:800px){ 1263 #audTerritoryMap{ 1264 height:auto!important; 1265 min-height:100vh!important; 1266 max-height:none!important; 1267 overflow:visible!important; 1268 } 1269} 1270 1271 1272/* ============================================================ 1273 A+UD TERRITORY MAP v0.10M 1274 DESKTOP HEADERLESS PAGE + EXACT A+UD LOGO + PROJECT ORBIT 1275 ============================================================ */ 1276 1277/* Hide ONLY the website header on the page containing this map. 1278 The site footer is deliberately left alone. */ 1279@media(min-width:801px){ 1280 body:has(#audTerritoryMap) .elementor-location-header, 1281 body:has(#audTerritoryMap) [data-elementor-type="header"], 1282 body:has(#audTerritoryMap) header.site-header, 1283 body:has(#audTerritoryMap) #masthead.site-header{ 1284 display:none!important; 1285 } 1286 1287 body:has(#audTerritoryMap){ 1288 margin-top:0!important; 1289 } 1290 1291 body:has(#audTerritoryMap) .site-content, 1292 body:has(#audTerritoryMap) main.site-main, 1293 body:has(#audTerritoryMap) .elementor-location-single{ 1294 margin-top:0!important; 1295 padding-top:0!important; 1296 } 1297} 1298 1299/* Use the REAL A+UD logo asset so the typography/geometry is exact. */ 1300#audTerritoryMap .brand{ 1301 align-items:center!important; 1302 gap:18px!important; 1303 overflow:visible; 1304} 1305 1306#audTerritoryMap .brand-home{ 1307 display:flex; 1308 align-items:center; 1309 flex:0 0 auto; 1310 color:inherit; 1311 text-decoration:none!important; 1312 line-height:0; 1313} 1314 1315#audTerritoryMap .brand-logo{ 1316 display:block; 1317 width:190px; 1318 height:auto; 1319 max-width:none!important; 1320 object-fit:contain; 1321 transform:translateY(1px); 1322 filter:grayscale(1) brightness(0); 1323 opacity:.96; 1324 transition:opacity .15s ease; 1325} 1326 1327#audTerritoryMap[data-theme="dark"] .brand-logo{ 1328 filter:grayscale(1) brightness(0) invert(1); 1329} 1330 1331#audTerritoryMap .brand-home:hover .brand-logo{ 1332 opacity:.68; 1333} 1334 1335/* Reposition ONLY the map topbar brand zone. 1336 Map body/sidebar/card scale remains untouched. */ 1337@media(min-width:1101px){ 1338 #audTerritoryMap .topbar{ 1339 padding-left:58px!important; 1340 grid-template-columns:260px minmax(0,1fr) auto!important; 1341 } 1342} 1343 1344@media(min-width:801px) and (max-width:1100px){ 1345 #audTerritoryMap .topbar{ 1346 padding-left:28px!important; 1347 grid-template-columns:205px minmax(0,1fr) auto!important; 1348 } 1349 1350 #audTerritoryMap .brand-logo{ 1351 width:92px; 1352 } 1353} 1354 1355@media(max-width:800px){ 1356 #audTerritoryMap .brand-logo{ 1357 width:88px; 1358 } 1359} 1360 1361 1362 1363/* ============================================================ 1364 A+UD TERRITORY MAP v0.10M â LOOSE TRUE ORBIT / ORTHOGONAL CARDS 1365 Isolated overlay: existing map/sidebar/left-click UI untouched. 1366 ============================================================ */ 1367 1368#audTerritoryMap .aud-orbit-layer{ 1369 position:absolute; 1370 inset:0; 1371 z-index:24; 1372 pointer-events:none; 1373 overflow:hidden; 1374} 1375 1376#audTerritoryMap .aud-orbit-field{ 1377 position:absolute; 1378 width:1px; 1379 height:1px; 1380 pointer-events:none; 1381} 1382 1383#audTerritoryMap .aud-orbit-field:before, 1384#audTerritoryMap .aud-orbit-field:after{ 1385 content:""; 1386 position:absolute; 1387 left:0; 1388 top:0; 1389 border:1px solid var(--blue); 1390 border-radius:50%; 1391 transform:translate(-50%,-50%) scale(.72); 1392 opacity:0; 1393 filter:blur(.15px); 1394 animation:audOrbitFieldIn .34s ease forwards, audOrbitFieldBreathe 3.8s ease-in-out .34s infinite; 1395} 1396 1397#audTerritoryMap .aud-orbit-field:before{ 1398 width:178px; 1399 height:178px; 1400} 1401 1402#audTerritoryMap .aud-orbit-field:after{ 1403 width:238px; 1404 height:238px; 1405 opacity:0;
1406 animation-delay:.07s,.41s; 1407} 1408 1409#audTerritoryMap .aud-orbit-node{ 1410 --orbit-x:0px; 1411 --orbit-y:0px; 1412 --drift-x:2px; 1413 --drift-y:-3px; 1414 --drift-r:1deg; 1415 position:absolute; 1416 left:0; 1417 top:0; 1418 width:58px; 1419 height:58px; 1420 padding:0; 1421 margin:0; 1422 border:1px solid color-mix(in srgb,var(--blue) 42%,transparent); 1423 background:var(--panel); 1424 box-shadow:0 0 16px color-mix(in srgb,var(--glow) 24%,transparent); 1425 transform:translate(-50%,-50%) translate(var(--orbit-x),var(--orbit-y)) scale(.18); 1426 opacity:0; 1427 overflow:visible; 1428 cursor:crosshair; 1429 pointer-events:auto; 1430 appearance:none; 1431 -webkit-appearance:none; 1432 animation: 1433 audOrbitDeploy .34s cubic-bezier(.22,.72,.25,1) forwards, 1434 audOrbitFloat 4.6s ease-in-out .34s infinite alternate; 1435 animation-delay:var(--deploy-delay,0ms),calc(var(--deploy-delay,0ms) + 340ms); 1436 transition: 1437 opacity .18s ease, 1438 filter .22s ease, 1439 border-color .18s ease, 1440 box-shadow .22s ease; 1441} 1442 1443#audTerritoryMap .aud-orbit-node img{ 1444 display:block; 1445 width:100%; 1446 height:100%; 1447 object-fit:cover; 1448 filter:grayscale(1) saturate(0) contrast(.88) brightness(.92); 1449 opacity:.48; 1450 transform:scale(1.01); 1451 transition:filter .2s ease,opacity .2s ease,transform .22s cubic-bezier(.22,.72,.25,1); 1452} 1453 1454#audTerritoryMap .aud-orbit-node.is-no-image{ 1455 display:flex; 1456 align-items:center; 1457 justify-content:center; 1458 color:var(--muted); 1459 font-size:6px; 1460 line-height:1.25; 1461 letter-spacing:.08em; 1462 text-align:center; 1463} 1464 1465#audTerritoryMap .aud-orbit-node:after{ 1466 content:attr(data-orbit-index); 1467 position:absolute; 1468 right:-1px; 1469 bottom:-11px; 1470 color:var(--muted); 1471 font-size:5px; 1472 letter-spacing:.08em; 1473 opacity:.52; 1474} 1475 1476#audTerritoryMap .aud-orbit-node .aud-orbit-title{ 1477 position:absolute; 1478 left:50%; 1479 bottom:calc(100% + 8px); 1480 width:max-content; 1481 max-width:180px; 1482 padding:4px 6px; 1483 transform:translate(-50%,4px); 1484 border-left:1px solid var(--blue); 1485 background:color-mix(in srgb,var(--panel) 88%,transparent); 1486 color:var(--text); 1487 font-size:6px; 1488 line-height:1.35; 1489 letter-spacing:.08em; 1490 text-transform:uppercase; 1491 opacity:0; 1492 pointer-events:none; 1493 transition:opacity .16s ease,transform .18s ease; 1494} 1495 1496#audTerritoryMap .aud-orbit-field.has-hover .aud-orbit-node:not(.is-hovered){ 1497 opacity:.18!important; 1498 filter:blur(.65px); 1499} 1500 1501#audTerritoryMap .aud-orbit-node.is-hovered{ 1502 z-index:5; 1503 opacity:1!important; 1504 border-color:var(--blue); 1505 box-shadow:0 0 10px var(--glow),0 0 28px color-mix(in srgb,var(--glow) 42%,transparent); 1506 animation-play-state:paused; 1507} 1508 1509#audTerritoryMap .aud-orbit-node.is-hovered img{ 1510 filter:grayscale(0) saturate(1) contrast(1) brightness(1); 1511 opacity:1; 1512 transform:scale(1.09); 1513} 1514 1515#audTerritoryMap .aud-orbit-node.is-hovered .aud-orbit-title{ 1516 opacity:1; 1517 transform:translate(-50%,0); 1518} 1519 1520#audTerritoryMap .aud-orbit-layer.is-releasing .aud-orbit-node{ 1521 pointer-events:none; 1522 animation:none!important; 1523 opacity:0!important; 1524 transform:translate(-50%,-50%) translate(0,0) scale(.12)!important; 1525 transition:opacity .18s ease,transform .22s cubic-bezier(.55,0,1,.45)!important; 1526} 1527 1528#audTerritoryMap .aud-orbit-layer.is-releasing .aud-orbit-field:before, 1529#audTerritoryMap .aud-orbit-layer.is-releasing .aud-orbit-field:after{ 1530 animation:none!important; 1531 opacity:0!important; 1532 transform:translate(-50%,-50%) scale(.35)!important; 1533 transition:opacity .15s ease,transform .2s ease!important; 1534} 1535 1536#audTerritoryMap.orbit-active .map-svg .municipality:not(.active){ 1537 opacity:.28; 1538} 1539 1540@keyframes audOrbitDeploy{ 1541 0%{ 1542 opacity:0; 1543 transform:translate(-50%,-50%) translate(0,0) scale(.12); 1544 } 1545 70%{opacity:.72} 1546 100%{ 1547 opacity:.62; 1548 transform:translate(-50%,-50%) translate(var(--orbit-x),var(--orbit-y)) scale(1); 1549 } 1550} 1551 1552@keyframes audOrbitFloat{ 1553 0%{ 1554 transform:translate(-50%,-50%) 1555 translate(var(--orbit-x),var(--orbit-y)) 1556 translate(calc(var(--drift-x) * -1),calc(var(--drift-y) * -1)) 1557 rotate(calc(var(--drift-r) * -1)) 1558 scale(1); 1559 } 1560 50%{ 1561 transform:translate(-50%,-50%) 1562 translate(var(--orbit-x),var(--orbit-y)) 1563 translate(var(--drift-x),var(--drift-y)) 1564 rotate(var(--drift-r)) 1565 scale(1.018); 1566 } 1567 100%{ 1568 transform:translate(-50%,-50%) 1569 translate(var(--orbit-x),var(--orbit-y)) 1570 translate(calc(var(--drift-x) * -.35),calc(var(--drift-y) * .45)) 1571 rotate(calc(var(--drift-r) * -.4)) 1572 scale(.992); 1573 } 1574} 1575 1576@keyframes audOrbitFieldIn{ 1577 to{opacity:.13;transform:translate(-50%,-50%) scale(1)} 1578} 1579 1580@keyframes audOrbitFieldBreathe{ 1581 0%,100%{opacity:.08;filter:blur(.2px)} 1582 50%{opacity:.16;filter:blur(.65px)} 1583} 1584 1585@media(max-width:800px){ 1586 #audTerritoryMap .aud-orbit-layer{display:none!important} 1587} 1588 1589@media(prefers-reduced-motion:reduce){ 1590 #audTerritoryMap .aud-orbit-node, 1591 #audTerritoryMap .aud-orbit-field:before, 1592 #audTerritoryMap .aud-orbit-field:after{ 1593 animation-duration:.01ms!important; 1594 animation-iteration-count:1!important; 1595 } 1596} 1597 1598 1599 1600/* ============================================================ 1601 v0.10A â ORGANIC / LIVING PROJECT ORBIT 1602 No visible orbital circles. Project nodes drift organically. 1603 ============================================================ */ 1604
1605/* Field remains as an invisible coordinate origin. */ 1606#audTerritoryMap .aud-orbit-field:before, 1607#audTerritoryMap .aud-orbit-field:after{ 1608 display:none!important; 1609} 1610 1611/* Slower, wider "bubble / ant in a bowl" motion. */ 1612#audTerritoryMap .aud-orbit-node{ 1613 animation: 1614 audOrbitDeploy .38s cubic-bezier(.22,.72,.25,1) forwards, 1615 audOrbitFloatAlive var(--float-duration,8.4s) ease-in-out 1616 calc(var(--deploy-delay,0ms) + 380ms) infinite alternate!important; 1617 transform-origin:center; 1618} 1619 1620#audTerritoryMap .aud-orbit-node.is-hovered{ 1621 animation-play-state:paused!important; 1622} 1623 1624/* Organic multi-axis drift; intentionally not circular/clockwise. */ 1625@keyframes audOrbitFloatAlive{ 1626 0%{ 1627 transform: 1628 translate(-50%,-50%) 1629 translate(var(--orbit-x),var(--orbit-y)) 1630 translate(calc(var(--drift-x) * -.70),calc(var(--drift-y) * -.45)) 1631 rotate(calc(var(--drift-r) * -.55)) 1632 scale(.992); 1633 } 1634 1635 22%{ 1636 transform: 1637 translate(-50%,-50%) 1638 translate(var(--orbit-x),var(--orbit-y)) 1639 translate(calc(var(--drift-x) * .25),calc(var(--drift-y) * -1)) 1640 rotate(calc(var(--drift-r) * .20)) 1641 scale(1.012); 1642 } 1643 1644 47%{ 1645 transform: 1646 translate(-50%,-50%) 1647 translate(var(--orbit-x),var(--orbit-y)) 1648 translate(var(--drift-x),calc(var(--drift-y) * .18)) 1649 rotate(var(--drift-r)) 1650 scale(1.022); 1651 } 1652 1653 72%{ 1654 transform: 1655 translate(-50%,-50%) 1656 translate(var(--orbit-x),var(--orbit-y)) 1657 translate(calc(var(--drift-x) * -.18),var(--drift-y)) 1658 rotate(calc(var(--drift-r) * -.18)) 1659 scale(1.004); 1660 } 1661 1662 100%{ 1663 transform: 1664 translate(-50%,-50%) 1665 translate(var(--orbit-x),var(--orbit-y)) 1666 translate(calc(var(--drift-x) * -.92),calc(var(--drift-y) * .42)) 1667 rotate(calc(var(--drift-r) * -.72)) 1668 scale(.986); 1669 } 1670} 1671 1672 1673 1674/* v0.10A â expanded control legend without increasing footer height */ 1675#audTerritoryMap .controls{ 1676 gap:8px 11px!important; 1677} 1678 1679#audTerritoryMap .controls .bottom-value{ 1680 font-size:6px!important; 1681 white-space:nowrap; 1682} 1683 1684#audTerritoryMap .controls .key{ 1685 font-size:6px!important; 1686 padding:2px 4px!important; 1687} 1688 1689 1690/* ============================================================ 1691 v0.10M â LOOSE TRUE ORBIT / ORTHOGONAL CARDS 1692 The constellation rotates as a loose field by X/Y coordinates. 1693 NO card, image, or title rotation is used anywhere. 1694 ============================================================ */ 1695 1696/* Hide any residual visual orbit guides. */ 1697#audTerritoryMap .aud-orbit-field:before, 1698#audTerritoryMap .aud-orbit-field:after{ 1699 display:none!important; 1700} 1701 1702/* JS owns X/Y. CSS owns only deployment scale/opacity. */ 1703#audTerritoryMap .aud-orbit-node{ 1704 /* JS owns left/top so each project can occupy its own orbit position. */ 1705 left:0; 1706 top:0; 1707 1708 --orbit-x:0px!important; 1709 --orbit-y:0px!important; 1710 --drift-r:0deg!important; 1711 1712 transform:translate(-50%,-50%) scale(1); 1713 animation:none!important; 1714 1715 /* Smooth deployment / release without introducing rotation. */ 1716 transition: 1717 opacity .18s ease, 1718 filter .22s ease, 1719 border-color .18s ease, 1720 box-shadow .22s ease, 1721 scale .22s cubic-bezier(.22,.72,.25,1)!important; 1722} 1723 1724/* Explicitly prohibit rotation on every visual part. */ 1725#audTerritoryMap .aud-orbit-node, 1726#audTerritoryMap .aud-orbit-node img, 1727#audTerritoryMap .aud-orbit-title, 1728#audTerritoryMap .aud-orbit-face{ 1729 rotate:0deg!important; 1730} 1731 1732#audTerritoryMap .aud-orbit-node{ 1733 scale:.16; 1734 opacity:0; 1735} 1736 1737#audTerritoryMap .aud-orbit-node.is-deployed{ 1738 scale:1; 1739 opacity:.62; 1740} 1741 1742#audTerritoryMap .aud-orbit-node.is-hovered{ 1743 scale:1.06; 1744 opacity:1!important; 1745} 1746 1747/* Title is attached to the upright card and only fades/slides vertically. */ 1748#audTerritoryMap .aud-orbit-node .aud-orbit-title{ 1749 transform:translate(-50%,4px)!important; 1750} 1751 1752#audTerritoryMap .aud-orbit-node.is-hovered .aud-orbit-title{ 1753 transform:translate(-50%,0)!important; 1754} 1755 1756/* Release collapses toward center; JS zeroes position first. */ 1757#audTerritoryMap .aud-orbit-layer.is-releasing .aud-orbit-node{ 1758 opacity:0!important; 1759 scale:.14!important; 1760 transition: 1761 opacity .17s ease, 1762 scale .20s cubic-bezier(.55,0,1,.45)!important; 1763} 1764 1765 1766/* ============================================================
1767 v0.10M â ORBIT POSITION FIX 1768 Inline JS left/top values must be allowed to move each card. 1769 ============================================================ */ 1770#audTerritoryMap .aud-orbit-field > .aud-orbit-node{ 1771 /* intentionally NOT !important */ 1772 position:absolute; 1773} 1774 1775 1776/* ============================================================ 1777 v0.10M â ELEMENTOR FULL-WIDTH / LEFT-CROP FIX 1778 Use a full-width Elementor parent; do not negative-breakout this map. 1779 ============================================================ */ 1780#audTerritoryMap{ 1781 width:100% !important; 1782 max-width:none !important; 1783 margin-left:0 !important; 1784 margin-right:0 !important; 1785} 1786 1787/* Prevent the Elementor widget/container itself from clipping the full-width map. */ 1788.elementor-widget-html:has(#audTerritoryMap), 1789.elementor-widget-html:has(#audTerritoryMap) > .elementor-widget-container, 1790.e-con:has(#audTerritoryMap), 1791.elementor-section:has(#audTerritoryMap), 1792.elementor-container:has(#audTerritoryMap){ 1793 max-width:none !important; 1794 overflow:visible !important; 1795} 1796 1797/* Let the closed PROJECT TYPE button show longer selected subtype names cleanly. */ 1798#audTerritoryMap .project-type-trigger{ 1799 width:auto !important; 1800 min-width:104px; 1801 max-width:190px; 1802} 1803 1804#audTerritoryMap .project-type-trigger-label{ 1805 max-width:160px; 1806} 1807 1808 1809/* ============================================================ 1810 v0.10M â RESTORE FULL VIEWPORT SIZE WITHOUT LEFT CROP 1811 Keep the large map. Center a true viewport-width shell from 1812 inside Elementor instead of using negative breakout margins. 1813 ============================================================ */ 1814 1815#audTerritoryMap{ 1816 width:100vw !important; 1817 max-width:100vw !important; 1818 1819 position:relative !important; 1820 left:50% !important; 1821 transform:translateX(-50%) !important; 1822 1823 margin-left:0 !important; 1824 margin-right:0 !important; 1825} 1826 1827/* Elementor wrappers must allow the viewport-width child to escape. */ 1828.elementor-widget-html:has(#audTerritoryMap), 1829.elementor-widget-html:has(#audTerritoryMap) > .elementor-widget-container, 1830.e-con:has(#audTerritoryMap), 1831.elementor-section:has(#audTerritoryMap), 1832.elementor-container:has(#audTerritoryMap){ 1833 overflow:visible !important; 1834 max-width:none !important; 1835} 1836 1837/* Prevent an ancestor from introducing a horizontal scrollbar/crop 1838 because the child is intentionally viewport-wide. */ 1839body:has(#audTerritoryMap), 1840html:has(#audTerritoryMap){ 1841 overflow-x:clip !important; 1842} 1843 1844 1845/* ============================================================ 1846 v0.10M â CURSOR INTERACTION TARGETS 1847 ============================================================ */ 1848#audTerritoryMap .map-svg .municipality{ 1849 cursor:pointer!important; 1850} 1851 1852#audTerritoryMap .aud-project-row, 1853#audTerritoryMap .aud-project-row *{ 1854 cursor:pointer!important; 1855} 1856 1857 1858/* ============================================================ 1859 v0.10M â CUSTOM CURSOR CLEANUP 1860 Removes native browser focus rectangle + hand cursor. 1861 ============================================================ */ 1862 1863#audTerritoryMap .map-svg .municipality{ 1864 outline:none!important; 1865} 1866 1867@media (hover:hover) and (pointer:fine){ 1868 #audTerritoryMap .map-svg .municipality, 1869 #audTerritoryMap .aud-project-row, 1870 #audTerritoryMap .aud-project-row *, 1871 #audTerritoryMap .aud-svg-marker{ 1872 cursor:none!important; 1873 } 1874} 1875 1876 1877/* ============================================================ 1878 v0.10M â HOMEPAGE-STYLE HEADER SPACING 1879 Header-only adjustment. Map/sidebar/orbit/filter UI untouched. 1880 ============================================================ */ 1881 1882#audTerritoryMap .topbar{ 1883 padding-top:48px !important; 1884} 1885 1886/* Align the internal A+UD logo more like the homepage header. */ 1887#audTerritoryMap .brand{ 1888 margin-left:22px !important; 1889} 1890 1891 1892/* ============================================================ 1893 v0.10M â FINAL HEADER FIT 1894 Keep the extra top breathing room, but contain the ENTIRE 1895 header inside its own row so nothing bleeds into the map frame. 1896 ============================================================ */ 1897 1898#audTerritoryMap{ 1899 --topbar-height:104px !important; 1900} 1901 1902/* Header now owns the full 104px; all controls stay inside it. */ 1903#audTerritoryMap .topbar{ 1904 height:var(--topbar-height) !important; 1905 min-height:var(--topbar-height) !important; 1906 padding-top:36px !important; 1907 padding-bottom:10px !important; 1908 align-items:center !important; 1909} 1910 1911/* Keep the logo nudge from v0.10M. */ 1912#audTerritoryMap .brand{ 1913 margin-left:22px !important; 1914} 1915 1916/* Center nav/actions vertically within the enlarged header row. */ 1917#audTerritoryMap .filters, 1918#audTerritoryMap .top-actions{ 1919 align-self:stretch !important; 1920 display:flex !important; 1921 align-items:center !important; 1922} 1923 1924/* Existing active underline should sit inside the header, 1925 not against the map-frame boundary. */ 1926#audTerritoryMap .filter.active:after{ 1927 bottom:11px !important; 1928} 1929 1930/* Map content starts AFTER the header row. */ 1931#audTerritoryMap .content{ 1932 min-height:0 !important; 1933} 1934 1935/* Preserve compact desktop page fit by using the already-existing 1936 map shell height; only redistribute its internal rows. */ 1937@media(max-width:1100px) and (min-width:801px){ 1938 #audTerritoryMap{ 1939 --topbar-height:94px !important; 1940 } 1941 1942 #audTerritoryMap .topbar{ 1943 padding-top:30px !important; 1944 padding-bottom:8px !important; 1945 } 1946} 1947 1948@media(max-width:800px){ 1949 /* Leave the established mobile header behavior alone. */ 1950 #audTerritoryMap{ 1951 --topbar-height:56px !important; 1952 } 1953 1954 #audTerritoryMap .topbar{ 1955 height:auto !important; 1956 min-height:0 !important; 1957 padding-top:0 !important; 1958 padding-bottom:0 !important; 1959 } 1960 1961 #audTerritoryMap .brand{ 1962 margin-left:0 !important; 1963 } 1964} 1965 1966 1967/* ============================================================ 1968 v0.10M â FINAL NAV UNDERLINE POSITION 1969 Keep the active blue bar directly beneath ALL / ARCHITECTURE / 1970 URBAN DESIGN instead of down at the bottom of the taller header. 1971 ============================================================ */ 1972 1973#audTerritoryMap .filter{ 1974 position:relative !important; 1975} 1976 1977#audTerritoryMap .filter.active:after{ 1978 bottom:-7px !important; 1979 left:0 !important; 1980 right:0 !important; 1981} 1982
1983#audTerritoryMap .aud-map-svg-error{ 1984 position:absolute; 1985 inset:0; 1986 display:flex; 1987 align-items:center; 1988 justify-content:center; 1989 color:var(--muted); 1990 font-size:8px; 1991 letter-spacing:.12em; 1992 text-align:center; 1993} 1994 1995</style> <style id="site-designer-logo-constraints"> 1996 .wp-block-site-logo.is-default-size img { 1997 width: auto; 1998 height: auto; 1999 max-width: 180px; 2000 max-height: 80px; 2001 } 2002 .wp-block-site-logo img { 2003 height: auto; 2004 } 2005 </style> 2006 <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" /></head> 2007<body class="wp-singular page-template page-template-elementor_canvas page page-id-1271 wp-embed-responsive wp-theme-hello-elementor angie-default hello-elementor-default elementor-default elementor-template-canvas elementor-kit-6 elementor-page elementor-page-1271"> 2008 <e-page-transition preloader-type="image" preloader-image-url="" class="e-page-transition--entering" exclude="^https\:\/\/audpr\.com\/wp\-admin\/"> 2009 </e-page-transition> 2010 <div data-elementor-type="wp-page" data-elementor-id="1271" class="elementor elementor-1271" data-elementor-post-type="page"> 2011 <div class="elementor-element elementor-element-c9eac0f e-con-full e-flex e-con e-parent" data-id="c9eac0f" data-element_type="container" data-e-type="container"> 2012 <div class="elementor-element elementor-element-97bad39 elementor-widget__width-inherit elementor-widget elementor-widget-template" data-id="97bad39" data-element_type="widget" data-e-type="widget" data-widget_type="template.default"> 2013 <div class="elementor-widget-container"> 2014 <div class="elementor-template"> 2015 <div data-elementor-type="container" data-elementor-id="4116" class="elementor elementor-4116" data-elementor-post-type="elementor_library"> 2016 <div class="elementor-element elementor-element-389177b7 e-flex e-con-boxed e-con e-parent" data-id="389177b7" data-element_type="container" data-e-type="container"> 2017 <div class="e-con-inner"> 2018 <div class="elementor-element elementor-element-57877b05 e-con-full e-flex e-con e-child" data-id="57877b05" data-element_type="container" data-e-type="container" id="aud-header"> 2019 <div class="elementor-element elementor-element-2b8d9346 e-con-full e-flex e-con e-child" data-id="2b8d9346" data-element_type="container" data-e-type="container"> 2020 <div class="elementor-element elementor-element-46fb2c59 e-con-full e-flex e-con e-child" data-id="46fb2c59" data-element_type="container" data-e-type="container"> 2021 <div class="elementor-element elementor-element-4fded952 elementor-widget elementor-widget-image" data-id="4fded952" data-element_type="widget" data-e-type="widget" data-widget_type="image.default"> 2022 <a href="https://audpr.com/home_new/"> 2023 <img fetchpriority="high" decoding="async" width="799" height="181" src="https://audpr.com/wp-content/uploads/2025/07/AUD-LOGO-COLOR-SHORT_Act.png" class="attachment-full size-full wp-image-560" alt="" srcset="https://audpr.com/wp-content/uploads/2025/07/AUD-LOGO-COLOR-SHORT_Act.png 799w, https://audpr.com/wp-content/uploads/2025/07/AUD-LOGO-COLOR-SHORT_Act-300x68.png 300w, https://audpr.com/wp-content/uploads/2025/07/AUD-LOGO-COLOR-SHORT_Act-768x174.png 768w" sizes="(max-width: 799px) 100vw, 799px" /> </a> 2024 </div> 2025 </div> 2026 <div class="elementor-element elementor-element-339caf74 e-con-full e-flex e-con e-child" data-id="339caf74" data-element_type="container" data-e-type="container"> 2027 <div class="elementor-element elementor-element-3d913e40 close-modal-img elementor-widget elementor-widget-image" data-id="3d913e40" data-element_type="widget" data-e-type="widget" data-widget_type="image.default"> 2028 <a href="https://audpr.com/home_new/"> 2029 <img decoding="async" width="166" height="164" src="https://audpr.com/wp-content/uploads/2025/07/cruz-2.png" class="attachment-full size-full wp-image-743" alt="" /> </a> 2030 </div> 2031 </div> 2032 </div> 2033 </div> 2034 <div class="elementor-element elementor-element-7928e4c2 e-con-full e-flex e-con e-child" data-id="7928e4c2" data-element_type="container" data-e-type="container" id="aud-contact-stage"> 2035 <div class="elementor-element elementor-element-4fe8bc8a e-con-full e-flex e-con e-child" data-id="4fe8bc8a" data-element_type="container" data-e-type="container" id="aud-contact-card"> 2036 <div class="elementor-element elementor-element-1233b00f elementor-hidden-tablet elementor-widget elementor-widget-spacer" data-id="1233b00f" data-element_type="widget" data-e-type="widget" data-widget_type="spacer.default"> 2037 <div class="elementor-spacer"> 2038 <div class="elementor-spacer-inner"></div> 2039 </div> 2040 </div> 2041 <div class="elementor-element elementor-element-2b70cdc9 elementor-widget elementor-widget-heading" data-id="2b70cdc9" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default"> 2042 <h2 class="elementor-heading-title elementor-size-default">CONTACT US</h2> </div> 2043 <div class="elementor-element elementor-element-4d31e1e9 elementor-widget elementor-widget-heading" data-id="4d31e1e9" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default"> 2044 <h2 class="elementor-heading-title elementor-size-default">57 Quisqueya St, Suite 300</h2> </div> 2045 <div class="elementor-element elementor-element-daabe1c elementor-widget elementor-widget-heading" data-id="daabe1c" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default"> 2046 <h2 class="elementor-heading-title elementor-size-default">San Juan, Puerto Rico</h2> </div> 2047 <div class="elementor-element elementor-element-365237e4 elementor-widget elementor-widget-heading" data-id="365237e4" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default"> 2048 <h2 class="elementor-heading-title elementor-size-default">00917-1202</h2> </div> 2049 <div class="elementor-element elementor-element-5cb9c085 elementor-widget elementor-widget-heading" data-id="5cb9c085" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default"> 2050 <h2 class="elementor-heading-title elementor-size-default"><b>T</b>: 787.622.0354</h2> </div> 2051 <div class="elementor-element elementor-element-6098658a elementor-widget elementor-widget-heading" data-id="6098658a" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default"> 2052 <h2 class="elementor-heading-title elementor-size-default"><b>F</b>: 787.754.9718</h2> </div> 2053 <div class="elementor-element elementor-element-4de246c1 elementor-widget elementor-widget-heading" data-id="4de246c1" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default"> 2054 <h2 class="elementor-heading-title elementor-size-default"><b>E</b>: [email protected]</h2> </div> 2055 <div class="elementor-element elementor-element-6a882361 elementor-shape-circle e-grid-align-left elementor-grid-0 elementor-widget elementor-widget-social-icons" data-id="6a882361" data-element_type="widget" data-e-type="widget" data-widget_type="social-icons.default"> 2056 <div class="elementor-social-icons-wrapper elementor-grid"> 2057 <span class="elementor-grid-item"> 2058 <a class="elementor-icon elementor-social-icon elementor-social-icon-facebook elementor-repeater-item-bc00fa8" href="https://www.facebook.com/infoarchud" target="_blank"> 2059 <span class="elementor-screen-only">Facebook</span> 2060 <svg aria-hidden="true" class="e-font-icon-svg e-fab-facebook" viewB
2060ox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="M504 256C504 119 393 8 256 8S8 119 8 256c0 123.78 90.69 226.38 209.25 245V327.69h-63V256h63v-54.64c0-62.15 37-96.48 93.67-96.48 27.14 0 55.52 4.84 55.52 4.84v61h-31.28c-30.8 0-40.41 19.12-40.41 38.73V256h68.78l-11 71.69h-57.78V501C413.31 482.38 504 379.78 504 256z"></path></svg> </a> 2061 </span> 2062 </div> 2063 </div> 2064 <div class="elementor-element elementor-element-639cd0ba elementor-button-align-start elementor-widget elementor-widget-form" data-id="639cd0ba" data-element_type="widget" data-e-type="widget" data-settings="{"step_next_label":"Next","step_previous_label":"Previous","button_width":"20","step_type":"number_text","step_icon_shape":"circle"}" data-widget_type="form.default"> 2065 <form class="elementor-form" method="post" name="New Form" aria-label="New Form"> 2066 <input type="hidden" name="post_id" value="4116"/> 2067 <input type="hidden" name="form_id" value="639cd0ba"/> 2068 <input type="hidden" name="referer_title" value="CONTACT US - A+UD" /> 2069 2070 <input type="hidden" name="queried_id" value="1271"/> 2071 2072 <div class="elementor-form-fields-wrapper elementor-labels-above"> 2073 <div class="elementor-field-type-text elementor-field-group elementor-column elementor-field-group-name elementor-col-100"> 2074 <label for="form-field-name" class="elementor-field-label"> 2075 Name: </label> 2076 <input size="1" type="text" name="form_fields[name]" id="form-field-name" class="elementor-field elementor-size-sm elementor-field-textual" placeholder="Enter Name"> 2077 </div> 2078 <div class="elementor-field-type-email elementor-field-group elementor-column elementor-field-group-email elementor-col-100 elementor-field-required"> 2079 <label for="form-field-email" class="elementor-field-label"> 2080 E-mail: </label> 2081 <input size="1" type="email" name="form_fields[email]" id="form-field-email" class="elementor-field elementor-size-sm elementor-field-textual" placeholder="Enter Email" required="required"> 2082 </div> 2083 <div class="elementor-field-type-textarea elementor-field-group elementor-column elementor-field-group-message elementor-col-100"> 2084 <label for="form-field-message" class="elementor-field-label"> 2085 Message: </label> 2086 <textarea class="elementor-field-textual elementor-field elementor-size-sm" name="form_fields[message]" id="form-field-message" rows="4" placeholder="Enter Your Message"></textarea> </div> 2087 <div class="elementor-field-type-turnstile elementor-field-group elementor-column elementor-field-group-field_0fa3667 elementor-col-100 elementor-field-required"> 2088 <label for="form-field-field_0fa3667" class="elementor-field-label"> 2089 </label> 2090 <div class="cf-turnstile cwebts-widget" data-sitekey="0x4AAAAAAE6enH4LKWzHMChe" data-theme="light" data-size="flexible" data-appearance="interaction-only" data-language="auto" data-action="elementor_form"></div> </div> 2091 <div class="elementor-field-group elementor-column elementor-field-type-submit elementor-col-20 e-form__buttons"> 2092 <button class="elementor-button elementor-size-md" type="submit"> 2093 <span class="elementor-button-content-wrapper"> 2094 <span class="elementor-button-text">SUBMIT</span> 2095 </span> 2096 </button> 2097 </div> 2098 </div> 2099 </form> 2100 </div> 2101 </div> 2102 <div class="elementor-element elementor-element-54cc53 e-con-full e-flex e-con e-child" data-id="54cc53" data-element_type="container" data-e-type="container" id="aud-contact-map"> 2103 <div class="elementor-element elementor-element-5089ea6f elementor-hidden-tablet elementor-widget elementor-widget-spacer" data-id="5089ea6f" data-element_type="widget" data-e-type="widget" data-widget_type="spacer.default"> 2104 <div class="elementor-spacer"> 2105 <div class="elementor-spacer-inner"></div> 2106 </div> 2107 </div> 2108 <div class="elementor-element elementor-element-64814f58 elementor-widget elementor-widget-aud_snazzy_google_maps" data-id="64814f58" data-element_type="widget" data-e-type="widget" data-widget_type="aud_snazzy_google_maps.default"> 2109 <div class="elementor-widget-container"> 2110 <div class="aud-sgm-shell" style="--aud-sgm-bg:#f1efeb"><div id="aud-sgm-64814f58" class="aud-sgm-map" data-aud-sgm="{"address":"San Juan, Puerto Rico","fallbackLat":18.2208,"fallbackLng":-66.5901,"lat":18.427476105127198,"lng":-66.05625650485807,"zoom":13,"tabletZoom":null,"mobileZoom":null,"focalPoint":{"desktop":{"x":58,"y":55},"tablet":{"x":50,"y":50},"mobile":{"x":50,"y":50}},"mapTypeId":"roadmap","heading":0,"tilt":0,"styleMode":"json","colors":{"geometry":"#ebe8e2","water":"#b9c4c8","road":"#ffffff","roadStroke":"#d8d3cc","highway":"#e2ddd5","park":"#d7ddd2","poi":"#dedbd5","transit":"#d2cdc5","administrative":"#c5c0b8","label":"#5b5752","labelHalo":"#f5f2ed"},"visibility":{"labels":true,"roadLabels":true,"poi":true,"business":false,"transit":false,"parks":true,"landscape":true,"administrative":true},"desktopFillViewport":true,"controls":{"zoomControl":true,"streetViewControl":false,"fullscreenControl":false,"mapTypeControl":false,"scaleControl":false,"rotateControl":false,"scrollwheel":true,"draggable":true,"disableDoubleClickZoom":false,"keyboardShortcuts":true,"gestureHandling":"greedy"},"primaryMarker":{"show":true,"image":"","color":"#5D6DC6","size":38,"title":"","description":"","link":"","newTab":false,"action":"directions","desktopTrigger":"dblclick","touchTrigger":"direct"},"mobilePresentation":{"sizeMode":"responsive_height","aspectRatio":null,"widthSize":150,"widthUnit":null,"align":null,"hideControls":true,"contactComposition":true},"pois":[],"fitToMarkers":false,"customJson":"[\r\n {\r\n \"featureType\": \"all\",\r\n \"elementType\": \"labels\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"administrative\",\r\n \"elementType\": \"labels\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"administrative\",\r\n \"elementType\": \"labels.text.fill\",\r\n \"stylers\": [\r\n {\r\n \"color\": \"#444444\"\r\n },\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"administrative.neighborhood\",\r\n \"elementType\": \"labels\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"landscape\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"on\"\r\n },\r\n {\r\n \"color\": \"#e0dfe0\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"landscape\",\r\n \"elementType\": \"labels\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"poi\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"poi\",\r\n \"elementType\": \"labels\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"poi.park\",\r\n \"elementType\": \"geometry\",\r\n \"stylers\": [\r\n {\r\n \"color\": \"#a8a9a8\"\r\n },\r\n {\r\n \"visibility\": \"on\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"road\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"saturation\": -100\r\n },\r\n {\r\n \"lightness\": 45\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"road\",\r\n \"elementType\": \"geometry.fill\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"on\"\r\n },\r\n {\r\n \"color\": \"#5b5b5a\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"road\",\r\n \"elementType\": \"labels\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"on\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"road.highway\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"simplified\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"road.highway\",\r\n \"elementType\": \"labels\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"road.arterial\",\r\n \"elementType\": \"labels.icon\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"transit\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"transit\",\r\n \"elementType\": \"labels\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"water\",\r\n \"elementType\": \"all\",\r\n \"stylers\": [\r\n {\r\n \"color\": \"#f7f7f7\"\r\n },\r\n {\r\n \"visibility\": \"on\"\r\n }\r\n ]\r\n },\r\n {\r\n \"featureType\": \"water\",\r\n \"elementType\": \"labels\",\r\n \"stylers\": [\r\n {\r\n \"visibility\": \"off\"\r\n }\r\n ]\r\n }\r\n]\r\n","background":"#f1efeb","minZoom":null,"maxZoom":null,"restriction":null}"></div>
2110<div class="aud-sgm-status" aria-live="polite" hidden></div></div> </div> 2111 </div> 2112 </div> 2113 </div> 2114 </div> 2115 </div> 2116 </div> 2117 </div> 2118 </div> 2119 </div> 2120 </div> 2121 </div> 2122
2122<script type="speculationrules"> 2123{"prefetch":[{"source":"document","where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":["/wp-*.php","/wp-admin/*","/wp-content/uploads/*","/wp-content/*","/wp-content/plugins/*","/wp-content/themes/hello-elementor/*","/*\\?(.+)"]}},{"not":{"selector_matches":"a[rel~=\"nofollow\"]"}},{"not":{"selector_matches":".no-prefetch, .no-prefetch a"}}]},"eagerness":"conservative"}]} 2124</script>
2124 2125<style> 2126#audpr-plus { 2127 --audpr-cursor-color: #8e8277; 2128 2129 position: fixed; 2130 z-index: 2147483647; 2131 pointer-events: none; 2132 2133 width: 21px; 2134 height: 21px; 2135 2136 left: 0; 2137 top: 0; 2138 2139 opacity: 0; 2140 2141 transform: translate(-50%, -50%) rotate(0deg); 2142} 2143 2144#audpr-plus::before, 2145#audpr-plus::after { 2146 content: ""; 2147 position: absolute; 2148 2149 left: 50%; 2150 top: 50%; 2151 2152 background: var(--audpr-cursor-color); 2153 2154 transform: translate(-50%, -50%); 2155} 2156 2157#audpr-plus::before { 2158 width: 21px; 2159 height: 6px; 2160} 2161 2162#audpr-plus::after { 2163 width: 6px; 2164 height: 21px; 2165} 2166 2167 2168/* Desktop / mouse devices only */ 2169@media (hover: hover) and (pointer: fine) { 2170 2171 html.audpr-loading-cursor, 2172 html.audpr-loading-cursor * { 2173 cursor: none !important; 2174 } 2175 2176 html.audpr-cursor, 2177 html.audpr-cursor * { 2178 cursor: none !important; 2179 } 2180 2181} 2182</style> 2183
2184<script> 2185document.addEventListener('DOMContentLoaded', function () { 2186 2187 /* 2188 * Do absolutely nothing on mobile/touch devices. 2189 */ 2190 var finePointer = window.matchMedia( 2191 '(hover: hover) and (pointer: fine)' 2192 ).matches; 2193 2194 if (!finePointer) { 2195 return; 2196 } 2197 2198 2199 var root = document.documentElement; 2200 2201 var c = document.createElement('div'); 2202 2203 var r = 0; 2204 var down = false; 2205 var lastX = 0; 2206 var moved = false; 2207 var linkPressed = false; 2208 2209 var snapTimer = null; 2210 var colorTimer = null; 2211 2212 2213 c.id = 'audpr-plus'; 2214 2215 document.body.appendChild(c); 2216 2217 2218 /* 2219 * Return cursor to normal resting color. 2220 */ 2221 function setRestingColor() { 2222 2223 clearTimeout(colorTimer); 2224 2225 c.style.setProperty( 2226 '--audpr-cursor-color', 2227 '#8e8277' 2228 ); 2229 } 2230 2231 2232 /* 2233 * Link click color. 2234 * 2235 * Explicit BLUE. 2236 * Only lasts 180ms. 2237 */ 2238 function setLinkColorBriefly() { 2239 2240 clearTimeout(colorTimer); 2241 2242 c.style.setProperty( 2243 '--audpr-cursor-color', 2244 '#0000ff' 2245 ); 2246 2247 colorTimer = setTimeout( 2248 setRestingColor, 2249 180 2250 ); 2251 } 2252 2253 2254 /* 2255 * Fast snapback to perfectly orthogonal. 2256 * 2257 * USED ONLY AFTER A DRAG 2258 * OR POINTER CANCEL. 2259 */ 2260 function resetTransform() { 2261 2262 clearTimeout(snapTimer); 2263 2264 r = 0; 2265 2266 c.style.transition = 2267 'transform .22s cubic-bezier(.22,.75,.25,1)'; 2268 2269 c.style.transform = 2270 'translate(-50%, -50%) rotate(0deg)'; 2271 2272 snapTimer = setTimeout(function () { 2273 2274 c.style.transition = 'none'; 2275 2276 }, 240); 2277 } 2278 2279 2280 /* 2281 * Show cursor. 2282 */ 2283 function show() { 2284 2285 root.classList.remove( 2286 'audpr-loading-cursor' 2287 ); 2288 2289 root.classList.add( 2290 'audpr-cursor' 2291 ); 2292 2293 c.style.opacity = '1'; 2294 2295 resetTransform(); 2296 } 2297 2298 2299 /* 2300 * ONLY the root homepage gets the 2301 * 9-second delay. 2302 * 2303 * "/" = https://audpr.com/ 2304 * 2305 * /home_new/ does NOT get the delay. 2306 */ 2307 var isRootHomepage = 2308 window.location.pathname === '/' || 2309 window.location.pathname === ''; 2310 2311 2312 if (isRootHomepage) { 2313 2314 /* 2315 * Hide the normal browser cursor 2316 * during the 9-second intro. 2317 */ 2318 root.classList.add( 2319 'audpr-loading-cursor' 2320 ); 2321 2322 setTimeout(show, 9000); 2323 2324 } else { 2325 2326 /* 2327 * Every other page loads the cursor immediately. 2328 */ 2329 show(); 2330 } 2331 2332 2333 /* 2334 * ======================================================== 2335 * POINTER MOVEMENT 2336 * ======================================================== 2337 */ 2338 2339 document.addEventListener( 2340 'pointermove', 2341 function (e) { 2342 2343 if (e.pointerType !== 'mouse') { 2344 return; 2345 } 2346 2347 2348 c.style.left = 2349 e.clientX + 'px'; 2350 2351 c.style.top = 2352 e.clientY + 'px'; 2353 2354 2355 /* 2356 * Rotation is ONLY possible while 2357 * the mouse button is held. 2358 */ 2359 if (down) { 2360 2361 var d = 2362 e.clientX - lastX; 2363 2364 2365 if (Math.abs(d) > 2) { 2366 moved = true; 2367 } 2368 2369 2370 if (moved) { 2371 2372 r += d * 0.8; 2373 2374 c.style.transition = 2375 'none'; 2376 2377 c.style.transform = 2378 'translate(-50%, -50%) rotate(' + 2379 r + 2380 'deg)'; 2381 } 2382 } 2383 2384 2385 lastX = e.clientX; 2386 2387 } 2388 ); 2389 2390 2391 /* 2392 * ======================================================== 2393 * MOUSE DOWN 2394 * ======================================================== 2395 */ 2396 2397 document.addEventListener( 2398 'pointerdown', 2399 function (e) { 2400 2401 if ( 2402 e.pointerType !== 'mouse' || 2403 e.button !== 0 2404 ) { 2405 return; 2406 } 2407 2408 2409 down = true; 2410 2411 moved = false;
2412 2413 lastX = e.clientX; 2414 2415 2416 /* 2417 * Is this a clickable link? 2418 */ 2419 linkPressed = 2420 !!e.target.closest('a[href]'); 2421 2422 2423 clearTimeout(snapTimer); 2424 clearTimeout(colorTimer); 2425 2426 2427 c.style.transition = 2428 'none'; 2429 2430 2431 /* 2432 * Link = brief BLUE flash. 2433 */ 2434 if (linkPressed) { 2435 2436 setLinkColorBriefly(); 2437 2438 } 2439 2440 } 2441 ); 2442 2443 2444 /* 2445 * ======================================================== 2446 * MOUSE UP 2447 * ======================================================== 2448 * 2449 * SIMPLE CLICK: 2450 * 360° clockwise spin. 2451 * 2452 * CLICK + DRAG: 2453 * Rotate during drag, 2454 * then snap back. 2455 */ 2456 2457 document.addEventListener( 2458 'pointerup', 2459 function (e) { 2460 2461 if ( 2462 e.pointerType !== 'mouse' || 2463 e.button !== 0 || 2464 !down 2465 ) { 2466 return; 2467 } 2468 2469 2470 down = false; 2471 2472 2473 /* 2474 * ================================================= 2475 * DRAG 2476 * ================================================= 2477 * 2478 * Snap back ONLY after actual movement. 2479 */ 2480 if (moved) { 2481 2482 resetTransform(); 2483 2484 2485 } else { 2486 2487 2488 /* 2489 * ================================================= 2490 * SIMPLE CLICK 2491 * ================================================= 2492 * 2493 * One clockwise 360° spin. 2494 * 2495 * NO resetTransform() afterward. 2496 * 2497 * Because r increases by exactly 360°, 2498 * the cursor finishes orthogonal. 2499 */ 2500 r += 360; 2501 2502 c.style.transition = 2503 'transform .38s cubic-bezier(.2,.8,.2,1)'; 2504 2505 c.style.transform = 2506 'translate(-50%, -50%) rotate(' + 2507 r + 2508 'deg)'; 2509 } 2510 2511 2512 moved = false; 2513 2514 linkPressed = false; 2515 2516 2517 /* 2518 * Make sure blue doesn't linger. 2519 */ 2520 setRestingColor(); 2521 2522 } 2523 ); 2524 2525 2526 /* 2527 * ======================================================== 2528 * SAFETY FALLBACK 2529 * ======================================================== 2530 */ 2531 2532 document.addEventListener( 2533 'pointercancel', 2534 function (e) { 2535 2536 if (e.pointerType !== 'mouse') { 2537 return; 2538 } 2539 2540 2541 down = false; 2542 2543 moved = false; 2544 2545 linkPressed = false; 2546 2547 2548 setRestingColor(); 2549 2550 resetTransform(); 2551 2552 } 2553 ); 2554 2555}); 2556</script>
2556 2557 2558<style> 2559 2560/* 2561 * ============================================================ 2562 * AUDPR â INTERACTIVE TEXT REVEAL 2563 * ============================================================ 2564 * 2565 * PRODUCTION VERSION 2566 * 2567 * Desktop mouse only. 2568 * Does not modify the existing #audpr-plus cursor. 2569 */ 2570 2571 2572/* 2573 * ============================================================ 2574 * PALETTE CONTROL 2575 * ============================================================ 2576 * 2577 * CHANGE ONLY THIS COLOR IN THE FUTURE. 2578 */ 2579 2580:root { 2581 --audpr-interactive-accent: #C7B8D9; 2582} 2583 2584 2585/* 2586 * ============================================================ 2587 * INTERACTIVE TEXT LAYER 2588 * ============================================================ 2589 */ 2590 2591#audpr-interactive-reveal { 2592 position: fixed !important; 2593 2594 pointer-events: none !important; 2595 2596 /* 2597 * Above the existing cross cursor. 2598 */ 2599 z-index: 2147483648 !important; 2600 2601 margin: 0 !important; 2602 padding: 0 !important; 2603 2604 background: transparent !important; 2605 2606 border: 0 !important; 2607 box-shadow: none !important; 2608 text-shadow: none !important; 2609 2610 white-space: nowrap !important; 2611 2612 display: block !important; 2613 2614 visibility: hidden; 2615 opacity: 0; 2616 2617 /* 2618 * FUTURE COLOR CONTROL. 2619 */ 2620 color: var(--audpr-interactive-accent) !important; 2621 2622 filter: none !important; 2623 mix-blend-mode: normal !important; 2624} 2625 2626 2627/* 2628 * Visible state. 2629 */ 2630#audpr-interactive-reveal.audpr-interactive-visible { 2631 visibility: visible; 2632 opacity: 1; 2633} 2634 2635</style> 2636 2637
2638<script> 2639document.addEventListener('DOMContentLoaded', function () { 2640 2641 2642 /* 2643 * ======================================================== 2644 * DESKTOP MOUSE ONLY 2645 * ======================================================== 2646 */ 2647 2648 if (!window.matchMedia( 2649 '(hover: hover) and (pointer: fine)' 2650 ).matches) { 2651 return; 2652 } 2653 2654 2655 /* 2656 * ======================================================== 2657 * CREATE VISUAL REVEAL 2658 * ======================================================== 2659 */ 2660 2661 var reveal = 2662 document.createElement('span'); 2663 2664 reveal.id = 2665 'audpr-interactive-reveal'; 2666 2667 reveal.setAttribute( 2668 'aria-hidden', 2669 'true' 2670 ); 2671 2672 document.body.appendChild( 2673 reveal 2674 ); 2675 2676 2677 /* 2678 * Currently active interactive element. 2679 */ 2680 var activeElement = null; 2681 2682 2683 /* 2684 * ======================================================== 2685 * HIDE 2686 * ======================================================== 2687 */ 2688 2689 function hide() { 2690 2691 reveal.classList.remove( 2692 'audpr-interactive-visible' 2693 ); 2694 2695 reveal.style.visibility = 2696 'hidden'; 2697 2698 reveal.style.opacity = 2699 '0'; 2700 2701 /* 2702 * Collapse the mask completely. 2703 */ 2704 reveal.style.clipPath = 2705 'polygon(0 0, 0 0, 0 0)'; 2706 2707 reveal.style.webkitClipPath = 2708 'polygon(0 0, 0 0, 0 0)'; 2709 } 2710 2711 2712 /* 2713 * ======================================================== 2714 * FIND INTERACTIVE ELEMENT 2715 * ======================================================== 2716 */ 2717 2718 function getInteractiveElement(element) { 2719 2720 if (!element) { 2721 return null; 2722 } 2723 2724 2725 var interactive = 2726 element.closest( 2727 'a[href], button, [role="button"]' 2728 ); 2729 2730 2731 if (!interactive) { 2732 return null; 2733 } 2734 2735 2736 /* 2737 * Must contain actual text. 2738 */ 2739 if ( 2740 !(interactive.textContent || '') 2741 .trim() 2742 ) { 2743 return null; 2744 } 2745 2746 2747 /* 2748 * Ignore disabled controls. 2749 */ 2750 if ( 2751 interactive.disabled || 2752 interactive.getAttribute( 2753 'aria-disabled' 2754 ) === 'true' 2755 ) { 2756 return null; 2757 } 2758 2759 2760 /* 2761 * Ignore hidden/off-screen Elementor 2762 * duplicate elements. 2763 */ 2764 var rect = 2765 interactive.getBoundingClientRect(); 2766 2767 2768 if ( 2769 rect.width <= 0 || 2770 rect.height <= 0 2771 ) { 2772 return null; 2773 } 2774 2775 2776 return interactive; 2777 } 2778 2779 2780 /* 2781 * ======================================================== 2782 * SYNCHRONIZE TEXT 2783 * ======================================================== 2784 */ 2785 2786 function sync(element) { 2787 2788 if (!element) { 2789 return null; 2790 } 2791 2792 2793 /* 2794 * Get the actual text bounds. 2795 */ 2796 var range = 2797 document.createRange(); 2798 2799 range.selectNodeContents( 2800 element 2801 ); 2802 2803 2804 var textRect = 2805 range.getBoundingClientRect(); 2806 2807 2808 /* 2809 * Fallback to the entire element. 2810 */ 2811 var elementRect = 2812 element.getBoundingClientRect(); 2813 2814 2815 var rect; 2816 2817 2818 if ( 2819 textRect.width > 0 && 2820 textRect.height > 0 2821 ) { 2822 rect = textRect; 2823 } else { 2824 rect = elementRect; 2825 } 2826 2827 2828 /* 2829 * Get Elementor typography. 2830 */ 2831 var style = 2832 window.getComputedStyle( 2833 element 2834 ); 2835 2836 2837 /* 2838 * Copy text. 2839 */ 2840 reveal.textContent = 2841 element.textContent.trim(); 2842 2843 2844 /* 2845 * Typography. 2846 */ 2847 reveal.style.fontFamily = 2848 style.fontFamily; 2849 2850 reveal.style.fontSize = 2851 style.fontSize; 2852 2853 reveal.style.fontWeight = 2854 style.fontWeight; 2855 2856 reveal.style.fontStyle = 2857 style.fontStyle; 2858 2859 reveal.style.fontVariant = 2860 style.fontVariant; 2861 2862 reveal.style.letterSpacing = 2863 style.letterSpacing; 2864 2865 reveal.style.lineHeight = 2866 style.lineHeight; 2867 2868 reveal.style.textTransform = 2869 style.textTransform; 2870 2871 reveal.style.textAlign = 2872 style.textAlign; 2873 2874 reveal.style.fontKerning = 2875 style.fontKerning; 2876 2877 reveal.style.fontFeatureSettings = 2878 style.fontFeatureSettings; 2879 2880 reveal.style.fontStretch = 2881 style.fontStretch; 2882 2883 2884 /* 2885 * Position over the actual text. 2886 */ 2887 reveal.style.left = 2888 rect.left + 'px'; 2889 2890 reveal.style.top = 2891 rect.top + 'px'; 2892 2893 2894 return rect; 2895 } 2896 2897 2898 /* 2899 * ======================================================== 2900 * POINTER TRACKING 2901 * ======================================================== 2902 */ 2903 2904 document.addEventListener( 2905 'pointermove', 2906 function (event) { 2907 2908 2909 /* 2910 * Mouse only. 2911 */ 2912 if ( 2913 event.pointerType !== 2914 'mouse' 2915 ) { 2916 return; 2917 } 2918 2919 2920 /* 2921 * Find the real element beneath 2922 * the cursor. 2923 */ 2924 var element = 2925 document.elementFromPoint( 2926 event.clientX, 2927 event.clientY 2928 ); 2929 2930 2931 /* 2932 * Find interactive parent. 2933 */ 2934 var interactive = 2935 getInteractiveElement( 2936 element 2937 ); 2938 2939 2940 /* 2941 * Nothing interactive. 2942 */ 2943 if (!interactive) { 2944 2945 activeElement = 2946 null; 2947 2948 hide(); 2949 2950 return; 2951 } 2952 2953 2954 activeElement = 2955 interactive; 2956 2957 2958 /* 2959 * Synchronize text position. 2960 */ 2961 var rect = 2962 sync( 2963 interactive 2964 ); 2965 2966 2967 if (!rect) { 2968 hide(); 2969 return; 2970 } 2971 2972 2973 /* 2974 * ================================================= 2975 * CURSOR POSITION RELATIVE TO TEXT 2976 * ================================================= 2977 */ 2978 2979 var x = 2980 event.clientX - 2981 rect.left; 2982 2983 var y = 2984 event.clientY - 2985 rect.top; 2986 2987 2988 /* 2989 * ================================================= 2990 * EXACT CROSS MASK 2991 * ================================================= 2992 * 2993 * Your cursor is: 2994 * 2995 * 21px wide 2996 * 21px high 2997 * 2998 * Horizontal bar: 2999 * 21px à 6px 3000 * 3001 * Vertical bar: 3002 * 6px à 21px 3003 * 3004 * Therefore: 3005 * 3006 * half-width = 10.5px 3007 * half-bar = 3px 3008 * 3009 * The polygon below traces that exact 3010 * plus/cross silhouette. 3011 */ 3012 3013 3014 /* 3015 * Store the mouse position as CSS variables. 3016 */ 3017 reveal.style.setProperty(
3018 '--audpr-mouse-x', 3019 x + 'px' 3020 ); 3021 3022 reveal.style.setProperty( 3023 '--audpr-mouse-y', 3024 y + 'px' 3025 ); 3026 3027 3028 /* 3029 * Exact cross: 3030 * 3031 * 6px 3032 * âââââââ 3033 * â â 3034 * âââââ âââââ 3035 * â â 3036 * âââââ âââââ 3037 * â â 3038 * âââââââ 3039 * 3040 * Overall: 3041 * 21px à 21px 3042 * 3043 * Bar thickness: 3044 * 6px 3045 */ 3046 3047 var crossMask = 3048 'polygon(' + 3049 3050 /* 3051 * Top-left of vertical bar. 3052 */ 3053 'calc(var(--audpr-mouse-x) - 3px) ' + 3054 'calc(var(--audpr-mouse-y) - 10.5px), ' + 3055 3056 /* 3057 * Top-right of vertical bar. 3058 */ 3059 'calc(var(--audpr-mouse-x) + 3px) ' + 3060 'calc(var(--audpr-mouse-y) - 10.5px), ' + 3061 3062 /* 3063 * Vertical bar â horizontal bar. 3064 */ 3065 'calc(var(--audpr-mouse-x) + 3px) ' + 3066 'calc(var(--audpr-mouse-y) - 3px), ' + 3067 3068 /* 3069 * Right edge. 3070 */ 3071 'calc(var(--audpr-mouse-x) + 10.5px) ' + 3072 'calc(var(--audpr-mouse-y) - 3px), ' + 3073 3074 /* 3075 * Bottom-right of horizontal bar. 3076 */ 3077 'calc(var(--audpr-mouse-x) + 10.5px) ' + 3078 'calc(var(--audpr-mouse-y) + 3px), ' + 3079 3080 /* 3081 * Horizontal bar â vertical bar. 3082 */ 3083 'calc(var(--audpr-mouse-x) + 3px) ' + 3084 'calc(var(--audpr-mouse-y) + 3px), ' + 3085 3086 /* 3087 * Bottom-right of vertical bar. 3088 */ 3089 'calc(var(--audpr-mouse-x) + 3px) ' + 3090 'calc(var(--audpr-mouse-y) + 10.5px), ' + 3091 3092 /* 3093 * Bottom-left of vertical bar. 3094 */ 3095 'calc(var(--audpr-mouse-x) - 3px) ' + 3096 'calc(var(--audpr-mouse-y) + 10.5px), ' + 3097 3098 /* 3099 * Vertical bar â horizontal bar. 3100 */ 3101 'calc(var(--audpr-mouse-x) - 3px) ' + 3102 'calc(var(--audpr-mouse-y) + 3px), ' + 3103 3104 /* 3105 * Left edge. 3106 */ 3107 'calc(var(--audpr-mouse-x) - 10.5px) ' + 3108 'calc(var(--audpr-mouse-y) + 3px), ' + 3109 3110 /* 3111 * Horizontal bar â vertical bar. 3112 */ 3113 'calc(var(--audpr-mouse-x) - 10.5px) ' + 3114 'calc(var(--audpr-mouse-y) - 3px), ' + 3115 3116 /* 3117 * Close the cross. 3118 */ 3119 'calc(var(--audpr-mouse-x) - 3px) ' + 3120 'calc(var(--audpr-mouse-y) - 3px)' + 3121 3122 ')'; 3123 3124 3125 reveal.style.clipPath = 3126 crossMask; 3127 3128 reveal.style.webkitClipPath = 3129 crossMask; 3130 3131 3132 /* 3133 * Show. 3134 */ 3135 reveal.style.visibility = 3136 'visible'; 3137 3138 reveal.style.opacity = 3139 '1'; 3140 3141 reveal.classList.add( 3142 'audpr-interactive-visible' 3143 ); 3144 3145 }, 3146 true 3147 ); 3148 3149 3150 /* 3151 * ======================================================== 3152 * POINTER LEAVES DOCUMENT 3153 * ======================================================== 3154 */ 3155 3156 document.addEventListener( 3157 'mouseleave', 3158 function () { 3159 3160 activeElement = 3161 null; 3162 3163 hide(); 3164 3165 }, 3166 true 3167 ); 3168 3169 3170 /* 3171 * ======================================================== 3172 * RESIZE 3173 * ======================================================== 3174 */ 3175 3176 window.addEventListener( 3177 'resize', 3178 function () { 3179 3180 if (activeElement) { 3181 3182 sync( 3183 activeElement 3184 ); 3185 3186 } 3187 3188 }, 3189 { passive: true } 3190 ); 3191 3192 3193 /* 3194 * ======================================================== 3195 * SCROLL 3196 * ======================================================== 3197 */ 3198 3199 window.addEventListener( 3200 'scroll', 3201 function () { 3202 3203 if (activeElement) { 3204 3205 sync( 3206 activeElement 3207 ); 3208 3209 } 3210 3211 }, 3212 { passive: true } 3213 ); 3214 3215 3216 /* 3217 * ======================================================== 3218 * WEB FONTS 3219 * ======================================================== 3220 */ 3221 3222 if ( 3223 document.fonts && 3224 document.fonts.ready 3225 ) { 3226 3227 document.fonts.ready.then( 3228 function () { 3229 3230 if (activeElement) { 3231 3232 sync( 3233 activeElement 3234 ); 3235 3236 } 3237 3238 } 3239 ); 3240 3241 } 3242 3243 3244 /* 3245 * ======================================================== 3246 * INITIAL STATE 3247 * ======================================================== 3248 */ 3249 3250 hide(); 3251 3252}); 3253</script>
3253 3254 3255
3255<script> 3256 document.addEventListener('DOMContentLoaded', function () { 3257 3258 const grid = document.querySelector( 3259 '.elementor-element-324ae32 .elementor-portfolio' 3260 ); 3261 3262 if (!grid) { 3263 return; 3264 } 3265 3266 3267 /* ===================================================== 3268 IMAGE LOADING 3269 ===================================================== */ 3270 3271 const images = grid.querySelectorAll('img'); 3272 3273 images.forEach(function (img, index) { 3274 3275 /* 3276 * Decode images without blocking rendering. 3277 */ 3278 img.decoding = 'async'; 3279 3280 3281 /* 3282 * First six images are the first desktop row. 3283 * Load them immediately. 3284 */ 3285 if (index < 6) { 3286 3287 img.loading = 'eager'; 3288 3289 /* 3290 * First image gets highest browser priority. 3291 */ 3292 if (index === 0) { 3293 img.fetchPriority = 'high'; 3294 } 3295 3296 } else { 3297 3298 /* 3299 * Everything further down can lazy-load. 3300 */ 3301 img.loading = 'lazy'; 3302 3303 } 3304 3305 }); 3306 3307 3308 /* ===================================================== 3309 SMOOTH RESPONSIVE GRID TRANSITION 3310 ===================================================== */ 3311 3312 let previousPositions = new Map(); 3313 let resizeTimer = null; 3314 let resizeFrame = null; 3315 3316 3317 /* 3318 * Get every portfolio item and its current position. 3319 */ 3320 function capturePositions() { 3321 3322 const positions = new Map(); 3323 3324 const items = 3325 grid.querySelectorAll( 3326 '.elementor-portfolio-item' 3327 ); 3328 3329 items.forEach(function (item) { 3330 3331 const rect = 3332 item.getBoundingClientRect(); 3333 3334 positions.set(item, { 3335 left: rect.left, 3336 top: rect.top, 3337 width: rect.width, 3338 height: rect.height 3339 }); 3340 3341 }); 3342 3343 return positions; 3344 } 3345 3346 3347 /* 3348 * Initial layout. 3349 */ 3350 previousPositions = 3351 capturePositions(); 3352 3353 3354 /* 3355 * Animate items from their old position 3356 * to their new responsive position. 3357 */ 3358 function animateGridTransition() { 3359 3360 const oldPositions = 3361 previousPositions; 3362 3363 const newPositions = 3364 capturePositions(); 3365 3366 3367 newPositions.forEach(function (newRect, item) { 3368 3369 const oldRect = 3370 oldPositions.get(item); 3371 3372 if (!oldRect) { 3373 return; 3374 } 3375 3376 3377 const deltaX = 3378 oldRect.left - 3379 newRect.left; 3380 3381 const deltaY = 3382 oldRect.top - 3383 newRect.top; 3384 3385 3386 /* 3387 * Don't animate tiny movements. 3388 */ 3389 if ( 3390 Math.abs(deltaX) < 1 && 3391 Math.abs(deltaY) < 1 3392 ) { 3393 return; 3394 } 3395 3396 3397 /* 3398 * Animate only the POSITION. 3399 * 3400 * We deliberately do NOT scale the item. 3401 * Elementor remains responsible for the 3402 * thumbnail's dimensions. 3403 */ 3404 item.animate( 3405 [ 3406 { 3407 transform: 3408 'translate(' + 3409 deltaX + 3410 'px, ' + 3411 deltaY + 3412 'px)' 3413 }, 3414 { 3415 transform: 3416 'translate(0, 0)' 3417 } 3418 ], 3419 { 3420 duration: 500, 3421 easing: 3422 'cubic-bezier(0.22, 1, 0.36, 1)', 3423 fill: 'none' 3424 } 3425 ); 3426 3427 }); 3428 3429 3430 previousPositions = 3431 newPositions; 3432 } 3433 3434 3435 /* 3436 * Watch for responsive layout changes. 3437 */ 3438 window.addEventListener( 3439 'resize', 3440 function () { 3441 3442 clearTimeout(resizeTimer); 3443 3444 resizeTimer = 3445 setTimeout(function () { 3446 3447 cancelAnimationFrame( 3448 resizeFrame 3449 ); 3450 3451 3452 resizeFrame = 3453 requestAnimationFrame( 3454 function () { 3455 3456 animateGridTransition(); 3457 3458 } 3459 ); 3460 3461 }, 100); 3462 3463 } 3464 ); 3465 3466 3467 }); 3468 </script>
3468 3469 <style> 3470 /* Original cruz-2.png â Media Library attachment 743 */ 3471 img.wp-image-743 { 3472 display: block !important; 3473 transform: rotate(0deg); 3474 transform-origin: 50% 50%; 3475 transition: 3476 transform 0.4s cubic-bezier(.22, .61, .36, 1), 3477 opacity 0.2s ease; 3478 will-change: transform; 3479 } 3480 3481 /* Desktop hover: X becomes + */ 3482 a:hover img.wp-image-743 { 3483 transform: rotate(45deg); 3484 opacity: 0.8; 3485 } 3486 3487 /* Keyboard accessibility */ 3488 a:focus-visible img.wp-image-743 { 3489 outline: 2px solid #315c7d; 3490 outline-offset: 4px; 3491 } 3492 3493 @media (prefers-reduced-motion: reduce) { 3494 img.wp-image-743 { 3495 transition: opacity 0.2s ease; 3496 } 3497 3498 a:hover img.wp-image-743 { 3499 transform: none; 3500 } 3501 } 3502 </style> 3503 3504
3504<script> 3505 (function () { 3506 const navigationFloor = 3507 "https:\/\/audpr.com\/home_new\/"; 3508 3509 /* 3510 * New storage key deliberately ignores the experimental 3511 * history created by previous versions. 3512 */ 3513 const stackKey = 'aud_x_navigation_stack_v2'; 3514 3515 function cleanInternalURL(value) { 3516 if ( 3517 !value || 3518 value === 'null' || 3519 value === 'undefined' 3520 ) { 3521 return null; 3522 } 3523 3524 try { 3525 const url = new URL( 3526 String(value), 3527 window.location.origin 3528 ); 3529 3530 if (url.origin !== window.location.origin) { 3531 return null; 3532 } 3533 3534 if ( 3535 url.pathname === '/null' || 3536 url.pathname === '/undefined' 3537 ) { 3538 return null; 3539 } 3540 3541 url.hash = ''; 3542 3543 return url.href; 3544 } catch (error) { 3545 return null; 3546 } 3547 } 3548 3549 function pageIdentity(value) { 3550 const cleaned = cleanInternalURL(value); 3551 3552 if (!cleaned) { 3553 return null; 3554 } 3555 3556 const url = new URL(cleaned); 3557 3558 let pathname = url.pathname; 3559 3560 if (pathname !== '/') { 3561 pathname = pathname.replace(/\/+$/, ''); 3562 } 3563 3564 return url.origin + pathname + url.search; 3565 } 3566 3567 function readStack() { 3568 try { 3569 const stored = JSON.parse( 3570 sessionStorage.getItem(stackKey) || '[]' 3571 ); 3572 3573 if (!Array.isArray(stored)) { 3574 return []; 3575 } 3576 3577 return stored 3578 .map(cleanInternalURL) 3579 .filter(Boolean) 3580 .slice(-50); 3581 } catch (error) { 3582 return []; 3583 } 3584 } 3585 3586 function saveStack(stack) { 3587 try { 3588 sessionStorage.setItem( 3589 stackKey, 3590 JSON.stringify(stack.slice(-50)) 3591 ); 3592 } catch (error) { 3593 // Navigation still falls back to /home_new/. 3594 } 3595 } 3596 3597 function isModifiedClick(event) { 3598 return ( 3599 event.button !== 0 || 3600 event.ctrlKey || 3601 event.metaKey || 3602 event.shiftKey || 3603 event.altKey 3604 ); 3605 } 3606 3607 function isTrackableLink(link) { 3608 if ( 3609 !link || 3610 link.target === '_blank' || 3611 link.hasAttribute('download') 3612 ) { 3613 return false; 3614 } 3615 3616 const rawHref = link.getAttribute('href'); 3617 3618 if ( 3619 !rawHref || 3620 rawHref.startsWith('#') || 3621 rawHref.startsWith('javascript:') || 3622 rawHref.startsWith('mailto:') || 3623 rawHref.startsWith('tel:') 3624 ) { 3625 return false; 3626 } 3627 3628 const destination = cleanInternalURL(link.href); 3629 3630 if (!destination) { 3631 return false; 3632 } 3633 3634 return ( 3635 pageIdentity(destination) !== 3636 pageIdentity(window.location.href) 3637 ); 3638 } 3639 3640 function initializeSmartX() { 3641 const currentURL = cleanInternalURL( 3642 window.location.href 3643 ); 3644 3645 const currentIdentity = pageIdentity(currentURL); 3646 const floorIdentity = pageIdentity(navigationFloor); 3647 3648 if (!currentURL || !currentIdentity) { 3649 return; 3650 } 3651 3652 let stack = readStack(); 3653 3654 /* 3655 * Arriving at /home_new/ resets the navigation floor. 3656 * Nothing before it can ever be reached using X. 3657 */ 3658 if (currentIdentity === floorIdentity) { 3659 stack = []; 3660 saveStack(stack); 3661 } 3662 3663 /* 3664 * Remove any accidental references to the root intro 3665 * or to the current page. 3666 */ 3667 stack = stack.filter(function (url) { 3668 const identity = pageIdentity(url); 3669 3670 return ( 3671 identity && 3672 identity !== window.location.origin && 3673 identity !== window.location.origin + '/' && 3674 identity !== currentIdentity 3675 ); 3676 }); 3677 3678 saveStack(stack); 3679 3680 let xDestination = navigationFloor; 3681 3682 if (stack.length) { 3683 xDestination = 3684 stack[stack.length - 1]; 3685 } 3686 3687 /* 3688 * Configure every responsive copy of cruz-2.png. 3689 */ 3690 document 3691 .querySelectorAll('img.wp-image-743') 3692 .forEach(function (image) { 3693 const button = image.closest('a'); 3694 3695 if (!button) { 3696 return; 3697 } 3698 3699 button.href = xDestination; 3700 button.dataset.audSmartX = 'true'; 3701 3702 button.setAttribute( 3703 'aria-label', 3704 pageIdentity(xDestination) === 3705 floorIdentity 3706 ? 'Return to main page' 3707 : 'Return to previous page' 3708 ); 3709 }); 3710 3711 /* 3712 * Capture navigation before Elementor begins its 3713 * transition. 3714 */ 3715 document.addEventListener( 3716 'click', 3717 function (event) { 3718 const link = event.target.closest('a'); 3719 3720 if ( 3721 !link || 3722 isModifiedClick(event) 3723 ) { 3724 return; 3725 } 3726 3727 let latestStack = readStack(); 3728 3729 /* 3730 * X navigation: 3731 * remove its destination from the stack so the 3732 * next X continues backward instead of looping. 3733 */ 3734 if ( 3735 link.dataset.audSmartX === 'true' 3736 ) { 3737 const destinationIdentity = 3738 pageIdentity(link.href); 3739 3740 while ( 3741 latestStack.length && 3742 pageIdentity( 3743 latestStack[ 3744 latestStack.length - 1 3745 ] 3746 ) !== destinationIdentity 3747 ) { 3748 latestStack.pop(); 3749 } 3750 3751 if ( 3752 latestStack.length && 3753 pageIdentity( 3754 latestStack[ 3755 latestStack.length - 1 3756 ] 3757 ) === destinationIdentity 3758 ) { 3759 latestStack.pop(); 3760 } 3761 3762 saveStack(latestStack); 3763 3764 /* 3765 * Do not prevent the click. 3766 * Elementor receives a normal internal link 3767 * and may play its page transition. 3768 */ 3769 return; 3770 } 3771 3772 /* 3773 * Ordinary forward navigation: 3774 * remember the page the visitor is leaving. 3775 */ 3776 if (!isTrackableLink(link)) { 3777 return; 3778 } 3779 3780 /* 3781 * Never place the root intro page into the stack. 3782 */ 3783 if ( 3784 currentIdentity === 3785 window.location.origin || 3786 currentIdentity === 3787 window.location.origin + '/' 3788 ) { 3789 return; 3790 } 3791 3792 const topIdentity = latestStack.length 3793 ? pageIdentity( 3794 latestStack[ 3795 latestStack.length - 1 3796 ] 3797 ) 3798 : null; 3799 3800 if (topIdentity !== currentIdentity) { 3801 latestStack.push(currentURL); 3802 } 3803 3804 saveStack(latestStack); 3805 }, 3806 true 3807 ); 3808 } 3809 3810 if (document.readyState === 'loading') { 3811 document.addEventListener( 3812 'DOMContentLoaded', 3813 initializeSmartX 3814 ); 3815 } else { 3816 initializeSmartX(); 3817 } 3818 })(); 3819 </script>
3819 3820 <style> 3821 3822/* ============================================================ 3823 A+UD â CURSOR INTERACTIONS 3824 SAFE ADD-ON FOR CURSOR CROSS V7 3825 ============================================================ */ 3826 3827 3828/* ============================================================ 3829 HAND MODE 3830 ============================================================ */ 3831 3832/* 3833 * Hide the existing custom cross while over an 3834 * interactive object. 3835 */ 3836 3837body.audpr-cursor-hand #audpr-plus { 3838 opacity: 0 !important; 3839 visibility: hidden !important; 3840} 3841 3842 3843/* 3844 * Force the native browser hand everywhere inside 3845 * the currently hovered interactive object. 3846 * 3847 * This overrides cursor:none on nested Elementor 3848 * spans/icons/text. 3849 */ 3850 3851body.audpr-cursor-hand, 3852body.audpr-cursor-hand * { 3853 cursor: pointer !important; 3854} 3855 3856 3857/* ============================================================ 3858 TEXT FIELD MODE 3859 ============================================================ */ 3860 3861body.audpr-cursor-text #audpr-plus { 3862 opacity: 0 !important; 3863 visibility: hidden !important; 3864} 3865 3866 3867/* 3868 * Hide the interactive text reveal while typing. 3869 */ 3870 3871body.audpr-cursor-text #audpr-interactive-reveal { 3872 opacity: 0 !important; 3873 visibility: hidden !important; 3874} 3875 3876 3877/* ============================================================ 3878 DELICATE I-BEAM 3879 ============================================================ */ 3880 3881#audpr-text-cursor { 3882 position: fixed; 3883 3884 left: 0; 3885 top: 0; 3886 3887 width: 7px; 3888 height: 21px; 3889 3890 pointer-events: none !important; 3891 3892 z-index: 2147483649; 3893 3894 opacity: 0; 3895 visibility: hidden; 3896 3897 transform: translate(-50%, -50%); 3898 3899 transition: opacity .08s ease; 3900} 3901 3902 3903/* Vertical stem */ 3904 3905#audpr-text-cursor::before { 3906 content: ""; 3907 3908 position: absolute; 3909 3910 left: 50%; 3911 top: 0; 3912 3913 width: 1px; 3914 height: 21px; 3915 3916 background: #8e8277; 3917 3918 transform: translateX(-50%); 3919} 3920 3921 3922/* Top + bottom caps */ 3923 3924#audpr-text-cursor::after { 3925 content: ""; 3926 3927 position: absolute; 3928 3929 left: 0; 3930 top: 0; 3931 3932 width: 7px; 3933 height: 1px; 3934 3935 background: #8e8277; 3936 3937 box-shadow: 3938 0 20px 0 #8e8277; 3939} 3940 3941 3942body.audpr-cursor-text #audpr-text-cursor { 3943 opacity: 1; 3944 visibility: visible; 3945} 3946 3947 3948/* 3949 * Hide native browser text cursor. 3950 */ 3951 3952input, 3953textarea, 3954[contenteditable="true"] { 3955 cursor: none !important; 3956} 3957 3958</style> 3959 3960
3961<script> 3962document.addEventListener('DOMContentLoaded', function () { 3963 3964 /* ======================================================== 3965 DESKTOP MOUSE ONLY 3966 ======================================================== */ 3967 3968 if (!window.matchMedia( 3969 '(hover: hover) and (pointer: fine)' 3970 ).matches) { 3971 return; 3972 } 3973 3974 3975 var plus = 3976 document.getElementById('audpr-plus'); 3977 3978 3979 /* 3980 * Don't do anything if Cursor Cross v7 3981 * isn't present. 3982 */ 3983 3984 if (!plus) { 3985 return; 3986 } 3987 3988 3989 /* ======================================================== 3990 CREATE TEXT CURSOR 3991 ======================================================== */ 3992 3993 var textCursor = 3994 document.createElement('div'); 3995 3996 textCursor.id = 3997 'audpr-text-cursor'; 3998 3999 textCursor.setAttribute( 4000 'aria-hidden', 4001 'true' 4002 ); 4003 4004 document.body.appendChild( 4005 textCursor 4006 ); 4007 4008 4009 /* ======================================================== 4010 TEXT FIELD DETECTION 4011 ======================================================== */ 4012 4013 function getTextTarget(element) { 4014 4015 if (!element) { 4016 return null; 4017 } 4018 4019 return element.closest( 4020 'textarea,' + 4021 '[contenteditable="true"],' + 4022 'input[type="text"],' + 4023 'input[type="email"],' + 4024 'input[type="tel"],' + 4025 'input[type="url"],' + 4026 'input[type="search"],' + 4027 'input[type="password"],' + 4028 'input[type="number"],' + 4029 'input:not([type])' 4030 ); 4031 } 4032 4033 4034 /* ======================================================== 4035 INTERACTIVE DETECTION 4036 ======================================================== */ 4037 4038 function getInteractiveTarget(element) { 4039 4040 if (!element) { 4041 return null; 4042 } 4043 4044 4045 /* 4046 * Includes: 4047 * 4048 * - normal links 4049 * - buttons 4050 * - Elementor buttons 4051 * - submit buttons 4052 * - role=button 4053 * - Elementor icon-list names even before 4054 * you give them LinkedIn URLs 4055 */ 4056 4057 var target = 4058 element.closest( 4059 'a[href],' + 4060 'button,' + 4061 '[role="button"],' + 4062 '.elementor-button,' + 4063 '.elementor-icon-list-item,' + 4064 '.elementor-icon-list-text,' + 4065 'input[type="submit"],' + 4066 'input[type="button"],' + 4067 'input[type="reset"]' 4068 ); 4069 4070 4071 if (!target) { 4072 return null; 4073 } 4074 4075 4076 /* 4077 * Ignore disabled controls. 4078 */ 4079 4080 if ( 4081 target.disabled || 4082 target.getAttribute('aria-disabled') === 'true' 4083 ) { 4084 return null; 4085 } 4086 4087 4088 /* 4089 * Ignore hidden duplicate Elementor elements. 4090 */ 4091 4092 var rect = 4093 target.getBoundingClientRect(); 4094 4095 4096 if ( 4097 rect.width <= 0 || 4098 rect.height <= 0 4099 ) { 4100 return null; 4101 } 4102 4103 4104 return target; 4105 } 4106 4107 4108 /* ======================================================== 4109 STATES 4110 ======================================================== */ 4111 4112 function enterText() { 4113 4114 document.body.classList.add( 4115 'audpr-cursor-text' 4116 ); 4117 4118 document.body.classList.remove( 4119 'audpr-cursor-hand' 4120 ); 4121 } 4122 4123 4124 function enterHand() { 4125 4126 document.body.classList.add( 4127 'audpr-cursor-hand' 4128 ); 4129 4130 document.body.classList.remove( 4131 'audpr-cursor-text' 4132 ); 4133 } 4134 4135 4136 function enterNormal() { 4137 4138 document.body.classList.remove( 4139 'audpr-cursor-text', 4140 'audpr-cursor-hand' 4141 ); 4142 } 4143 4144 4145 /* ======================================================== 4146 POINTER TRACKING 4147 ======================================================== */ 4148 4149 document.addEventListener( 4150 'pointermove', 4151 4152 function (event) { 4153 4154 if (event.pointerType !== 'mouse') { 4155 return; 4156 } 4157 4158 4159 /* 4160 * Keep custom I-beam following the mouse 4161 * even when it's invisible. 4162 */ 4163 4164 textCursor.style.left = 4165 event.clientX + 'px'; 4166 4167 textCursor.style.top = 4168 event.clientY + 'px'; 4169 4170 4171 /* 4172 * Get the real element under the pointer. 4173 */ 4174 4175 var element = 4176 document.elementFromPoint( 4177 event.clientX, 4178 event.clientY 4179 ); 4180 4181 4182 /* -------------------------------------------- 4183 TEXT FIELD 4184 -------------------------------------------- */ 4185 4186 if (getTextTarget(element)) { 4187 4188 enterText(); 4189 4190 return; 4191 } 4192 4193 4194 /* -------------------------------------------- 4195 INTERACTIVE 4196 -------------------------------------------- */ 4197 4198 if (getInteractiveTarget(element)) { 4199 4200 enterHand(); 4201 4202 return; 4203 } 4204 4205 4206 /* -------------------------------------------- 4207 NORMAL 4208 -------------------------------------------- */ 4209 4210 enterNormal(); 4211 4212 }, 4213 4214 true 4215 ); 4216 4217 4218 /* ======================================================== 4219 POINTER LEAVES PAGE 4220 ======================================================== */ 4221 4222 document.addEventListener( 4223 'mouseleave', 4224 4225 function () { 4226 4227 enterNormal(); 4228 4229 }, 4230 4231 true 4232 ); 4233 4234}); 4235</script>
4235 4236 4237 <div data-elementor-type="popup" data-elementor-id="672" class="elementor elementor-672 elementor-location-popup" data-elementor-settings="{"entrance_animation":"fadeInRight","entrance_animation_mobile":"fadeInRight","exit_animation":"fadeInLeft","exit_animation_mobile":"fadeInRight","entrance_animation_duration":{"unit":"px","size":1.2,"sizes":[]},"a11y_navigation":"yes","triggers":[],"timing":[]}" data-elementor-post-type="elementor_library"> 4238 <div class="elementor-element elementor-element-87afe6c e-flex e-con-boxed e-con e-parent" data-id="87afe6c" data-element_type="container" data-e-type="container"> 4239 <div class="e-con-inner"> 4240 <div class="elementor-element elementor-element-a60b456 elementor-widget elementor-widget-spacer" data-id="a60b456" data-element_type="widget" data-e-type="widget" data-widget_type="spacer.default"> 4241 <div class="elementor-spacer"> 4242 <div class="elementor-spacer-inner"></div> 4243 </div> 4244 </div> 4245 <div class="elementor-element elementor-element-c141cf3 e-con-full e-flex e-con e-child" data-id="c141cf3" data-element_type="container" data-e-type="container"> 4246 <div class="elementor-element elementor-element-f8ab857 elementor-widget__width-initial elementor-widget elementor-widget-heading" data-id="f8ab857" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default"> 4247 <h2 class="elementor-heading-title elementor-size-default"><a href="https://audpr.com/architecture/">ARCHITECTURE</a></h2> </div> 4248 </div> 4249 <div class="elementor-element elementor-element-c414328 e-con-full e-flex e-con e-child" data-id="c414328" data-element_type="container" data-e-type="container"> 4250 <div class="elementor-element elementor-element-336d4a0 elementor-widget__width-initial elementor-widget elementor-widget-heading" data-id="336d4a0" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default"> 4251 <h2 class="elementor-heading-title elementor-size-default"><a href="https://audpr.com/urban-design/">URBAN DESIGN</a></h2> </div> 4252 </div> 4253 <div class="elementor-element elementor-element-4c3411a elementor-widget elementor-widget-spacer" data-id="4c3411a" data-element_type="widget" data-e-type="widget" data-widget_type="spacer.default"> 4254 <div class="elementor-spacer"> 4255 <div class="elementor-spacer-inner"></div> 4256 </div> 4257 </div> 4258 <div class="elementor-element elementor-element-52802ef e-con-full e-flex e-con e-child" data-id="52802ef" data-element_type="container" data-e-type="container"> 4259 <div class="elementor-element elementor-element-6833c50 elementor-widget__width-initial elementor-widget elementor-widget-heading" data-id="6833c50" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default"> 4260 <h2 class="elementor-heading-title elementor-size-default"><a href="https://audpr.com/about-us-services/">ABOUT US + SERVICES</a></h2> </div> 4261 </div> 4262 <div class="elementor-element elementor-element-9cddce6 e-con-full e-flex e-con e-child" data-id="9cddce6" data-element_type="container" data-e-type="container"> 4263 <div class="elementor-element elementor-element-96bebe3 elementor-widget__width-initial elementor-widget elementor-widget-heading" data-id="96bebe3" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default"> 4264 <h2 class="elementor-heading-title elementor-size-default"><a href="https://audpr.com/staff-collaborators/">STAFF + COLLABORATORS</a></h2> </div> 4265 </div> 4266 <div class="elementor-element elementor-element-6743300 e-con-full e-flex e-con e-child" data-id="6743300" data-element_type="container" data-e-type="container"> 4267 <div class="elementor-element elementor-element-31f5633 elementor-widget__width-initial elementor-widget elementor-widget-heading" data-id="31f5633" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default"> 4268 <h2 class="elementor-heading-title elementor-size-default"><a href="https://audpr.com/contact-us/">CONTACT US</a></h2> </div>
4269 </div> 4270 <div class="elementor-element elementor-element-2d043d5 e-con-full e-flex e-con e-child" data-id="2d043d5" data-element_type="container" data-e-type="container"> 4271 <div class="elementor-element elementor-element-f675dd4 elementor-widget__width-initial elementor-widget elementor-widget-heading" data-id="f675dd4" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default"> 4272 <h2 class="elementor-heading-title elementor-size-default"><a href="https://audpr.com/clients/">CLIENTS</a></h2> </div> 4273 </div> 4274 <div class="elementor-element elementor-element-234c44e e-con-full e-flex e-con e-child" data-id="234c44e" data-element_type="container" data-e-type="container"> 4275 <div class="elementor-element elementor-element-a037738 elementor-widget__width-initial elementor-widget elementor-widget-heading" data-id="a037738" data-element_type="widget" data-e-type="widget" data-widget_type="heading.default"> 4276 <h2 class="elementor-heading-title elementor-size-default"><a href="https://audpr.com/awards-publications/">AWARDS + PUBLICATIONS</a></h2> </div> 4277 </div> 4278 </div> 4279 </div> 4280 </div> 4281
4281<script> 4282 ( () => { 4283 const lazyloadRunObserver = () => { 4284 const lazyloadBackgrounds = document.querySelectorAll( `.e-con.e-parent:not(.e-lazyloaded)` ); 4285 const lazyloadBackgroundObserver = new IntersectionObserver( ( entries ) => { 4286 entries.forEach( ( entry ) => { 4287 if ( entry.isIntersecting ) { 4288 let lazyloadBackground = entry.target; 4289 if( lazyloadBackground ) { 4290 lazyloadBackground.classList.add( 'e-lazyloaded' ); 4291 } 4292 lazyloadBackgroundObserver.unobserve( entry.target ); 4293 } 4294 }); 4295 }, { rootMargin: '200px 0px 200px 0px' } ); 4296 lazyloadBackgrounds.forEach( ( lazyloadBackground ) => { 4297 lazyloadBackgroundObserver.observe( lazyloadBackground ); 4298 } ); 4299 }; 4300 const events = [ 4301 'DOMContentLoaded', 4302 'elementor/lazyload/observe', 4303 ]; 4304 events.forEach( ( event ) => { 4305 document.addEventListener( event, lazyloadRunObserver ); 4306 } ); 4307 } )(); 4308 </script>
4308 4309 <link rel='stylesheet' id='elementor-post-4116-css' href='https://audpr.com/wp-content/uploads/elementor/css/post-4116.css?ver=1791533034' media='all' /> 4310<link rel='stylesheet' id='widget-image-css' href='https://audpr.com/wp-content/plugins/elementor/assets/css/widget-image.min.css?ver=4.3.4' media='all' /> 4311<link rel='stylesheet' id='widget-social-icons-css' href='https://audpr.com/wp-content/plugins/elementor/assets/css/widget-social-icons.min.css?ver=4.3.4' media='all' /> 4312<link rel='stylesheet' id='e-apple-webkit-css' href='https://audpr.com/wp-content/plugins/elementor/assets/css/conditionals/apple-webkit.min.css?ver=4.3.4' media='all' /> 4313<link rel='stylesheet' id='widget-form-css' href='https://audpr.com/wp-content/plugins/elementor-pro/assets/css/widget-form.min.css?ver=4.3.1' media='all' /> 4314<link rel='stylesheet' id='aud-maplibre-css' href='https://unpkg.com/[email protected]/dist/maplibre-gl.css?ver=5.24.0' media='all' /> 4315<link rel='stylesheet' id='aud-snazzy-google-maps-css' href='https://audpr.com/wp-content/plugins/snazzy-google-maps-elementor/assets/css/snazzy-google-maps.css?ver=3.0.14' media='all' />
4316<script id="angie-canvas-template-js-before"> 4317angieCanvasTemplateData = {"templateSlug":"angie-canvas"}; 4318//# sourceURL=angie-canvas-template-js-before 4319</script>
vendor: 1 bytes, line 4319
4319
4320<script id="hello-theme-frontend-js" src="https://audpr.com/wp-content/themes/hello-elementor/assets/js/hello-frontend.js?ver=3.5.1"></script>
vendor: 1 bytes, line 4320
4320
4321<script id="instant-page-js" src="https://audpr.com/wp-content/plugins/elementor-pro/assets//lib/instant-page/instant-page.min.js?ver=4.3.1"></script>
vendor: 1 bytes, line 4321
4321
4322<script id="elementor-webpack-runtime-js" src="https://audpr.com/wp-content/plugins/elementor/assets/js/webpack.runtime.min.js?ver=4.3.4"></script>
vendor: 1 bytes, line 4322
4322
4323<script id="elementor-frontend-modules-js" src="https://audpr.com/wp-content/plugins/elementor/assets/js/frontend-modules.min.js?ver=4.3.4"></script>
vendor: 1 bytes, line 4323
4323
4324<script id="jquery-ui-core-js-before"> 4325jQuery.uiBackCompat = true; 4326//# sourceURL=jquery-ui-core-js-before 4327</script>
vendor: 1 bytes, line 4327
4327
4328<script id="jquery-ui-core-js" src="https://audpr.com/wp-includes/js/jquery/ui/core.min.js?ver=1.14.2"></script>
vendor: 1 bytes, line 4328
4328
4329<script id="elementor-frontend-js-before"> 4330var elementorFrontendConfig = {"environmentMode":{"edit":false,"wpPreview":false,"isScriptDebug":false},"i18n":{"shareOnFacebook":"Share on Facebook","shareOnX":"Share on X","pinIt":"Pin it","download":"Download","downloadImage":"Download image","fullscreen":"Fullscreen","zoom":"Zoom","share":"Share","playVideo":"Play Video","previous":"Previous","next":"Next","close":"Close","a11yCarouselPrevSlideMessage":"Previous slide","a11yCarouselNextSlideMessage":"Next slide","a11yCarouselFirstSlideMessage":"This is the first slide","a11yCarouselLastSlideMessage":"This is the last slide","a11yCarouselPaginationBulletMessage":"Go to slide"},"is_rtl":false,"breakpoints":{"xs":0,"sm":480,"md":768,"lg":1025,"xl":1440,"xxl":1600},"responsive":{"breakpoints":{"mobile":{"label":"Mobile Portrait","value":767,"default_value":767,"direction":"max","is_enabled":true},"mobile_extra":{"label":"Mobile Landscape","value":880,"default_value":880,"direction":"max","is_enabled":false},"tablet":{"label":"Tablet Portrait","value":1024,"default_value":1024,"direction":"max","is_enabled":true},"tablet_extra":{"label":"Tablet Landscape","value":1200,"default_value":1200,"direction":"max","is_enabled":false},"laptop":{"label":"Laptop","value":1366,"default_value":1366,"direction":"max","is_enabled":false},"widescreen":{"label":"Widescreen","value":2400,"default_value":2400,"direction":"min","is_enabled":false}},"hasCustomBreakpoints":false},"version":"4.3.4","is_static":false,"experimentalFeatures":{"e_font_icon_svg":true,"additional_custom_breakpoints":true,"container":true,"e_optimized_markup":true,"theme_builder_v2":true,"hello-theme-header-footer":true,"nested-elements":true,"import-export-customization":true,"e_pro_atomic_form":true,"e_pro_collection_loop":true,"mega-menu":true},"urls":{"assets":"https:\/\/audpr.com\/wp-content\/plugins\/elementor\/assets\/","ajaxurl":"https:\/\/audpr.com\/wp-admin\/admin-ajax.php","uploadUrl":"https:\/\/audpr.com\/wp-content\/uploads"}
4330,"nonces":{"floatingButtonsClickTracking":"eb86be3b85","atomicFormsSendForm":"4a5cb22acf"},"swiperClass":"swiper","settings":{"page":[],"editorPreferences":[]},"kit":{"active_breakpoints":["viewport_mobile","viewport_tablet"],"global_image_lightbox":"yes","lightbox_enable_counter":"yes","lightbox_enable_fullscreen":"yes","lightbox_enable_zoom":"yes","lightbox_enable_share":"yes","lightbox_title_src":"title","lightbox_description_src":"description","hello_header_logo_type":"title","hello_header_menu_layout":"horizontal","hello_footer_logo_type":"logo"},"post":{"id":1271,"title":"CONTACT%20US%20-%20A%2BUD","excerpt":"","featuredImage":"https:\/\/audpr.com\/wp-content\/uploads\/2025\/09\/mapa_2.jpg"}}; 4331//# sourceURL=elementor-frontend-js-before 4332</script>
vendor: 1 bytes, line 4332
4332
4333<script id="elementor-frontend-js" src="https://audpr.com/wp-content/plugins/elementor/assets/js/frontend.min.js?ver=4.3.4"></script>
vendor: 1 bytes, line 4333
4333
4334<script id="aud-maplibre-js" src="https://unpkg.com/[email protected]/dist/maplibre-gl.js?ver=5.24.0"></script>
vendor: 1 bytes, line 4334
4334
4335<script id="aud-pmtiles-js" src="https://unpkg.com/[email protected]/dist/pmtiles.js?ver=4.5.0"></script>
vendor: 1 bytes, line 4335
4335
4336<script id="aud-snazzy-google-maps-js-extra"> 4337var AUDSnazzyGoogleMaps = {"sourceMode":"openfreemap","styleUrl":"https://tiles.openfreemap.org/styles/liberty","pmtilesUrl":"","ajaxUrl":"https://audpr.com/wp-admin/admin-ajax.php","geocodeNonce":"1784f6efc9","editor":"","messages":{"loadError":"The vector basemap could not load. Check the map source/style URL and network access.","geocodeError":"Address lookup was unavailable. The map loaded using its fallback center; enter latitude/longitude for an exact location.","pmtilesMissing":"PMTiles mode is enabled but no PMTiles URL is configured in Settings \u2192 Snazzy Google Maps."}}; 4338//# sourceURL=aud-snazzy-google-maps-js-extra 4339</script>
vendor: 1 bytes, line 4339
4339
4340<script id="aud-snazzy-google-maps-js" src="https://audpr.com/wp-content/plugins/snazzy-google-maps-elementor/assets/js/snazzy-google-maps.js?ver=3.0.14"></script>
vendor: 1 bytes, line 4340
4340
4341<script id="cwebts-js" src="https://audpr.com/wp-content/plugins/cweb-form-protection-turnstile-elementor/assets/js/turnstile.js?ver=1.2.2"></script>
vendor: 1 bytes, line 4341
4341
4342<script id="cwebts-api-js" defer src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit&onload=cwebtsOnload&ver=1.2.2"></script>
vendor: 1 bytes, line 4342
4342
4343<script id="wp-hooks-js" src="https://audpr.com/wp-includes/js/dist/hooks.min.js?ver=f0f188028580e8dc1255"></script>
vendor: 1 bytes, line 4343
4343
4344<script id="wp-i18n-js" src="https://audpr.com/wp-includes/js/dist/i18n.min.js?ver=1dfe7db3940c23ea9216"></script>
vendor: 1 bytes, line 4344
4344
4345<script id="wp-i18n-js-after"> 4346wp.i18n.setLocaleData( { 'text direction\u0004ltr': [ 'ltr' ] } ); 4347//# sourceURL=wp-i18n-js-after 4348</script>
vendor: 1 bytes, line 4348
4348
4349<script id="elementor-pro-frontend-js-before"> 4350var ElementorProFrontendConfig = {"ajaxurl":"https:\/\/audpr.com\/wp-admin\/admin-ajax.php","nonce":"e0f63b5a51","urls":{"assets":"https:\/\/audpr.com\/wp-content\/plugins\/elementor-pro\/assets\/","rest":"https:\/\/audpr.com\/wp-json\/"},"settings":{"lazy_load_background_images":true},"popup":{"hasPopUps":true},"shareButtonsNetworks":{"facebook":{"title":"Facebook","has_counter":true},"twitter":{"title":"Twitter"},"linkedin":{"title":"LinkedIn","has_counter":true},"pinterest":{"title":"Pinterest","has_counter":true},"reddit":{"title":"Reddit","has_counter":true},"vk":{"title":"VK","has_counter":true},"odnoklassniki":{"title":"OK","has_counter":true},"tumblr":{"title":"Tumblr"},"digg":{"title":"Digg"},"skype":{"title":"Skype"},"stumbleupon":{"title":"StumbleUpon","has_counter":true},"mix":{"title":"Mix"},"telegram":{"title":"Telegram"},"pocket":{"title":"Pocket","has_counter":true},"xing":{"title":"XING","has_counter":true},"whatsapp":{"title":"WhatsApp"},"email":{"title":"Email"},"print":{"title":"Print"},"x-twitter":{"title":"X"},"threads":{"title":"Threads"}},"facebook_sdk":{"lang":"en_US","app_id":""},"lottie":{"defaultAnimationUrl":"https:\/\/audpr.com\/wp-content\/plugins\/elementor-pro\/modules\/lottie\/assets\/animations\/default.json"}}; 4351//# sourceURL=elementor-pro-frontend-js-before 4352</script>
vendor: 1 bytes, line 4352
4352
4353<script id="elementor-pro-frontend-js" src="https://audpr.com/wp-content/plugins/elementor-pro/assets/js/frontend.min.js?ver=4.3.1"></script>
vendor: 1 bytes, line 4353
4353
4354<script id="pro-elements-handlers-js" src="https://audpr.com/wp-content/plugins/elementor-pro/assets/js/elements-handlers.min.js?ver=4.3.1"></script>
vendor: 1 bytes, line 4354
4354
4355<script id="wp-emoji-settings" type="application/json"> 4356{"baseUrl":"https://s.w.org/images/core/emoji/17.0.2/72x72/","ext":".png","svgUrl":"https://s.w.org/images/core/emoji/17.0.2/svg/","svgExt":".svg","source":{"concatemoji":"https://audpr.com/wp-includes/js/wp-emoji-release.min.js?ver=7.1.3"}} 4357</script>
vendor: 1 bytes, line 4357
4357
4358<script type="module"> 4359/*! This file is auto-generated */ 4360var e="script#wp-emoji-settings",t=document.querySelector(e);if(!(t instanceof HTMLScriptElement))throw new Error("Element missing: "+e);const r=JSON.parse(t.text),s=(window._wpemojiSettings=r,"wpEmojiSettingsSupports"),o=["flag","emoji"];function i(e){try{var t={supportTests:e,timestamp:(new Date).valueOf()};sessionStorage.setItem(s,JSON.stringify(t))}catch(e){}}function c(e,t,n){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);t=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(n,0,0);const r=new Uint32Array(e.getImageData(0,0,e.canvas.width,e.canvas.height).data);return t.every((e,t)=>e===r[t])}function p(e,t){e.clearRect(0,0,e.canvas.width,e.canvas.height),e.fillText(t,0,0);var n=e.getImageData(16,16,1,1);for(let e=0;e<n.data.length;e++)if(0!==n.data[e])return!1;return!0}function u(e,t,n,r){switch(t){case"flag":return n(e,"\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f","\ud83c\udff3\ufe0f\u200b\u26a7\ufe0f")?!1:!n(e,"\ud83c\udde8\ud83c\uddf6","\ud83c\udde8\u200b\ud83c\uddf6")&&!n(e,"\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f","\ud83c\udff4\u200b\udb40\udc67\u200b\udb40\udc62\u200b\udb40\udc65\u200b\udb40\udc6e\u200b\udb40\udc67\u200b\udb40\udc7f");case"emoji":return!r(e,"\ud83e\u1fac8")}return!1}function f(e,t,n,r){let a;const s=(a="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?new OffscreenCanvas(300,150):document.createElement("canvas")).getContext("2d",{willReadFrequently:!0}),o=(s.textBaseline="top",s.font="600 32px Arial",{});return e.forEach(e=>{o[e]=t(s,e,n,r)}),o}function a(e){var t=document.createElement("script");t.src=e,t.defer=!0,document.head.appendChild(t)}r.supports={everything:!0,everythingExceptFlag:!0},new Promise(t=>{let n=function(){try{var e=JSON.parse(sessionStorage.getItem(s));if("object"==typeof e&&"number"==typeof e.timestamp&&(new Date).valueOf()<e.timestamp+604800&&"object"==typeof e.supportTests)return e.supportTests}catch(e){}return null}();if(!n){if("undefined"!=typeof Worker&&"undefined"!=typeof OffscreenCanvas&&"undefined"!=typeof URL&&URL.createObjectURL&&"undefined"!=typeof Blob)try{var e="postMessage("+f.toString()+"("+[JSON.stringify(o),u.toString(),c.toString(),p.toString()].join(",")+"));",r=new Blob([e],{type:"text/javascript"});const a=new Worker(URL.createObjectURL(r),{name:"wpTestEmojiSupports"});return void(a.onmessage=e=>{i(n=e.data),a.terminate(),t(n)})}catch(e){}i(n=f(o,u,c,p))}t(n)}).then(e=>{for(const n in e)r.supports[n]=e[n],r.supports.everything=r.supports.everything&&r.supports[n],"flag"!==n&&(r.supports.everythingExceptFlag=r.supports.everythingExceptFlag&&r.supports[n]);var t;r.supports.everythingExceptFlag=r.supports.everythingExceptFlag&&!r.supports.flag,r.supports.everything||((t=r.source||{}).concatemoji?a(t.concatemoji):t.wpemoji&&t.twemoji&&(a(t.twemoji),a(t.wpemoji)))}); 4361//# sourceURL=https://audpr.com/wp-includes/js/wp-emoji-loader.min.js 4362</script>
4362 4363<!-- start Simple Custom CSS and JS -->
4364<script> 4365document.addEventListener("DOMContentLoaded", function () { 4366 const page = document.querySelector("body"); // Aplica a todo el body 4367 4368 if (page) { 4369 // Animación de entrada 4370 requestAnimationFrame(() => { 4371 page.classList.add("slideUp"); 4372 }); 4373 } 4374 4375 // Interceptar todos los links internos 4376 const links = document.querySelectorAll("a"); 4377 links.forEach(link => { 4378 link.addEventListener("click", function (e) { 4379 const href = link.getAttribute("href"); 4380 4381 // Solo aplica a enlaces internos 4382 if (href && (href.includes(location.hostname) || href.startsWith("/"))) { 4383 e.preventDefault(); 4384 page.classList.remove("slideUp"); 4385 page.classList.add("slideDown"); 4386 4387 setTimeout(() => { 4388 window.location.href = href; 4389 }, 600); // Igual que la duración de la animación 4390 } 4391 }); 4392 }); 4393 4394 // Botón de cierre de Elementor (si existe) 4395 const closeBtn = document.querySelector(".dialog-close-button"); 4396 if (closeBtn) { 4397 closeBtn.addEventListener("click", function (e) { 4398 e.preventDefault(); 4399 page.classList.remove("slideUp"); 4400 page.classList.add("slideDown"); 4401 4402 setTimeout(() => { 4403 window.history.back(); 4404 }, 600); 4405 }); 4406 } 4407}); 4408 4409 4410</script>
4410 4411<!-- end Simple Custom CSS and JS -->
4412<script id="aud-territory-map-v10-js"> 4413(async () => { 4414 'use strict'; 4415 4416 const root = document.getElementById('audTerritoryMap'); 4417 if (!root) return; 4418 4419 /* ============================================================ 4420 v10N3 â EXTERNAL SVG LOADER 4421 Restores the EXACT municipality names from the original v10 4422 inline SVG before the existing interaction logic initializes. 4423 This also restores name-dependent adjustments such as the 4424 Mayagüez marker anchor. 4425 ============================================================ */ 4426 const SVG_ASSET_URL = 'https://audpr.com/wp-content/uploads/2026/08/ISLA.svg?v=10n3'; 4427 const mapHost = root.querySelector('#mapSvg'); 4428 4429 const MUNICIPALITY_NAMES = {"PR-YU":"Yauco","PR-YB":"Yabucoa","PR-VL":"Villalba","PR-VQ":"Vieques","PR-VB":"Vega Baja","PR-VA":"Vega Alta","PR-UT":"Utuado","PR-TJ":"Trujillo Alto","PR-TB":"Toa Baja","PR-TA":"Toa Alta","PR-SI":"Santa Isabel","PR-SS":"San Sebastián","PR-SL":"San Lorenzo","PR-SJ":"San Juan","PR-SG":"San Germán","PR-SA":"Salinas","PR-SB":"Sabana Grande","PR-RG":"RÃo Grande","PR-RC":"Rincón","PR-QB":"Quebradillas","PR-PO":"Ponce","PR-PN":"Peñuelas","PR-PT":"Patillas","PR-OR":"Orocovis","PR-NR":"Naranjito","PR-NG":"Naguabo","PR-MV":"Morovis","PR-MC":"Moca","PR-MG":"Mayagüez","PR-MB":"Maunabo","PR-MR":"Maricao","PR-MT":"ManatÃ","PR-LQ":"Luquillo","PR-LZ":"LoÃza","PR-LP":"Las Piedras","PR-LM":"Las MarÃas","PR-LR":"Lares","PR-LJ":"Lajas","PR-JC":"Juncos","PR-JD":"Juana DÃaz","PR-JY":"Jayuya","PR-IS":"Isabela","PR-HU":"Humacao","PR-HO":"Hormigueros","PR-HA":"Hatillo","PR-GR":"Gurabo","PR-GB":"Guaynabo","PR-GL":"Guayanilla","PR-GM":"Guayama","PR-GC":"Guánica","PR-FL":"Florida","PR-FJ":"Fajardo","PR-DO":"Dorado","PR-CU":"Culebra","PR-CZ":"Corozal","PR-CM":"ComerÃo","PR-CO":"Coamo","PR-CD":"Cidra","PR-CL":"Ciales","PR-CB":"Ceiba","PR-CY":"Cayey","PR-CT":"Cataño","PR-CN":"Carolina","PR-CV":"Canóvanas","PR-CA":"Camuy","PR-CG":"Caguas","PR-CR":"Cabo Rojo","PR-BY":"Bayamón","PR-BQ":"Barranquitas","PR-BC":"Barceloneta","PR-AR":"Arroyo","PR-AC":"Arecibo","PR-AN":"Añasco","PR-AI":"Aibonito","PR-AB":"Aguas Buenas","PR-AL":"Aguadilla","PR-AD":"Aguada","PR-AJ":"Adjuntas"}; 4430 4431 if (!mapHost) { 4432 console.error('A+UD Territory Map: #mapSvg host not found.'); 4433 return; 4434 } 4435 4436 async function loadTerritorySvg() { 4437 const existing = mapHost.querySelector('svg'); 4438 if (existing && root.querySelectorAll('.municipality').length) { 4439 return true; 4440 } 4441 4442 try { 4443 const response = await fetch(SVG_ASSET_URL, { 4444 method: 'GET', 4445 credentials: 'same-origin', 4446 cache: 'force-cache' 4447 }); 4448 4449 if (!response.ok) { 4450 throw new Error(`HTTP ${response.status}`); 4451 } 4452 4453 const source = await response.text(); 4454 const parsed = new DOMParser().parseFromString(source, 'image/svg+xml'); 4455 4456 if (parsed.querySelector('parsererror')) { 4457 throw new Error('SVG XML parse error'); 4458 } 4459 4460 const sourceSvg = parsed.documentElement; 4461 if (!sourceSvg || sourceSvg.localName.toLowerCase() !== 'svg') { 4462 throw new Error('Downloaded asset is not an SVG document'); 4463 } 4464 4465 const importedSvg = document.importNode(sourceSvg, true); 4466 const rawMunicipalities = importedSvg.querySelectorAll('path[id^="PR-"]'); 4467 4468 rawMunicipalities.forEach(path => { 4469 const id = (path.getAttribute('id') || '').toUpperCase(); 4470 4471 /* Never trust the raw SVG's short title/code for UI naming. 4472 The working v10 inline SVG is the authoritative name source. */ 4473 const name = 4474 MUNICIPALITY_NAMES[id] || 4475 path.getAttribute('data-name') || 4476 path.getAttribute('title') || 4477 id.replace(/^PR-/, ''); 4478 4479 path.classList.remove('land'); 4480 path.classList.add('municipality'); 4481 path.setAttribute('data-id', id); 4482 path.setAttribute('data-name', name); 4483 path.setAttribute('aria-label', `${name} municipality`); 4484 path.removeAttribute('title'); 4485 }); 4486 4487 importedSvg.style.backgroundColor = 'transparent'; 4488 importedSvg.removeAttribute('width'); 4489 importedSvg.removeAttribute('height'); 4490 importedSvg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); 4491 importedSvg.setAttribute('aria-label', 'Puerto Rico municipalities map'); 4492 importedSvg.setAttribute('role', 'img'); 4493 4494 mapHost.replaceChildren(importedSvg); 4495 4496 const municipalityCount = mapHost.querySelectorAll('.municipality').length; 4497 if (!municipalityCount) { 4498 throw new Error('SVG loaded but no PR municipality paths were found'); 4499 } 4500 4501 console.info( 4502 `A+UD Territory Map: external SVG loaded; ${municipalityCount} municipalities normalized with v10 names.` 4503 ); 4504 4505 return true; 4506 } catch (error) { 4507 console.error('A+UD Territory Map: external SVG failed to load:', error); 4508 mapHost.innerHTML = 4509 '<div class="aud-map-svg-error" role="status">MAP DATA UNAVAILABLE</div>'; 4510 return false;
4511 } 4512 } 4513 4514 if (!(await loadTerritorySvg())) return; 4515 4516 const DATA = {"projects":[{"name":"Able Sales Freezer","type":"Architecture","url":"https://audpr.com/able-sales-freezer","location":"Cataño, PR","municipality":"PR-CT","municipality_name":"Cataño","category":"Industrial Architecture"},{"name":"A+I House","type":"Architecture","url":"https://audpr.com/ai-house","location":"Lajas, PR","municipality":"PR-LJ","municipality_name":"Lajas","category":"Residential Architecture"},{"name":"Almacén del Vino - Tiendita Pan PepÃn, Ponce","type":"Architecture","url":"https://audpr.com/almacen-del-vino-tiendita-pan-pepin-ponce","location":"Ponce, PR","municipality":"PR-PO","municipality_name":"Ponce","category":"Commercial Architecture"},{"name":"Assertus Facade Improvements","type":"Architecture","url":"https://audpr.com/assertus-facade-improvements","location":"Guaynabo, PR","municipality":"PR-GB","municipality_name":"Guaynabo","category":"Commercial Architecture"},{"name":"Assertus Interiors","type":"Architecture","url":"https://audpr.com/assertus-interiors","location":"Guaynabo, PR","municipality":"PR-GB","municipality_name":"Guaynabo","category":"Commercial Architecture"},{"name":"Clinica de Ojos - ESSPR","type":"Architecture","url":"https://audpr.com/clinica-de-ojos-esspr","location":"Tres Monjitas Industrial Park, San Juan, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Commercial Architecture"},{"name":"ClÃnica de Ojos (ESSPR): Architectural Programming","type":"Architecture","url":"https://audpr.com/clinica-de-ojos-esspr-architectural-programming","location":"Santurce, PR (former location)","municipality":"PR-SJ","municipality_name":"San Juan","category":"Program / Construction Management"},{"name":"Commonwealth School Improvements","type":"Architecture","url":"https://audpr.com/commonwealth-parkville-schools","location":"San Juan, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Institutional Architecture"},{"name":"Conservatory of Music of Puerto Rico: Academic Bldg. (Phase III)","type":"Architecture","url":"https://audpr.com/conservatory-of-music-of-pr-academic-bldg-phase-iii","location":"Miramar, San Juan, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Architectural Planning & Programming"},{"name":"Cronos","type":"Architecture","url":"https://audpr.com/cronos","location":"San Juan, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Commercial Architecture"},{"name":"Day Care Center, UPR Carolina Campus","type":"Architecture","url":"https://audpr.com/day-care-center-upr-carolina-campus","location":"UPR Carolina Campus, PR","municipality":"PR-CN","municipality_name":"Carolina","category":"Institutional Architecture"},{"name":"Escuela Superior Vocacional San Isidro, Canóvanas","type":"Architecture","url":"https://audpr.com/escuela-superior-vocacional-san-isidro-canovanas","location":"Canóvanas, PR","municipality":"PR-CV","municipality_name":"Canóvanas","category":"Institutional Architecture"},{"name":"González Trading Headquarters (Teselta)","type":"Architecture","url":"https://audpr.com/gonzalez-trading-headquarters-teselta","location":"PR-866, Toa Baja, PR","municipality":"PR-TB","municipality_name":"Toa Baja","category":"Industrial Architecture"},{"name":"Improvements to La Boutique Du Vin","type":"Architecture","url":"https://audpr.com/improvements-to-la-boutique-du-vin","location":"Plaza Chardon 210, Suite 102, Hato Rey, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Commercial Architecture"},{"name":"J. Machado Residence","type":"Architecture","url":"https://audpr.com/j-machado-residence","location":"Guaynabo, PR","municipality":"PR-GB","municipality_name":"Guaynabo","category":"Residential Architecture"},{"name":"LEAP Academy of Sciences and Technology: Programming Explorations","type":"Architecture","url":"https://audpr.com/leap-academy-of-sciences-and-techonology","location":"Bayamón, PR","municipality":"PR-BY","municipality_name":"Bayamón","category":"Program / Construction Management"},{"name":"Lofts @ 55 Residential Development","type":"Architecture","url":"https://audpr.com/lofts-55-residential-development","location":"Calle Méndez Vigo, Mayagüez Town Center","municipality":"PR-MG","municipality_name":"Mayagüez","category":"Residential Architecture"},{"name":"Lufthansa Technik / AAIPR / Aguadilla BQN Airport Apron","type":"Architecture","url":"https://audpr.com/lufthansa-technik-aaipr-aguadilla-airport","location":"Rafael Hernández Airport (BQN), Aguadilla","municipality":"PR-AL","municipality_name":"Aguadilla","category":"Architectural Planning & Programming"},{"name":"M. Suárez Residence","type":"Architecture","url":"https://audpr.com/m-suarez-residence","location":"Guaynabo, PR","municipality":"PR-GB","municipality_name":"Guaynabo","category":"Residential Architecture"},{"name":"Martex Farms","type":"Architecture","url":"https://audpr.com/martex-farms","location":"Salinas, PR","municipality":"PR-SA","municipality_name":"Salinas","category":"Industrial Architecture"},{"name":"Marvel Freezer Reconstruction (Phases I & II)","type":"Architecture","url":"https://audpr.com/marvel-freezer-reconstruction-phase-i-ii","location":"Amelia Distribution Center, Guaynabo, PR","municipality":"PR-GB","municipality_name":"Guaynabo","category":"Industrial Architecture"},{"name":"New Headquarters for the PR Justice Department","type":"Architecture","url":"https://audpr.com/new-headquarters-for-the-pr-justice-department","location":"Hato Rey (Proposed), San Juan, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Institutional Architecture"},{"name":"New Headquarters for the PRSTRT","type":"Architecture","url":"https://audpr.com/new-headquarters-for-the-prstrt","location":"San Juan, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Commercial Architecture"},{"name":"NextGEN Toa Baja Facility - Planning-Programming","type":"Architecture","url":"https://audpr.com/nextgen-toa-baja-facility-planning-programming","location":"Toa Baja, PR","municipality":"PR-TB","municipality_name":"Toa Baja","category":"Program / Construction Management"},{"name":"Pan Pepin- New Employee Parking (Phase I) & New Manufacturing Plant A (Phase II)","type":"Architecture","url":"https://audpr.com/pan-pepin-new-employee-parking-phase-i-new-manufacturing-plant-a-phase-ii","location":"Minillas Industrial Park, Bayamón","municipality":"PR-BY","municipality_name":"Bayamón","category":"Industrial Architecture"},{"name":"PARKVILLE SCHOOL IMPROVEMENTS","type":"Architecture","url":"https://audpr.com/parkville-school-improvements","location":"Guaynabo, PR","municipality":"PR-GB","municipality_name":"Guaynabo","category":"Institutional Architecture"}
4516,{"name":"Pauliâs Residence","type":"Architecture","url":"https://audpr.com/paulis-residence","location":"Salinas, PR","municipality":"PR-SA","municipality_name":"Salinas","category":"Residential Architecture"},{"name":"PFS Administration Interiors","type":"Architecture","url":"https://audpr.com/pfs-administration-interiors","location":"Caguas, PR","municipality":"PR-CG","municipality_name":"Caguas","category":"Commercial Architecture"},{"name":"Phantom Estate","type":"Architecture","url":"https://audpr.com/phantom-estate","location":"Dorado Beach Plantation","municipality":"PR-DO","municipality_name":"Dorado","category":"Residential Architecture"},{"name":"Plaza Cellars Headquarters Interiors","type":"Architecture","url":"https://audpr.com/plaza-cellars-headquarters-interiors","location":"San Juan, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Commercial Architecture"},{"name":"Plaza Cellars Relocation @ Chardon 210 Planning","type":"Architecture","url":"https://audpr.com/plaza-cellars-relocation-chardon-210-planning","location":"San Juan, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Program / Construction Management"},{"name":"PLAZA FOOD SYSTEMS: DRY STORAGE EXPANSION (PHASE I)","type":"Architecture","url":"https://audpr.com/plaza-food-systems-dry-storage-expansion-phase-i","location":"Caguas, PR","municipality":"PR-CG","municipality_name":"Caguas","category":"Industrial Architecture"},{"name":"Plaza Food Systems- New Freezer & Cooler (Phase II)","type":"Architecture","url":"https://audpr.com/plaza-food-systems-new-freezer-cooler-phase-ii","location":"Caguas, PR","municipality":"PR-CG","municipality_name":"Caguas","category":"Industrial Architecture"},{"name":"Plaza Provision Company (PPC): Freezer Expansion","type":"Architecture","url":"https://audpr.com/plaza-provision-company-ppc-freezer-expansion","location":"PR-1, Barrio RÃo Cañas, Caguas, PR","municipality":"PR-CG","municipality_name":"Caguas","category":"Industrial Architecture"},{"name":"PR Department of Justice: Architectural Programming","type":"Architecture","url":"https://audpr.com/pr-department-of-justice-architectural-programming","location":"San Juan, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Program / Construction Management"},{"name":"PRTA Headquarters & Tennis Center, Bayamón","type":"Architecture","url":"https://audpr.com/prta-headquarters-tennis-center-bayamon","location":"Bayamón, PR","municipality":"PR-BY","municipality_name":"Bayamón","category":"Institutional Architecture"},{"name":"Restaurant & Dock at Marina Puerto del Rey, Ceiba","type":"Architecture","url":"https://audpr.com/restaurant-dock-at-marina-puerto-del-rey-ceiba","location":"Ceiba, PR","municipality":"PR-CB","municipality_name":"Ceiba","category":"Commercial Architecture"},{"name":"Serrallés Sugar Cane Initiative","type":"Architecture","url":"https://audpr.com/serralles-sugar-cane-initiative","location":"Ponce & Southwest PR","municipality":"PR-PO","municipality_name":"Ponce","category":"Architectural Planning & Programming"},{"name":"Subwayâs Central Office Relocation: Space Programing","type":"Architecture","url":"https://audpr.com/sub-island-development-sid-subway-central-office-relocation","location":"Space Programing LOCATION: San Juan, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Program / Construction Management"},{"name":"Tennis Complex for Mayagüez 2010","type":"Architecture","url":"https://audpr.com/tennis-complex-for-mayaguez-2010","location":"University of Puerto Rico, Mayagüez Campus","municipality":"PR-MG","municipality_name":"Mayagüez","category":"Institutional Architecture"},{"name":"Toyota PR Headquarters","type":"Architecture","url":"https://audpr.com/toyota-pr-headquarters","location":"San Juan, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Commercial Architecture"},{"name":"Villa Gabi","type":"Architecture","url":"https://audpr.com/villa-gabi","location":"Rio Grande, PR","municipality":"PR-RG","municipality_name":"RÃo Grande","category":"Residential Architecture"},{"name":"Able Sales: Planning & Programming","type":"Urban Design","url":"https://audpr.com/able-sales-planning-programming","location":"Centro de Distribución del Norte, Cataño, PR","municipality":"PR-CT","municipality_name":"Cataño","category":"Industrial Planning"},{"name":"American Tugs Inc. Growth Plan","type":"Urban Design","url":"https://audpr.com/american-tugs-inc-growth-plan","location":"Las Mareas Port, Guayama, PR","municipality":"PR-GM","municipality_name":"Guayama","category":"Industrial Planning"},{"name":"Ballester Hermanos New Distribution Center","type":"Urban Design","url":"https://audpr.com/ballester-hermanos-new-distribution-center","location":"Sabana Seca, Toa Baja, PR","municipality":"PR-TB","municipality_name":"Toa Baja","category":"Industrial Planning"},{"name":"Comunidad RÃo Bayamón","type":"Urban Design","url":"https://audpr.com/comunidad-rio-bayamon","location":"Bayamón, PR","municipality":"PR-BY","municipality_name":"Bayamón","category":"Urban Infrastru
4516cture Design / CPM"},{"name":"Cronos Office Development (Feasibility Study)","type":"Urban Design","url":"https://audpr.com/cronos-office-development-feasibility-study","location":"Ave. Roosevelt, San Juan, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Master Planning"},{"name":"Grupo Colón Gerena: Facility Planning","type":"Urban Design","url":"https://audpr.com/grupo-colon-gerena-facility-planning","location":"San Juan Metropolitan Area","municipality":"PR-SJ","municipality_name":"San Juan","category":"Industrial Planning"},{"name":"Knowledge Corridor / Science City @ Oso Blanco","type":"Urban Design","url":"https://audpr.com/knowledge-corridor-science-city-oso-blanco","location":"San Juan, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Transit-Oriented Development"},{"name":"Marvel International: Planning and Programming","type":"Urban Design","url":"https://audpr.com/marvel-international-planning-programming","location":"Amelia Distribution Center, Guaynabo, PR","municipality":"PR-GB","municipality_name":"Guaynabo","category":"Industrial Planning"},{"name":"Mayagüez Transit Corridor","type":"Urban Design","url":"https://audpr.com/mayaguez-transit-corridor","location":"Mayagüez town center and PR-2 corridor","municipality":"PR-MG","municipality_name":"Mayagüez","category":"Transit / Transportation Planning"},{"name":"Olein Recovery: Master Plan","type":"Urban Design","url":"https://audpr.com/olein-recovery-master-plan","location":"Yabucoa, PR","municipality":"PR-YB","municipality_name":"Yabucoa","category":"Industrial Planning"},{"name":"Pan PepÃn: Master Plan","type":"Urban Design","url":"https://audpr.com/pan-pepin-master-plan","location":"Minillas Industrial Park, Bayamón","municipality":"PR-BY","municipality_name":"Bayamón","category":"Industrial Planning"},{"name":"Plaza Food Systems: Master Plan","type":"Urban Design","url":"https://audpr.com/plaza-food-systems-master-plan","location":"RÃo Cañas Industrial Park, Caguas, PR","municipality":"PR-CG","municipality_name":"Caguas","category":"Industrial Planning"},{"name":"Plaza Provision Company (PPC): New Distribution Center","type":"Urban Design","url":"https://audpr.com/plaza-provision-company-ppc-new-distribution-center","location":"Sabana Seca, PR","municipality":"PR-TB","municipality_name":"Toa Baja","category":"Industrial Planning"},{"name":"Roosevelt Roads Naval Base Master Plan","type":"Urban Design","url":"https://audpr.com/roosevelt-roads-naval-base","location":"Ceiba, PR","municipality":"PR-CB","municipality_name":"Ceiba","category":"Master Planning"},{"name":"Sabana Seca Eco Industrial Park: Urban Infrastructure (SSEIP)","type":"Urban Design","url":"https://audpr.com/sabana-seca-eco-industrial-park-sseip-urban-infrastructure","location":"Former Sabana Seca Naval Base, Toa Baja, PR","municipality":"PR-TB","municipality_name":"Toa Baja","category":"Urban Infrastructure Design / CPM"},{"name":"Science City: Boulevard Urban Infrastructure","type":"Urban Design","url":"https://audpr.com/science-city-boulevard-urban-infrastructure","location":"San Juan, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Urban Infrastructure Design / CPM"},{"name":"Science City: Laboratory Road","type":"Urban Design","url":"https://audpr.com/science-city-laboratory-road","location":"San Juan, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Urban Infrastructure Design / CPM"},{"name":"Science City : PR-21/PR-18 Interchange","type":"Urban Design","url":"https://audpr.com/science-city-pr-21-pr-18-intersection","location":"San Juan, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Urban Infrastructure Design / CPM"},{"name":"Tren Urbano (TOD, Joint Development)","type":"Urban Design","url":"https://audpr.com/tren-urbano-tod-joint-development","location":"San Juan Metropolitan Area, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Transit-Oriented Development"},{"name":"Tren Urbano Project (Transit Corridor Planning, Phase I Construction)","type":"Urban Design","url":"https://audpr.com/tren-urbano-transit-corridor-planning-phase-i-construction","location":"San Juan Metropolitan Area, PR","municipality":"PR-SJ","municipality_name":"San Juan","category":"Transit / Transportation Planning"},{"name":"VIVO Beach Club Boardwalk","type":"Urban Design","url":"https://audpr.com/vivo-beach-club-boardwalk","location":"Isla Verde, Carolina, PR","municipality":"PR-CN","municipality_name":"Carolina","category":"Master Planning"}],"islandwide":[{"name":"T&TH PRDE Program Management","type":"Architecture","url":"https://audpr.com/tth-prde-program-management","location":"Puerto Rico-Islandwide","municipality":"ISLANDWIDE","municipality_name":"PUERTO RICO â ISLANDWIDE","category":"Architectural Planning & Programming"},{"name":"Puerto Rico Department of Education (PRDE) Master Plan","type":"Urban Design","url":"https://audpr.com/prde-master-plan","location":"PR-Islandwide","municipality":"ISLANDWIDE","municipality_name":"PUERTO RICO â ISLANDWIDE","category":"Master Planning"}]}; 4517 4518 /* ========================================================== 4519 FUTURE-PROOF PROJECT REPOSITORY 4520 ---------------------------------------------------------- 4521 MANUAL ADD: 4522 Add new projects to MANUAL_PROJECTS using this shape: 4523 4524 { 4525 name: "PROJECT NAME", 4526 url: "https://audpr.com/project-slug/", 4527 type: "Architecture", // or "Urban Design" 4528 category: "Commercial Architecture", 4529 municipality: "PR-SJ", // SVG data-id; use null for islandwide 4530 municipalityName: "San Juan", 4531 thumbnail: "https://audpr.com/wp-content/uploads/..." 4532 } 4533 4534 AUTO DISCOVERY: 4535 When enabled, the map checks the two public project indexes 4536 for NEW project links. Existing hard-coded records remain the 4537 source of truth and are never replaced. 4538 ========================================================== */ 4539 4540 const REPOSITORY_CONFIG = { 4541 autoDiscovery: true, 4542 indexes: [ 4543 { url: 'https://audpr.com/architecture/', type: 'Architecture' }, 4544 { url: 'https://audpr.com/urban-design/', type: 'Urban Design' } 4545 ] 4546 }; 4547 4548 const MANUAL_PROJECTS = [ 4549 // Add future manual entries here. 4550 ]; 4551 4552const PROJECTS = DATA.projects; 4553 const ISLANDWIDE = DATA.islandwide; 4554 4555 function normalizeRepositoryUrl(url) { 4556 try { 4557 const u = new URL(url, location.origin); 4558 u.hash = ''; 4559 u.search = ''; 4560 return u.href.replace(/\/$/, '').toLowerCase(); 4561 } catch { 4562 return ''; 4563 } 4564 } 4565 4566 function repositoryHasUrl(url) {
4567 const key = normalizeRepositoryUrl(url); 4568 return [...PROJECTS, ...ISLANDWIDE].some( 4569 p => normalizeRepositoryUrl(p.url) === key 4570 ); 4571 } 4572 4573 function addRepositoryProject(project) { 4574 if (!project || !project.url || repositoryHasUrl(project.url)) return false; 4575 4576 const normalized = { 4577 name: project.name || 'UNTITLED PROJECT', 4578 url: project.url, 4579 type: project.type || 'Architecture', 4580 category: project.category || project.type || 'Project', 4581 municipality: project.municipality || null, 4582 municipalityName: project.municipalityName || 'PUERTO RICO / ISLANDWIDE' 4583 }; 4584 4585 if (normalized.municipality) { 4586 PROJECTS.push(normalized); 4587 } else { 4588 ISLANDWIDE.push(normalized); 4589 } 4590 4591 if (project.thumbnail) { 4592 PROJECT_THUMBNAILS.set( 4593 normalizeProjectUrl(project.url), 4594 project.thumbnail 4595 ); 4596 } 4597 4598 return true; 4599 } 4600 4601 function installManualProjects() { 4602 MANUAL_PROJECTS.forEach(addRepositoryProject); 4603 } 4604 4605 function headingBeforeLink(link) { 4606 const all = [...link.ownerDocument.querySelectorAll('h1,h2,h3,h4,h5,h6,a[href]')]; 4607 const at = all.indexOf(link); 4608 if (at < 0) return ''; 4609 4610 for (let i = at - 1; i >= 0; i--) { 4611 if (/^H[1-6]$/.test(all[i].tagName)) { 4612 return (all[i].textContent || '').replace(/\s+/g, ' ').trim(); 4613 } 4614 } 4615 return ''; 4616 } 4617 4618 function municipalityLookupFromSvg() { 4619 const lookup = new Map(); 4620 4621 // v0.10M: ESC resets PROJECT TYPE back to ALL PROJECT TYPES 4622 // and clears the current municipality selection/view. 4623 document.addEventListener('keydown', event => { 4624 if (event.key !== 'Escape') return; 4625 4626 if (categoryFilter !== 'all') { 4627 categoryFilter = 'all'; 4628 refreshProjectTypeOptions(); 4629 } 4630 4631 if (audOrbit) { 4632 closeProjectOrbit(true); 4633 } 4634 4635 clearSelection(); 4636 rebuildLabels(); 4637 }); 4638 4639 paths.forEach(path => { 4640 lookup.set( 4641 (path.dataset.name || '').toLowerCase(), 4642 { 4643 id: path.dataset.id, 4644 name: path.dataset.name 4645 } 4646 ); 4647 }); 4648 return lookup; 4649 } 4650 4651 function detectMunicipalityFromText(text) { 4652 const lower = (text || '').toLowerCase(); 4653 const lookup = municipalityLookupFromSvg(); 4654 4655 // Prefer explicit LOCATION metadata when present. 4656 const locMatch = (text || '').match( 4657 /LOCATION\s*:\s*([^\n|â¢]+)/i 4658 ); 4659 const locationText = locMatch ? locMatch[1].toLowerCase() : lower; 4660 4661 for (const [name, data] of lookup.entries()) { 4662 if (locationText.includes(name)) return data; 4663 } 4664 4665 return null; 4666 } 4667 4668 async function inspectDiscoveredProject(url, type, category, thumbnail) { 4669 try { 4670 const response = await fetch(url, { 4671 credentials: 'same-origin', 4672 cache: 'force-cache' 4673 }); 4674 if (!response.ok) return null; 4675 4676 const source = await response.text(); 4677 const doc = new DOMParser().parseFromString(source, 'text/html'); 4678 4679 const name = 4680 doc.querySelector('h1')?.textContent?.replace(/\s+/g, ' ').trim() || 4681 doc.querySelector('meta[property="og:title"]')?.content?.trim() || 4682 doc.title?.replace(/\s*[|ââ-].*$/, '').trim() || 4683 'UNTITLED PROJECT'; 4684 4685 const pageText = 4686 (doc.querySelector('main,article,.elementor')?.textContent || '') 4687 .replace(/\s+/g, ' ') 4688 .trim(); 4689 4690 const municipality = detectMunicipalityFromText(pageText); 4691 4692 return { 4693 name, 4694 url, 4695 type, 4696 category: category || type, 4697 municipality: municipality?.id || null, 4698 municipalityName: municipality?.name || 'PUERTO RICO / ISLANDWIDE', 4699 thumbnail: thumbnail || '' 4700 }; 4701 } catch (error) { 4702 console.warn('A+UD auto-discovery project inspection failed:', url, error); 4703 return null; 4704 } 4705 } 4706 4707 async function discoverNewProjectsFromIndexes() { 4708 if (!REPOSITORY_CONFIG.autoDiscovery) return; 4709 4710 let added = 0; 4711 4712 for (const index of REPOSITORY_CONFIG.indexes) { 4713 try { 4714 const response = await fetch(index.url, { 4715 credentials: 'same-origin', 4716 cache: 'force-cache' 4717 }); 4718 if (!response.ok) continue; 4719 4720 const source = await response.text(); 4721 const doc = new DOMParser().parseFromString(source, 'text/html'); 4722 4723 const links = [...doc.querySelectorAll('a[href]')]; 4724 4725 for (const link of links) { 4726 let url; 4727 try { 4728 url = new URL(link.getAttribute('href'), index.url).href; 4729 } catch { 4730 continue; 4731 } 4732 4733 if (new URL(url).origin !== location.origin) continue; 4734 if (repositoryHasUrl(url)) continue; 4735 4736 const pathname = new URL(url).pathname.toLowerCase(); 4737 4738 // Ignore obvious non-project destinations. 4739 if ( 4740 pathname === '/' || 4741 pathname.includes('/architecture/') && pathname === '/architecture/' || 4742 pathname.includes('/urban-design/') && pathname === '/urban-design/' || 4743 /\/(about|contact|staff|clients|awards|category|tag|author|wp-admin|wp-login)\b/.test(pathname) 4744 ) continue; 4745 4746 const category = headingBeforeLink(link) || index.type; 4747 4748 const img = 4749 link.querySelector('img') || 4750 link.closest('article,li,.elementor-element')?.querySelector('img'); 4751 4752 const thumbnail = 4753 img?.getAttribute('data-src') || 4754 img?.getAttribute('data-lazy-src') || 4755 img?.getAttribute('src') || 4756 ''; 4757 4758 const project = await inspectDiscoveredProject( 4759 url, 4760 index.type, 4761 category, 4762 thumbnail 4763 ); 4764 4765 if (project && addRepositoryProject(project)) { 4766 added++; 4767 } 4768 } 4769 } catch (error) { 4770 console.warn('A+UD auto-discovery index failed:', index.url, error); 4771 } 4772 } 4773 4774 if (added > 0) { 4775 refreshProjectTypeOptions(); 4776 installManualProjects(); 4777 4778 totalProjectsEl.textContent = PROJECTS.length + ISLANDWIDE.length; 4779 bottomTotal.textContent = PROJECTS.length + ISLANDWIDE.length; 4780 rebuildLabels(); 4781 4782 if (selected) { 4783 selectPath(selected); 4784 } else if (viewAllMode) { 4785 showAllProjects(); 4786 } else { 4787 setTerritoryOverview(); 4788 } 4789 4790 console.info(`A+UD repository: ${added} new project(s) discovered.`); 4791 } 4792 } 4793 4794 4795 4796 const map = root.querySelector('#mapSvg'); 4797 const mapFrame = root.querySelector('#mapFrame'); 4798 const labels = root.querySelector('#mapLabels'); 4799 const paths = [...root.querySelectorAll('.municipality')]; 4800 paths.forEach(path => { 4801 // Hook for the custom cursor without native browser focus UI.
4802 path.classList.add('aud-cursor-interactive'); 4803 path.setAttribute( 4804 'aria-label', 4805 `${path.dataset.name || 'Municipality'} municipality` 4806 ); 4807 }); 4808 4809 const municipalityName = root.querySelector('#municipalityName'); 4810 const municipalityFootprint = root.querySelector('#municipalityFootprint'); 4811 const sideProjectScroll = root.querySelector('#sideProjectScroll'); 4812 const scopeIslandwide = root.querySelector('#scopeIslandwide'); 4813 const scopeRegional = root.querySelector('#scopeRegional'); 4814 const projectTypePicker = root.querySelector('#projectTypePicker'); 4815 const projectTypeTrigger = root.querySelector('#projectTypeTrigger'); 4816 const projectTypeTriggerLabel = root.querySelector('.project-type-trigger-label'); 4817 const projectTypeMenu = root.querySelector('#projectTypeMenu'); 4818 const municipalityProjects = root.querySelector('#municipalityProjects'); 4819 const architectureCount = root.querySelector('#architectureCount'); 4820 const urbanCount = root.querySelector('#urbanCount'); 4821 const projectList = root.querySelector('#projectList'); 4822 const mapTitle = root.querySelector('#mapTitle'); 4823 const listTitle = root.querySelector('#listTitle'); 4824 const overviewLabel = root.querySelector('#overviewLabel'); 4825 const totalProjectsEl = root.querySelector('#totalProjects'); 4826 const bottomTotal = root.querySelector('#bottomTotal'); 4827 const themeToggle = root.querySelector('#themeToggle'); 4828 4829 let selected = null; 4830 let filter = 'all'; 4831 let categoryFilter = 'all'; 4832 let viewAllMode = false; 4833 4834 function filteredProjects(list) { 4835 return list.filter(p => { 4836 const typeMatch = filter === 'all' || p.type === filter; 4837 const categoryMatch = categoryFilter === 'all' || p.category === categoryFilter; 4838 return typeMatch && categoryMatch; 4839 }); 4840 } 4841 4842 4843 function projectTypesForCurrentSection() { 4844 const base = [...PROJECTS, ...ISLANDWIDE].filter( 4845 p => filter === 'all' || p.type === filter 4846 ); 4847 return [...new Set(base.map(p => p.category).filter(Boolean))] 4848 .sort((a,b) => a.localeCompare(b)); 4849 } 4850 4851 function refreshProjectTypeOptions() { 4852 if (!projectTypeMenu) return; 4853 4854 const categories = projectTypesForCurrentSection(); 4855 4856 if ( 4857 categoryFilter !== 'all' && 4858 !categories.includes(categoryFilter) 4859 ) { 4860 categoryFilter = 'all'; 4861 } 4862 4863 projectTypeMenu.innerHTML = ''; 4864 4865 const options = [ 4866 { value: 'all', label: 'ALL PROJECT TYPES' }, 4867 ...categories.map(category => ({ 4868 value: category, 4869 label: category 4870 })) 4871 ]; 4872 4873 options.forEach((item, index) => { 4874 const option = document.createElement('button'); 4875 option.type = 'button'; 4876 option.className = 'project-type-option'; 4877 option.dataset.value = item.value; 4878 option.setAttribute('role', 'option'); 4879 option.setAttribute( 4880 'aria-selected', 4881 String(item.value === categoryFilter) 4882 ); 4883 4884 const number = document.createElement('span'); 4885 number.className = 'project-type-option-index'; 4886 number.textContent = String(index).padStart(2, '0'); 4887 4888 const label = document.createElement('span'); 4889 label.className = 'project-type-option-label'; 4890 label.textContent = item.label; 4891 4892 const active = document.createElement('span'); 4893 active.className = 'project-type-option-active'; 4894 active.textContent = item.value === categoryFilter ? 'â' : ''; 4895 4896 option.append(number, label, active); 4897 4898 projectTypeMenu.appendChild(option); 4899 }); 4900 if (projectTypeTriggerLabel) { 4901 projectTypeTriggerLabel.textContent = 4902 categoryFilter === 'all' 4903 ? 'PROJECT TYPE' 4904 : categoryFilter.toUpperCase(); 4905 projectTypeTrigger.title = 4906 categoryFilter === 'all' 4907 ? 'Project Type' 4908 : categoryFilter; 4909 } 4910 4911 } 4912 4913 function openProjectTypeMenu() { 4914 if (!projectTypePicker || !projectTypeMenu || !projectTypeTrigger) return; 4915 projectTypePicker.classList.add('is-open'); 4916 projectTypeMenu.setAttribute('aria-hidden', 'false'); 4917 projectTypeTrigger.setAttribute('aria-expanded', 'true'); 4918 } 4919 4920 function closeProjectTypeMenu() { 4921 if (!projectTypePicker || !projectTypeMenu || !projectTypeTrigger) return; 4922 projectTypePicker.classList.remove('is-open'); 4923 projectTypeMenu.setAttribute('aria-hidden', 'true'); 4924 projectTypeTrigger.setAttribute('aria-expanded', 'false'); 4925 } 4926 4927 function toggleProjectTypeMenu() { 4928 if (projectTypePicker?.classList.contains('is-open')) { 4929 closeProjectTypeMenu(); 4930 } else { 4931 openProjectTypeMenu(); 4932 } 4933 } 4934 4935function allRepositoryFilteredProjects() { 4936 return filteredProjects([...PROJECTS, ...ISLANDWIDE]); 4937 } 4938 4939 function allFilteredProjects() { 4940 return filteredProjects(PROJECTS); 4941 } 4942 4943 function projectsForPath(path) { 4944 const id = path.dataset.id; 4945 return filteredProjects( 4946 PROJECTS.filter(p => (p.municipality || p.municipalityId) === id) 4947 ); 4948 } 4949 4950 function rebuildLabels() { 4951 labels.innerHTML = ''; 4952 4953 const svg = map.querySelector('svg'); 4954 if (!svg) return; 4955 4956 let markerLayer = svg.querySelector('.aud-svg-marker-layer'); 4957 if (!markerLayer) { 4958 markerLayer = document.createElementNS('http://www.w3.org/2000/svg', 'g'); 4959 markerLayer.setAttribute('class', 'aud-svg-marker-layer'); 4960 svg.appendChild(markerLayer); 4961 } 4962 markerLayer.innerHTML = ''; 4963 4964 4965 paths.forEach(path => { 4966 const projects = projectsForPath(path); 4967 const hasProjects = projects.length > 0; 4968 4969 const box = path.getBBox(); 4970 let cx = box.x + (box.width / 2); 4971 const cy = box.y + (box.height / 2); 4972 // Mayagüez: STATIC marker anchor. The SVG path includes offshore geometry, 4973 // so do not recalculate from sampled path points. Keep the marker fixed 4974 // toward the mainland side of the municipality bbox. 4975 if (path.dataset.name === 'Mayagüez') { 4976 cx = box.x + (box.width * 0.90); 4977 } 4978 4979 const group = document.createElementNS( 4980 'http://www.w3.org/2000/svg', 4981 'g' 4982 ); 4983 4984 group.setAttribute('class', 'aud-svg-marker'); 4985 group.setAttribute('data-id', path.dataset.id); 4986 group.setAttribute('data-name', path.dataset.name); 4987 group.setAttribute('data-state', 'idle'); 4988 group.setAttribute('transform', `translate(${cx} ${cy})`); 4989 4990 const name = document.createElementNS( 4991 'http://www.w3.org/2000/svg', 4992 'text' 4993 ); 4994 name.setAttribute('class', 'aud-svg-marker-name'); 4995 name.setAttribute('x', '0'); 4996 name.setAttribute('y', '-10'); 4997 name.setAttribute('text-anchor', 'middle'); 4998 name.textContent = path.dataset.name; 4999 5000 const count = document.createElementNS( 5001 'http://www.w3.org/2000/svg', 5002 'text' 5003 ); 5004 count.setAttribute('class', 'aud-svg-marker-count'); 5005 count.setAttribute('x', '0'); 5006 count.setAttribute('y', '1'); 5007 count.setAttribute('text-anchor', 'middle'); 5008 count.textContent = 5009 `${projects.length} PROJECT${projects.length === 1 ? '' : 'S'}`; 5010 5011 group.appendChild(name); 5012 group.appendChild(count); 5013 5014 if (hasProjects) { 5015 const plus = document.createElementNS( 5016 'http://www.w3.org/2000/svg', 5017 'text' 5018 ); 5019 plus.setAttribute('class', 'aud-svg-marker-plus'); 5020 plus.setAttribute('x', '0'); 5021 plus.setAttribute('y', '17'); 5022 plus.setAttribute('text-anchor', 'middle'); 5023 plus.textContent = '+'; 5024 group.appendChild(plus); 5025 } 5026 5027 // IMPORTANT: marker text must NOT create its own sticky hover state. 5028 // Hover visibility is controlled only by the actual municipality path. 5029 // Clicking the marker still selects the municipality. 5030 group.addEventListener('click', e => { 5031 e.stopPropagation(); 5032 selectPath(path); 5033 }); 5034 5035 markerLayer.appendChild(group); 5036 }); 5037 5038 labels.innerHTML = ''; 5039 } 5040 5041 function syncMarkerSelection(selectedPath) { 5042 const markerLayer = map.querySelector('.aud-svg-marker-layer'); 5043 if (!markerLayer) return; 5044 5045 markerLayer.querySelectorAll('.aud-svg-marker').forEach(marker => { 5046 marker.setAttribute( 5047 'data-state', 5048 marker.dataset.id === selectedPath.dataset.id ? 'selected' : 'idle' 5049 ); 5050 }); 5051 } 5052 5053 function syncMarkerHover(path, hovering) { 5054 const markerLayer = map.querySelector('.aud-svg-marker-layer'); 5055 if (!markerLayer || !path) return; 5056 5057 const marker = markerLayer.querySelector( 5058 `.aud-svg-marker[data-id="${path.dataset.id}"]` 5059 ); 5060 if (!marker) return; 5061 5062 if (selected === path) { 5063 marker.setAttribute('data-state', 'selected'); 5064 } else { 5065 marker.setAttribute('data-state', hovering ? 'hover' : 'idle'); 5066 } 5067 } 5068 5069 5070 5071 /* ============================================================ 5072 v0.9A NON-STICKY HOVER LABELS / v0.7B PROJECT PREVIEW CACHE 5073 Thumbnails are loaded BEFORE a card is expanded. 5074 Primary sources: 5075 1) Architecture / Urban Design index cards 5076 2) WordPress REST featured_media fallback 5077 ============================================================ */ 5078 5079 const PROJECT_THUMBNAILS = new Map(); 5080 let thumbnailPreloadPromise = null; 5081 5082 function normalizeProjectUrl(url) { 5083 try { 5084 const u = new URL(url, window.location.origin); 5085 u.hash = ''; 5086 u.search = ''; 5087 u.pathname = u.pathname.replace(/\/+$/, '') || '/'; 5088 return u.origin + u.pathname; 5089 } catch (_) { 5090 return String(url || '').replace(/\/+$/, ''); 5091 } 5092 } 5093 5094 function isRejectedPreviewAsset(url) { 5095 const low = String(url || '').toLowerCase(); 5096 return !low || 5097 /aud-logo|cruz-2\.png|logo|favicon|icon|avatar|gravatar|profile|placeholder|dummy|spacer|transparent|loading|loader|spinner|social|facebook|instagram|twitter|youtube|linkedin/.test(low); 5098 } 5099 5100 function largestSrcsetUrl(srcset, baseUrl) { 5101 if (!srcset) return ''; 5102 const items = srcset.split(',').map(x => x.trim()).map(x => { 5103 const parts = x.split(/\s+/); 5104 return { url: parts[0], size: parseInt(parts[1] || '0', 10) || 0 }; 5105 }).filter(x => x.url).sort((a,b) => b.size - a.size); 5106 if (!items.length) return ''; 5107 try { return new URL(items[0].url, baseUrl).href; } catch (_) { return ''; } 5108 } 5109 5110 function imageUrlFromNode(img, baseUrl) { 5111 if (!img) return ''; 5112 const candidates = [ 5113 img.getAttribute?.('data-full-url'), 5114 img.getAttribute?.('data-large-image'), 5115 img.getAttribute?.('data-zoom-image'), 5116 largestSrcsetUrl(img.getAttribute?.('data-srcset'), baseUrl), 5117 largestSrcsetUrl(img.getAttribute?.('srcset'), baseUrl), 5118 img.getAttribute?.('data-lazy-src'), 5119 img.getAttribute?.('data-src'), 5120 img.getAttribute?.('src') 5121 ]; 5122 for (const raw of candidates) { 5123 if (!raw) continue;
5124 try { 5125 const absolute = new URL(raw, baseUrl).href; 5126 if (!isRejectedPreviewAsset(absolute)) return absolute; 5127 } catch (_) {} 5128 } 5129 return ''; 5130 } 5131 5132 function findIndexThumbnailForLink(link, baseUrl) { 5133 // Walk upward until we reach the smallest ancestor that contains a useful 5134 // image. This matches Elementor portfolio/loop cards much more reliably 5135 // than scraping the project page header. 5136 let node = link; 5137 for (let depth = 0; node && depth < 8; depth++, node = node.parentElement) { 5138 const imgs = [...node.querySelectorAll?.('img') || []]; 5139 for (const img of imgs) { 5140 const found = imageUrlFromNode(img, baseUrl); 5141 if (found) return found; 5142 } 5143 const style = node.getAttribute?.('style') || ''; 5144 const bg = style.match(/background-image\s*:\s*url\((['\"]?)(.*?)\1\)/i); 5145 if (bg?.[2]) { 5146 try { 5147 const found = new URL(bg[2], baseUrl).href; 5148 if (!isRejectedPreviewAsset(found)) return found; 5149 } catch (_) {} 5150 } 5151 } 5152 return ''; 5153 } 5154 5155 async function harvestIndexThumbnails(indexUrl) { 5156 try { 5157 const response = await fetch(indexUrl, { credentials:'same-origin', cache:'force-cache' }); 5158 if (!response.ok) return; 5159 const html = await response.text(); 5160 const doc = new DOMParser().parseFromString(html, 'text/html'); 5161 const known = new Set([...PROJECTS, ...ISLANDWIDE].map(p => normalizeProjectUrl(p.url))); 5162 5163 doc.querySelectorAll('a[href]').forEach(link => { 5164 let target; 5165 try { target = normalizeProjectUrl(new URL(link.getAttribute('href'), indexUrl).href); } 5166 catch (_) { return; } 5167 if (!known.has(target) || PROJECT_THUMBNAILS.has(target)) return; 5168 const image = findIndexThumbnailForLink(link, indexUrl); 5169 if (image) PROJECT_THUMBNAILS.set(target, image); 5170 }); 5171 } catch (error) { 5172 console.warn('A+UD index thumbnail harvest failed:', indexUrl, error); 5173 } 5174 } 5175 5176 async function harvestWpFeaturedMedia() { 5177 // WordPress featured images are the closest thing to the manually assigned 5178 // project thumbnails, so use them as a fallback when an index card did not 5179 // yield an image. 5180 try { 5181 const response = await fetch('/wp-json/wp/v2/pages?per_page=100&_embed=1', { 5182 credentials:'same-origin', cache:'force-cache' 5183 }); 5184 if (!response.ok) return; 5185 const pages = await response.json(); 5186 pages.forEach(page => { 5187 const key = normalizeProjectUrl(page.link || ''); 5188 if (!key || PROJECT_THUMBNAILS.has(key)) return; 5189 const media = page?._embedded?.['wp:featuredmedia']?.[0]; 5190 const sizes = media?.media_details?.sizes || {}; 5191 const image = 5192 sizes.large?.source_url || 5193 sizes.medium_large?.source_url || 5194 sizes.medium?.source_url || 5195 media?.source_url || ''; 5196 if (image && !isRejectedPreviewAsset(image)) { 5197 PROJECT_THUMBNAILS.set(key, image); 5198 } 5199 }); 5200 } catch (error) { 5201 console.warn('A+UD WP featured-media preload failed:', error); 5202 } 5203 } 5204 5205 function setThumbnail(thumbWrap, project, fallbackText = 'NO PROJECT IMAGE') { 5206 if (!thumbWrap || !project) return; 5207 const url = project.thumbnail || PROJECT_THUMBNAILS.get(normalizeProjectUrl(project.url)); 5208 if (!url) { 5209 thumbWrap.innerHTML = `<span class="aud-project-no-image">${fallbackText}</span>`; 5210 return; 5211 } 5212 project.thumbnail = url; 5213 const img = document.createElement('img'); 5214 img.className = 'aud-project-thumb'; 5215 img.loading = 'lazy'; 5216 img.decoding = 'async'; 5217 img.alt = project.name || 'Project image'; 5218 img.addEventListener('error', () => { 5219 PROJECT_THUMBNAILS.delete(normalizeProjectUrl(project.url)); 5220 project.thumbnail = ''; 5221 thumbWrap.innerHTML = '<span class="aud-project-no-image">NO PROJECT IMAGE</span>'; 5222 }, { once:true }); 5223 img.src = url; 5224 thumbWrap.replaceChildren(img); 5225 } 5226 5227 function refreshVisibleThumbnails() { 5228 root.querySelectorAll('.aud-project-card[data-project-url]').forEach(card => { 5229 const project = [...PROJECTS, ...ISLANDWIDE].find( 5230 p => normalizeProjectUrl(p.url) === card.dataset.projectUrl 5231 ); 5232 if (!project) return; 5233 setThumbnail(card.querySelector('.aud-project-thumb-wrap'), project, 'PREVIEW'); 5234 }); 5235 } 5236 5237 async function harvestMissingFeaturedMedia() { 5238 const missing = [...PROJECTS, ...ISLANDWIDE].filter( 5239 p => !PROJECT_THUMBNAILS.has(normalizeProjectUrl(p.url)) 5240 ); 5241 const queue = [...missing]; 5242 const workers = Array.from({ length: Math.min(4, queue.length) }, async () => { 5243 while (queue.length) { 5244 const project = queue.shift(); 5245 if (!project) break; 5246 let slug = ''; 5247 try { 5248 const parts = new URL(project.url, window.location.origin).pathname
5249 .split('/').filter(Boolean); 5250 slug = parts[parts.length - 1] || ''; 5251 } catch (_) {} 5252 if (!slug) continue; 5253 5254 try { 5255 const response = await fetch( 5256 `/wp-json/wp/v2/pages?slug=${encodeURIComponent(slug)}&_embed=1`, 5257 { credentials:'same-origin', cache:'force-cache' } 5258 ); 5259 if (!response.ok) continue; 5260 const pages = await response.json(); 5261 const page = pages?.[0]; 5262 const media = page?._embedded?.['wp:featuredmedia']?.[0]; 5263 const sizes = media?.media_details?.sizes || {}; 5264 const image = 5265 sizes.large?.source_url || 5266 sizes.medium_large?.source_url || 5267 sizes.medium?.source_url || 5268 media?.source_url || ''; 5269 if (image && !isRejectedPreviewAsset(image)) { 5270 PROJECT_THUMBNAILS.set(normalizeProjectUrl(project.url), image); 5271 } 5272 } catch (_) {} 5273 } 5274 }); 5275 await Promise.all(workers); 5276 } 5277 5278 function preloadProjectThumbnails() { 5279 if (thumbnailPreloadPromise) return thumbnailPreloadPromise; 5280 thumbnailPreloadPromise = Promise.all([ 5281 harvestIndexThumbnails('/architecture/'), 5282 harvestIndexThumbnails('/urban-design/') 5283 ]) 5284 .then(harvestWpFeaturedMedia) 5285 .then(harvestMissingFeaturedMedia) 5286 .then(() => { 5287 [...PROJECTS, ...ISLANDWIDE].forEach(p => { 5288 p.thumbnail = PROJECT_THUMBNAILS.get(normalizeProjectUrl(p.url)) || p.thumbnail || ''; 5289 }); 5290 refreshVisibleThumbnails(); 5291 }).catch(error => { 5292 console.warn('A+UD thumbnail preload failed:', error); 5293 }); 5294 return thumbnailPreloadPromise; 5295 } 5296 5297 const PROJECT_DESCRIPTIONS = new Map(); 5298 const PROJECT_DESCRIPTION_PROMISES = new Map(); 5299 5300 function normalizePreviewText(text) { 5301 return String(text || '').replace(/\s+/g, ' ').trim(); 5302 } 5303 5304 function escapeRegExp(value) { 5305 return String(value || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); 5306 } 5307 5308 function cleanProjectDescription(raw, project) { 5309 let text = normalizePreviewText(raw); 5310 if (!text) return ''; 5311 5312 const title = normalizePreviewText(project?.name || ''); 5313 const location = normalizePreviewText(project?.location || ''); 5314 5315 // Strip repeated title(s) from the beginning. 5316 if (title) { 5317 const titleRE = new RegExp('^(?:\\s*' + escapeRegExp(title) + '\\s*)+', 'i'); 5318 text = text.replace(titleRE, '').trim(); 5319 } 5320 5321 // Strip any obvious leading location line already represented elsewhere. 5322 if (location) { 5323 text = text.replace( 5324 new RegExp('^\\s*(?:LOCATION\\s*:\\s*)?' + escapeRegExp(location) + '\\s*', 'i'), 5325 '' 5326 ).trim(); 5327 } 5328 5329 // A+UD pages generally begin with: 5330 // LOCATION: ... CLIENT: ... DESIGN: ... CONST. COST: ... [actual prose] 5331 // If CONST. COST exists, everything before it is redundant card metadata. 5332 const costMatch = /CONST\.?\s*COST\s*:/i.exec(text); 5333 if (costMatch) { 5334 text = text.slice(costMatch.index + costMatch[0].length).trim(); 5335 5336 // Remove only the short cost value after the label, never the prose. 5337 text = text.replace( 5338 /^\s*(?:(?:\$\s*)?[\d,.]+(?:\+)?(?:\s*(?:MM|M|B|K|BILL(?:ION)?|MILLION))?(?:\s*\([^)]*\))?|N\/?A|TBD|â|-)\s*/i, 5339 '' 5340 ).trim(); 5341 } else { 5342 // Pages without a cost label: remove leading metadata fields individually. 5343 text = text 5344 .replace(/^\s*LOCATION\s*:.*?(?=CLIENT\s*:|DESIGN\s*:|CONST\.?\s*COST\s*:|$)/i, '') 5345 .replace(/^\s*CLIENT\s*:.*?(?=DESIGN\s*:|CONST\.?\s*COST\s*:|$)/i, '') 5346 .replace(/^\s*DESIGN\s*:.*?(?=CONST\.?\s*COST\s*:|$)/i, '') 5347 .trim(); 5348 } 5349 5350 // Elementor sometimes repeats title + body later in the same text blob. 5351 if (title) { 5352 const duplicateAt = text.toLowerCase().indexOf(title.toLowerCase(), 80); 5353 if (duplicateAt > 80) text = text.slice(0, duplicateAt).trim(); 5354 } 5355 5356 // Trim any trailing metadata/navigation that survives parsing. 5357 text = text 5358 .split(/\s+LOCATION\s*:/i)[0] 5359 .split(/\bARCHITECTURE\s+URBAN DESIGN\s+ABOUT US/i)[0] 5360 .trim(); 5361 5362 if (text.length > 560) text = text.slice(0, 557).trimEnd() + 'â¦'; 5363 return text; 5364 } 5365 5366 function extractProjectDescription(doc, project) { 5367 const title = normalizePreviewText(project?.name || '').toLowerCase(); 5368 5369 // Prefer actual prose paragraphs. Do NOT use the entire Elementor container 5370 // first, because that is where title/location/client metadata gets merged. 5371 const paragraphs = [...doc.querySelectorAll( 5372 'main p, article p, .elementor-widget-text-editor p' 5373 )] 5374 .map(node =>
5374 normalizePreviewText(node.textContent)) 5375 .filter(Boolean) 5376 .filter(text => text.length > 55) 5377 .filter(text => !title || text.toLowerCase() !== title) 5378 .filter(text => !/^(LOCATION|CLIENT|DESIGN|CONST\.?\s*COST)\s*:/i.test(text)) 5379 .filter(text => !/ARCHITECTURE\s+URBAN DESIGN\s+ABOUT US/i.test(text)); 5380 5381 for (const paragraph of paragraphs) { 5382 const cleaned = cleanProjectDescription(paragraph, project); 5383 if ( 5384 cleaned.length > 55 && 5385 !/\bLOCATION\s*:.*\bCLIENT\s*:/i.test(cleaned) && 5386 !/\bCLIENT\s*:.*\bDESIGN\s*:/i.test(cleaned) 5387 ) return cleaned; 5388 } 5389 5390 // Fallback to the page body, then apply the stronger metadata cleaner. 5391 const main = doc.querySelector('main, article, .elementor') || doc.body; 5392 return cleanProjectDescription(main?.textContent || '', project); 5393 } 5394 5395 async function fetchProjectDescription(project) { 5396 const key = normalizeProjectUrl(project?.url || ''); 5397 if (!key) return ''; 5398 if (PROJECT_DESCRIPTIONS.has(key)) return PROJECT_DESCRIPTIONS.get(key); 5399 if (PROJECT_DESCRIPTION_PROMISES.has(key)) return PROJECT_DESCRIPTION_PROMISES.get(key); 5400 5401 const promise = (async () => { 5402 const controller = new AbortController(); 5403 const timer = setTimeout(() => controller.abort(), 9000); 5404 try { 5405 const response = await fetch(project.url, { 5406 credentials: 'same-origin', 5407 cache: 'force-cache', 5408 signal: controller.signal 5409 }); 5410 if (!response.ok) throw new Error('HTTP ' + response.status); 5411 5412 const source = await response.text(); 5413 const doc = new DOMParser().parseFromString(source, 'text/html'); 5414 const description = extractProjectDescription(doc, project); 5415 PROJECT_DESCRIPTIONS.set(key, description || ''); 5416 return description || ''; 5417 } finally { 5418 clearTimeout(timer); 5419 PROJECT_DESCRIPTION_PROMISES.delete(key); 5420 } 5421 })(); 5422 5423 PROJECT_DESCRIPTION_PROMISES.set(key, promise); 5424 return promise; 5425 } 5426 5427 function warmProjectDescriptions(projects) { 5428 const queue = projects 5429 .filter(p => p?.url) 5430 .filter(p => !PROJECT_DESCRIPTIONS.has(normalizeProjectUrl(p.url))) 5431 .slice(0, 8); 5432 5433 // Warm only the currently visible municipality/project set. Do not crawl 5434 // the whole archive in the background. 5435 const workers = Array.from({ length: Math.min(2, queue.length) }, async () => { 5436 while (queue.length) { 5437 const project = queue.shift(); 5438 try { await fetchProjectDescription(project); } catch (_) {} 5439 } 5440 }); 5441 return Promise.all(workers); 5442 } 5443 5444 function renderProjects(projects) { 5445 const list = document.getElementById('projectList'); 5446 if (!list) return; 5447 list.innerHTML = ''; 5448 5449 if (!projects.length) { 5450 list.innerHTML = '<div class="aud-project-empty">NO PROJECTS IN THIS VIEW</div>'; 5451 return; 5452 } 5453 5454 projects.forEach((project, index) => { 5455 const card = document.createElement('article'); 5456 card.className = 'aud-project-card'; 5457 card.dataset.projectIndex = index; 5458 card.dataset.projectUrl = normalizeProjectUrl(project.url || ''); 5459 5460 const row = document.createElement('button'); 5461 row.type = 'button'; 5462 row.className = 'aud-project-row aud-cursor-interactive'; 5463 row.setAttribute('aria-expanded', 'false'); 5464 5465 const thumbWrap = document.createElement('span'); 5466 thumbWrap.className = 'aud-project-thumb-wrap'; 5467 setThumbnail(thumbWrap, project, 'PREVIEW'); 5468 5469 const label = document.createElement('span'); 5470 label.className = 'aud-project-label'; 5471 5472 const title = document.createElement('span'); 5473 title.className = 'aud-project-title'; 5474 title.textContent = project.name || 'UNTITLED PROJECT'; 5475 5476 const meta = document.createElement('span'); 5477 meta.className = 'aud-project-meta'; 5478 meta.textContent = project.category || project.type || ''; 5479 5480 label.append(title, meta); 5481 5482 const toggle = document.createElement('span'); 5483 toggle.className = 'aud-project-toggle'; 5484 toggle.textContent = '+'; 5485 5486 row.append(thumbWrap, label, toggle); 5487 5488 const detail = document.createElement('div'); 5489 detail.className = 'aud-project-detail'; 5490 detail.setAttribute('aria-hidden', 'true'); 5491 5492 const copy = document.createElement('div'); 5493 copy.className = 'aud-project-copy'; 5494 5495 const desc = document.createElement('p'); 5496 desc.className = 'aud-project-description'; 5497
5498 const key = normalizeProjectUrl(project.url || ''); 5499 const cachedDescription = PROJECT_DESCRIPTIONS.get(key) || ''; 5500 desc.textContent = cachedDescription || 'PROJECT DETAILS'; 5501 copy.appendChild(desc); 5502 5503 if (project.url) { 5504 const open = document.createElement('a'); 5505 open.className = 'aud-project-open'; 5506 open.href = project.url; 5507 open.textContent = 'OPEN PROJECT â'; 5508 open.target = '_self'; 5509 copy.appendChild(open); 5510 } 5511 5512 detail.appendChild(copy); 5513 card.append(row, detail); 5514 list.appendChild(card); 5515 5516 row.addEventListener('click', async () => { 5517 const wasOpen = card.classList.contains('is-open'); 5518 5519 list.querySelectorAll('.aud-project-card.is-open').forEach(other => { 5520 if (other === card) return; 5521 other.classList.remove('is-open'); 5522 other.querySelector('.aud-project-row')?.setAttribute('aria-expanded', 'false'); 5523 const d = other.querySelector('.aud-project-detail'); 5524 const tg = other.querySelector('.aud-project-toggle'); 5525 if (d) d.setAttribute('aria-hidden', 'true'); 5526 if (tg) tg.textContent = '+'; 5527 }); 5528 5529 card.classList.toggle('is-open', !wasOpen); 5530 row.setAttribute('aria-expanded', String(!wasOpen)); 5531 detail.setAttribute('aria-hidden', String(wasOpen)); 5532 toggle.textContent = wasOpen ? '+' : 'â'; 5533 5534 if (!wasOpen && project.url) { 5535 const cached = PROJECT_DESCRIPTIONS.get(key); 5536 if (cached) { 5537 desc.textContent = cached; 5538 card.dataset.loaded = 'true'; 5539 } else if (card.dataset.loaded !== 'loading') { 5540 card.dataset.loaded = 'loading'; 5541 desc.textContent = 'LOADING PROJECT DETAILSâ¦'; 5542 try { 5543 const description = await fetchProjectDescription(project); 5544 desc.textContent = description || 'PROJECT DETAILS AVAILABLE ON THE PROJECT PAGE.'; 5545 card.dataset.loaded = 'true'; 5546 } catch (error) { 5547 console.warn('A+UD project preview failed:', project.url, error); 5548 desc.textContent = 'PROJECT DETAILS AVAILABLE ON THE PROJECT PAGE.'; 5549 card.dataset.loaded = 'error'; 5550 } 5551 } 5552 } 5553 }); 5554 }); 5555 5556 warmProjectDescriptions(projects); 5557 } 5558 5559 5560 function clearMunicipalityFootprint() { 5561 if (!municipalityFootprint) return; 5562 municipalityFootprint.innerHTML = 5563 '<div class="municipality-footprint-empty">SELECT<br>MUNICIPALITY</div>'; 5564 } 5565 5566 function renderMunicipalityFootprint(path) { 5567 if (!municipalityFootprint || !path) return; 5568 5569 const box = path.getBBox(); 5570 if (!box || !box.width || !box.height) { 5571 clearMunicipalityFootprint(); 5572 return; 5573 } 5574 5575 const padX = Math.max(box.width * 0.12, 8); 5576 const padY = Math.max(box.height * 0.12, 8); 5577 5578 const svgNS = 'http://www.w3.org/2000/svg'; 5579 const preview = document.createElementNS(svgNS, 'svg'); 5580 preview.setAttribute( 5581 'viewBox', 5582 `${box.x - padX} ${box.y - padY} ${box.width + padX * 2} ${box.height + padY * 2}` 5583 ); 5584 preview.setAttribute('preserveAspectRatio', 'xMidYMid meet'); 5585 preview.setAttribute('aria-label', `${path.dataset.name} municipality footprint`); 5586 5587 const clone = path.cloneNode(true); 5588 clone.removeAttribute('id'); 5589 clone.removeAttribute('class'); 5590 clone.removeAttribute('data-id'); 5591 clone.removeAttribute('data-name'); 5592 5593 preview.appendChild(clone); 5594 municipalityFootprint.replaceChildren(preview); 5595 } 5596 5597 function resetProjectScroll() { 5598 if (sideProjectScroll) { 5599 sideProjectScroll.scrollTop = 0; 5600 } 5601 } 5602 5603 5604 function setScopeMode(mode) { 5605 if (!scopeIslandwide || !scopeRegional) return; 5606 const regional = mode === 'regional'; 5607 scopeIslandwide.classList.toggle('is-active', !regional); 5608 scopeRegional.classList.toggle('is-active', regional); 5609 } 5610 5611function setTerritoryOverview() { 5612 setScopeMode('islandwide'); 5613 5614 const list = allRepositoryFilteredProjects(); 5615 const arch = list.filter(p => p.type === 'Architecture').length; 5616 const urban = list.filter(p => p.type === 'Urban Design').length; 5617 5618 municipalityName.textContent = 'PUERTO RICO'; 5619 municipalityProjects.textContent = String(list.length).padStart(2, '0'); 5620 architectureCount.textContent = arch ? String(arch).padStart(2, '0') : 'â'; 5621 urbanCount.textContent = urban ? String(urban).padStart(2, '0') : 'â'; 5622 5623 overviewLabel.textContent = 5624 categoryFilter === 'all' 5625 ? 'TERRITORY OVERVIEW' 5626 : categoryFilter.toUpperCase(); 5627 5628 clearMunicipalityFootprint(); 5629 resetProjectScroll(); 5630 5631 const broadLabel = 5632 filter === 'all' 5633 ? '' 5634 : `${filter.toUpperCase()} · `; 5635 5636 const subtypeLabel = 5637 categoryFilter === 'all' 5638 ? 'ALL PROJECT TYPES' 5639 : categoryFilter.toUpperCase(); 5640 5641 mapTitle.innerHTML = 5642 `<strong>PUERTO RICO</strong><span>${broadLabel}${subtypeLabel} · ${list.length} PROJECT${list.length === 1 ? '' : 'S'}</span>`;
5643 5644 // IMPORTANT: no municipality selected = global repository results, 5645 // including the stock/default ALL view. 5646 renderProjects(list); 5647 } 5648 5649 function selectPath(path) { 5650 setScopeMode('regional'); 5651 // v0.9A: only ONE municipality label may remain selected/visible. 5652 const markerLayer = map.querySelector('.aud-svg-marker-layer'); 5653 if (markerLayer) { 5654 markerLayer.querySelectorAll('.aud-svg-marker').forEach(marker => { 5655 marker.setAttribute('data-state', 'idle'); 5656 }); 5657 } 5658 5659 paths.forEach(p => p.classList.remove('active')); 5660 path.classList.add('active'); 5661 map.classList.add('dim'); 5662 selected = path; 5663 viewAllMode = false; 5664 5665 const projects = projectsForPath(path); 5666 const arch = projects.filter(p => p.type === 'Architecture').length; 5667 const urban = projects.filter(p => p.type === 'Urban Design').length; 5668 5669 municipalityName.textContent = path.dataset.name.toUpperCase(); 5670 municipalityProjects.textContent = String(projects.length).padStart(2, '0'); 5671 architectureCount.textContent = arch ? String(arch).padStart(2, '0') : 'â'; 5672 urbanCount.textContent = urban ? String(urban).padStart(2, '0') : 'â'; 5673 overviewLabel.textContent = 'MUNICIPALITY OVERVIEW'; 5674 renderMunicipalityFootprint(path); 5675 resetProjectScroll(); 5676 mapTitle.innerHTML = 5677 projects.length 5678 ? `<strong>${path.dataset.name.toUpperCase()}</strong><span>${projects.length} PROJECT${projects.length === 1 ? '' : 'S'}</span>` 5679 : `<strong>${path.dataset.name.toUpperCase()}</strong><span>0 PROJECTS<br>*FOR NOW</span>`; 5680 5681 renderProjects(projects, 'PROJECTS IN ' + path.dataset.name.toUpperCase()); 5682 syncMarkerSelection(path); 5683 } 5684 5685 function showAllProjects() { 5686 setScopeMode('islandwide'); 5687 paths.forEach(p => p.classList.remove('active')); 5688 map.classList.remove('dim'); 5689 selected = null; 5690 viewAllMode = true; 5691 5692 const list = allRepositoryFilteredProjects(); 5693 const arch = list.filter(p => p.type === 'Architecture').length; 5694 const urban = list.filter(p => p.type === 'Urban Design').length; 5695 5696 municipalityName.textContent = 'ALL PROJECTS'; 5697 municipalityProjects.textContent = String(list.length).padStart(2, '0'); 5698 architectureCount.textContent = arch ? String(arch).padStart(2, '0') : 'â'; 5699 urbanCount.textContent = urban ? String(urban).padStart(2, '0') : 'â'; 5700 overviewLabel.textContent = 5701 categoryFilter === 'all' 5702 ? 'PROJECT INDEX' 5703 : categoryFilter.toUpperCase(); 5704 5705 clearMunicipalityFootprint(); 5706 resetProjectScroll(); 5707 5708 mapTitle.innerHTML = 5709 `<strong>PROJECT INDEX</strong><span>${list.length} FILTERED PROJECT${list.length === 1 ? '' : 'S'}</span>`; 5710 5711 renderProjects(list); 5712 } 5713 5714 function clearSelection() { 5715 paths.forEach(p => p.classList.remove('active')); 5716 map.classList.remove('dim'); 5717 selected = null; 5718 viewAllMode = false; 5719 5720 const markerLayer = map.querySelector('.aud-svg-marker-layer'); 5721 if (markerLayer) { 5722 markerLayer.querySelectorAll('.aud-svg-marker').forEach(marker => { 5723 marker.setAttribute('data-state', 'idle'); 5724 }); 5725 } 5726 5727 setTerritoryOverview(); 5728 } 5729 5730 function updateFilter(newFilter) { 5731 filter = newFilter; 5732 refreshProjectTypeOptions(); 5733 5734 root.querySelectorAll('.filter').forEach(b => { 5735 b.classList.toggle('active', b.dataset.filter === filter); 5736 }); 5737 5738 const list = allRepositoryFilteredProjects(); 5739 totalProjectsEl.textContent = list.length; 5740 bottomTotal.textContent = list.length; 5741 5742 if (selected) { 5743 const stillValid = projectsForPath(selected).length > 0; 5744 if (stillValid) { 5745 selectPath(selected); 5746 } else { 5747 clearSelection(); 5748 } 5749 } else if (viewAllMode) { 5750 showAllProjects(); 5751 } else { 5752 setTerritoryOverview(); 5753 } 5754 5755 rebuildLabels(); 5756 } 5757 5758 5759 function updateCategoryFilter(newCategory) { 5760 categoryFilter = newCategory || 'all'; 5761 5762 const list = allRepositoryFilteredProjects(); 5763 totalProjectsEl.textContent = list.length; 5764 bottomTotal.textContent = list.length; 5765 5766 refreshProjectTypeOptions(); 5767 5768 if (selected) { 5769 const stillValid = projectsForPath(selected).length > 0; 5770 if (stillValid) { 5771 selectPath(selected); 5772 } else { 5773 clearSelection(); 5774 } 5775 } else if (viewAllMode) { 5776 showAllProjects(); 5777 } else { 5778 setTerritoryOverview(); 5779 } 5780 5781 rebuildLabels(); 5782 } 5783 5784 refreshProjectTypeOptions(); 5785 5786 projectTypeTrigger?.addEventListener('click', (event) => { 5787 event.stopPropagation(); 5788 toggleProjectTypeMenu(); 5789 }); 5790 5791 projectTypeMenu?.addEventListener('click', (event) => { 5792 event.stopPropagation(); 5793 5794 const option = event.target.closest('.project-type-option'); 5795 if (!option || !projectTypeMenu.contains(option)) return; 5796 5797 const value = option.dataset.value || 'all'; 5798 closeProjectTypeMenu(); 5799 updateCategoryFilter(value); 5800 }); 5801 5802 document.addEventListener('click', (event) => { 5803 if (!projectTypePicker?.contains(event.target)) { 5804 closeProjectTypeMenu(); 5805 } 5806 }); 5807 5808 paths.forEach(path => { 5809 path.addEventListener('mouseenter', () => { 5810 syncMarkerHover(path, true); 5811 if (!selected && !viewAllMode) { 5812 const projects = projectsForPath(path); 5813 municipalityName.textContent = path.dataset.name.toUpperCase(); 5814 municipalityProjects.textContent = 5815 String(projects.length).padStart(2, '0'); 5816 architectureCount.textContent = 5817 projects.filter(p => p.type === 'Architecture').length || 'â'; 5818 urbanCount.textContent = 5819 projects.filter(p => p.type === 'Urban Design').length || 'â'; 5820 mapTitle.innerHTML = 5821 projects.length 5822 ? `<strong>${path.dataset.name.toUpperCase()}</strong><span>${projects.length} PROJECT${projects.length === 1 ? '' : 'S'}</span>` 5823 : `<strong>${path.dataset.name.toUpperCase()}</strong><span>0 PROJECTS<br>*FOR NOW</span>`;
5824 } 5825 }); 5826 5827 path.addEventListener('mouseleave', () => { 5828 syncMarkerHover(path, false); 5829 if (!selected && !viewAllMode) setTerritoryOverview(); 5830 }); 5831 5832 path.addEventListener('click', e => { 5833 e.stopPropagation(); 5834 selectPath(path); 5835 }); 5836 5837 5838 }); 5839 5840 root.querySelector('#clearBtn').addEventListener('click', clearSelection); 5841 5842 root.querySelectorAll('.filter').forEach(button => { 5843 button.addEventListener('click', () => updateFilter(button.dataset.filter)); 5844 }); 5845 5846 root.querySelector('#viewAll').addEventListener('click', showAllProjects); 5847 5848 root.addEventListener('click', e => { 5849 if (e.target === mapFrame) clearSelection(); 5850 }); 5851 5852 document.addEventListener('keydown', e => { 5853 if (e.key === 'Escape') clearSelection(); 5854 }); 5855 5856 function applyTheme(theme) { 5857 root.dataset.theme = theme; 5858 themeToggle.textContent = theme === 'dark' ? 'â¾ DARK' : 'â¼ LIGHT'; 5859 try { localStorage.setItem('aud-map-theme-v045', theme); } catch (_) {} 5860 } 5861 5862 themeToggle.addEventListener('click', () => { 5863 applyTheme(root.dataset.theme === 'dark' ? 'light' : 'dark'); 5864 }); 5865 5866 let savedTheme = 'light'; 5867 try { 5868 savedTheme = localStorage.getItem('aud-map-theme-v045') || 'light'; 5869 } catch (_) {} 5870 applyTheme(savedTheme); 5871 5872 let audTerritoryMapResizeObserver = null; 5873 if (window.ResizeObserver) { 5874 audTerritoryMapResizeObserver = new ResizeObserver(() => { 5875 requestAnimationFrame(rebuildLabels); 5876 }); 5877 audTerritoryMapResizeObserver.observe(map); 5878 } else { 5879 let audMapResizeTimer; 5880 window.addEventListener('resize', () => { 5881 clearTimeout(audMapResizeTimer); 5882 audMapResizeTimer = setTimeout(rebuildLabels, 120); 5883 }); 5884 } 5885 5886 totalProjectsEl.textContent = PROJECTS.length + ISLANDWIDE.length; 5887 bottomTotal.textContent = PROJECTS.length + ISLANDWIDE.length; 5888 preloadProjectThumbnails(); 5889 setTerritoryOverview(); 5890 5891 window.requestAnimationFrame(() => { 5892 rebuildLabels(); 5893 setTimeout(rebuildLabels, 100); 5894 if (selected) syncMarkerSelection(selected); 5895 }); 5896 5897 5898 /* ============================================================ 5899 v0.10 â HOLD-RIGHT-CLICK PROJECT ORBIT 5900 Right mouse DOWN on a municipality: deploy. 5901 Keep holding: sustain. 5902 Hover project: color + live-open matching sidebar card. 5903 Right mouse UP: collapse. 5904 ============================================================ */ 5905 5906 let audOrbit = null; 5907 let audOrbitReleaseTimer = 0; 5908 let audOrbitHoverKey = ''; 5909 5910 function orbitMapPoint(path) { 5911 const svg = map.querySelector('svg'); 5912 if (!svg || !path) return null; 5913 5914 const box = path.getBBox(); 5915 let x = box.x + box.width / 2; 5916 const y = box.y + box.height / 2; 5917 5918 // Match the stable Mayagüez anchor already used by the working marker. 5919 if (path.dataset.name === 'Mayagüez') { 5920 x = box.x + box.width * 0.90; 5921 } 5922 5923 const pt = svg.createSVGPoint(); 5924 pt.x = x; 5925 pt.y = y; 5926 5927 const matrix = svg.getScreenCTM(); 5928 if (!matrix) return null; 5929 5930 const screen = pt.matrixTransform(matrix); 5931 const frameRect = mapFrame.getBoundingClientRect(); 5932 5933 return { 5934 x: screen.x - frameRect.left, 5935 y: screen.y - frameRect.top 5936 }; 5937 } 5938 5939 function orbitProjectImage(project) { 5940 return project?.thumbnail || 5941 PROJECT_THUMBNAILS.get(normalizeProjectUrl(project?.url || '')) || 5942 ''; 5943 } 5944 5945 function openOrbitProjectInSidebar(project, path) { 5946 if (!project || !path) return; 5947 5948 const key = normalizeProjectUrl(project.url || ''); 5949 if (!key || audOrbitHoverKey === key) return; 5950 audOrbitHoverKey = key; 5951 5952 // The right-clicked municipality is already selected when orbit opens. 5953 // Re-select only if something external changed selection meanwhile. 5954 if (selected !== path) selectPath(path); 5955 5956 const list = document.getElementById('projectList'); 5957 if (!list) return; 5958 5959 const card = [...list.querySelectorAll('.aud-project-card')] 5960 .find(node => node.dataset.projectUrl === key); 5961 5962 if (!card) return; 5963 5964 if (!card.classList.contains('is-open')) { 5965 card.querySelector('.aud-project-row')?.click(); 5966 } 5967 5968 card.scrollIntoView({ 5969 behavior:'smooth', 5970 block:'nearest' 5971 }); 5972 } 5973 5974 function stopProjectOrbitAnimation() { 5975 if (!audOrbit?.raf) return; 5976 cancelAnimationFrame(audOrbit.raf); 5977 audOrbit.raf = 0; 5978 } 5979 5980 function closeProjectOrbit(immediate = false) { 5981 if (!audOrbit) return; 5982 5983 stopProjectOrbitAnimation(); 5984 clearTimeout(audOrbitReleaseTimer); 5985 5986 const orbit = audOrbit; 5987 const layer = orbit.layer; 5988 5989 root.classList.remove('orbit-active'); 5990 audOrbitHoverKey = ''; 5991 5992 if (immediate) { 5993 layer.remove(); 5994 audOrbit = null; 5995 return; 5996 } 5997 5998 layer.classList.add('is-releasing'); 5999 6000 // Collapse each upright card back toward the municipality center. 6001 orbit.items?.forEach(item => { 6002 item.node.style.left = '0px'; 6003 item.node.style.top = '0px'; 6004 }); 6005 6006 audOrbitReleaseTimer = window.setTimeout(() => { 6007 layer.remove(); 6008 if (audOrbit === orbit) audOrbit = null; 6009 }, 220); 6010 } 6011 6012 6013 function makeOrbitNode(project, index, total, position, layerField, path) { 6014 const node = document.createElement('button'); 6015 node.type = 'button'; 6016 node.className = 'aud-orbit-node'; 6017 node.dataset.orbitIndex = String(index + 1).padStart(2, '0'); 6018 node.setAttribute('aria-label', project.name || 'Project'); 6019 6020 const image = orbitProjectImage(project); 6021 6022 if (image) { 6023 const img = document.createElement('img'); 6024 img.alt = ''; 6025 img.decoding = 'async'; 6026 img.draggable = false;
6027 img.src = image; 6028 6029 img.addEventListener('error', () => { 6030 node.classList.add('is-no-image'); 6031 img.remove(); 6032 6033 if (!node.querySelector('.aud-orbit-fallback')) { 6034 const fallback = document.createElement('span'); 6035 fallback.className = 'aud-orbit-fallback'; 6036 fallback.textContent = 'PROJECT'; 6037 node.prepend(fallback); 6038 } 6039 }, { once:true }); 6040 6041 node.appendChild(img); 6042 } else { 6043 node.classList.add('is-no-image'); 6044 6045 const fallback = document.createElement('span'); 6046 fallback.className = 'aud-orbit-fallback'; 6047 fallback.textContent = 'PROJECT'; 6048 node.appendChild(fallback); 6049 } 6050 6051 const title = document.createElement('span'); 6052 title.className = 'aud-orbit-title'; 6053 title.textContent = project.name || 'UNTITLED PROJECT'; 6054 node.appendChild(title); 6055 6056 node.addEventListener('mouseenter', () => { 6057 if (!audOrbit?.holding) return; 6058 6059 layerField.classList.add('has-hover'); 6060 6061 layerField.querySelectorAll('.aud-orbit-node').forEach(n => { 6062 n.classList.toggle('is-hovered', n === node); 6063 }); 6064 6065 openOrbitProjectInSidebar(project, path); 6066 }); 6067 6068 node.addEventListener('mouseleave', () => { 6069 if (!audOrbit?.holding) return; 6070 6071 node.classList.remove('is-hovered'); 6072 6073 if (!layerField.querySelector('.aud-orbit-node.is-hovered')) { 6074 layerField.classList.remove('has-hover'); 6075 } 6076 }); 6077 6078 node.addEventListener('contextmenu', e => e.preventDefault()); 6079 6080 return node; 6081 } 6082 6083 6084 function organicOrbitPositions(count) { 6085 const positions = []; 6086 const goldenAngle = Math.PI * (3 - Math.sqrt(5)); 6087 const minimumSpacing = 76; 6088 6089 for (let i = 0; i < count; i++) { 6090 let chosen = null; 6091 6092 for (let attempt = 0; attempt < 16; attempt++) { 6093 const theta = 6094 -Math.PI / 2 + 6095 i * goldenAngle + 6096 attempt * 0.075; 6097 6098 // Continuously expanding radius = no obvious concentric rows. 6099 const radius = 6100 92 + 6101 33 * Math.sqrt(i) + 6102 ((i * 17) % 19 - 9) + 6103 attempt * 7; 6104 6105 const x = 6106 Math.cos(theta) * radius + 6107 Math.sin(i * 1.73) * 11; 6108 6109 const y = 6110 Math.sin(theta) * radius * .72 + 6111 Math.cos(i * 2.11) * 8; 6112 6113 const clear = positions.every(p => 6114 Math.hypot(x - p.x, y - p.y) >= minimumSpacing 6115 ); 6116 6117 if (clear) { 6118 chosen = { x, y }; 6119 break; 6120 } 6121 } 6122 6123 if (!chosen) { 6124 const theta = -Math.PI / 2 + i * goldenAngle; 6125 const radius = 125 + 44 * Math.sqrt(i); 6126 chosen = { 6127 x: Math.cos(theta) * radius, 6128 y: Math.sin(theta) * radius * .72 6129 }; 6130 } 6131 6132 positions.push(chosen); 6133 } 6134 6135 return positions; 6136 } 6137 6138 function startLooseProjectOrbit(orbit) { 6139 if (!orbit?.items?.length) return; 6140 6141 const started = performance.now(); 6142 6143 // Whole constellation revolves slowly as a field. 6144 // Because every point receives the SAME rotation matrix, the loose 6145 // irregular spacing is preserved rather than snapping into radial rows. 6146 const radiansPerSecond = 6147 (Math.PI * 2) / 34 * orbit.direction; 6148 6149 const tick = now => { 6150 if (!audOrbit || audOrbit !== orbit || !orbit.layer.isConnected) return; 6151 6152 const elapsed = (now - started) / 1000; 6153 const rotation = elapsed * radiansPerSecond; 6154 const cosR = Math.cos(rotation); 6155 const sinR = Math.sin(rotation); 6156 6157 orbit.items.forEach(item => { 6158 // If hovered, keep that planet targetable at its current point. 6159 if (item.node.classList.contains('is-hovered')) return; 6160 6161 // Rigidly rotate the ORIGINAL loose field around the municipality. 6162 const baseX = item.baseX; 6163 const baseY = item.baseY; 6164 6165 let x = baseX * cosR - baseY * sinR; 6166 let y = baseX * sinR + baseY * cosR; 6167 6168 // Tiny independent water/bubble current. 6169 // This changes POSITION only. Never orientation. 6170 const floatT = elapsed + item.phase; 6171 x += Math.sin(floatT * item.floatSpeedX) * item.floatAmpX; 6172 y += Math.cos(floatT * item.floatSpeedY) * item.floatAmpY; 6173 6174 item.node.style.left = `${x.toFixed(2)}px`; 6175 item.node.style.top = `${y.toFixed(2)}px`; 6176 }); 6177 6178 orbit.raf = requestAnimationFrame(tick); 6179 }; 6180 6181 orbit.raf = requestAnimationFrame(tick); 6182 } 6183 6184 6185 function openProjectOrbit(path) { 6186 const projects = projectsForPath(path); 6187 if (!projects.length) return; 6188 6189 closeProjectOrbit(true); 6190 selectPath(path); 6191 6192 const point = orbitMapPoint(path); 6193 if (!point) return; 6194 6195 const layer = document.createElement('div'); 6196 layer.className = 'aud-orbit-layer'; 6197 6198 const field = document.createElement('div'); 6199 field.className = 'aud-orbit-field'; 6200 field.style.left = `${point.x}px`; 6201 field.style.top = `${point.y}px`; 6202 6203 layer.appendChild(field); 6204 mapFrame.appendChild(layer); 6205 6206 const positions = organicOrbitPositions(projects.length); 6207 const items = []; 6208
6209 projects.forEach((project, index) => { 6210 const position = positions[index]; 6211 6212 const node = makeOrbitNode( 6213 project, 6214 index, 6215 projects.length, 6216 position, 6217 field, 6218 path 6219 ); 6220 6221 // Initial position before the JS orbit begins. 6222 node.style.left = `${position.x.toFixed(1)}px`; 6223 node.style.top = `${position.y.toFixed(1)}px`; 6224 6225 field.appendChild(node); 6226 6227 items.push({ 6228 node, 6229 project, 6230 baseX:position.x, 6231 baseY:position.y, 6232 6233 // Independent tiny currents. No rotation values exist. 6234 phase:index * 1.37, 6235 floatAmpX:2.4 + (index % 3) * .75, 6236 floatAmpY:2.0 + (index % 4) * .55, 6237 floatSpeedX:.55 + (index % 5) * .045, 6238 floatSpeedY:.47 + (index % 4) * .052 6239 }); 6240 6241 requestAnimationFrame(() => { 6242 node.classList.add('is-deployed'); 6243 }); 6244 }); 6245 6246 root.classList.add('orbit-active'); 6247 6248 // Direction varies by municipality, but every project in that municipality 6249 // shares it, preserving the loose constellation and avoiding crossings. 6250 const seed = [...path.dataset.id] 6251 .reduce((sum, ch) => sum + ch.charCodeAt(0), 0); 6252 6253 audOrbit = { 6254 layer, 6255 field, 6256 path, 6257 items, 6258 holding:true, 6259 direction:seed % 2 ? -1 : 1, 6260 raf:0 6261 }; 6262 6263 startLooseProjectOrbit(audOrbit); 6264 6265 // Thumbnail harvesting may still be finishing in the background. 6266 Promise.resolve(preloadProjectThumbnails()).then(() => { 6267 if (!audOrbit || audOrbit.layer !== layer) return; 6268 6269 items.forEach(item => { 6270 const node = item.node; 6271 if (node.querySelector('img')) return; 6272 6273 const image = orbitProjectImage(item.project); 6274 if (!image) return; 6275 6276 const img = document.createElement('img'); 6277 img.alt = ''; 6278 img.decoding = 'async'; 6279 img.draggable = false; 6280 img.src = image; 6281 6282 node.classList.remove('is-no-image'); 6283 node.querySelector('.aud-orbit-fallback')?.remove(); 6284 node.prepend(img); 6285 }); 6286 }); 6287 } 6288 6289 6290 paths.forEach(path => { 6291 path.addEventListener('mousedown', event => { 6292 if (event.button !== 2) return; 6293 event.preventDefault(); 6294 event.stopPropagation(); 6295 6296 const projects = projectsForPath(path); 6297 if (!projects.length) return; 6298 6299 openProjectOrbit(path); 6300 }); 6301 6302 path.addEventListener('contextmenu', event => { 6303 event.preventDefault(); 6304 }); 6305 }); 6306 6307 // SVG-native "+" marker may sit above the municipality path. 6308 // Give it the same hold-right-click interaction without altering left-click. 6309 map.addEventListener('mousedown', event => { 6310 if (event.button !== 2) return; 6311 6312 const marker = event.target.closest?.('.aud-svg-marker'); 6313 if (!marker) return; 6314 6315 const path = paths.find(p => p.dataset.id === marker.dataset.id); 6316 if (!path || !projectsForPath(path).length) return; 6317 6318 event.preventDefault(); 6319 event.stopPropagation(); 6320 openProjectOrbit(path); 6321 }, true); 6322 6323 map.addEventListener('contextmenu', event => { 6324 if (event.target.closest?.('.municipality,.aud-svg-marker,.aud-orbit-node') || audOrbit) { 6325 event.preventDefault(); 6326 } 6327 }); 6328 6329 window.addEventListener('mouseup', event => { 6330 if (event.button !== 2 || !audOrbit) return; 6331 audOrbit.holding = false; 6332 closeProjectOrbit(false); 6333 }, true); 6334 6335 window.addEventListener('blur', () => closeProjectOrbit(true)); 6336 6337 document.addEventListener('keydown', event => { 6338 if (event.key === 'Escape' && audOrbit) { 6339 closeProjectOrbit(true); 6340 } 6341 }); 6342 6343 // Non-blocking repository scan. If it fails, the existing repository 6344 // and map remain fully functional. Keep this inside the app scope. 6345 window.setTimeout(() => { 6346 discoverNewProjectsFromIndexes(); 6347 }, 900); 6348 6349})(); 6350</script>
6350
6350<script>'undefined'=== typeof _trfq || (window._trfq = []);'undefined'=== typeof _trfd && (window._trfd=[]), 6351 _trfd.push({'tccl.baseHost':'secureserver.net'}), 6352 _trfd.push({'ap':'wpaas_v2'}, 6353 {'server':'355a6f8c8c6b'}, 6354 {'pod':'c17-prod-p3-us-west-2'}, 6355 {'xid':'46318172'}, 6356 {'wp':'7.1.3'}, 6357 {'php':'8.2.34'}, 6358 {'loggedin':'0'}, 6359 {'cdn':'1'}, 6360 {'builder':'elementor'}, 6361 {'theme':'hello-elementor'}, 6362 {'wds':'0'}, 6363 {'wp_alloptions_count':'303'}, 6364 {'wp_alloptions_bytes':'79820'}, 6365 {'gdl_coming_soon_page':'0'} 6366 , {'appid':'1244564'} ); 6367 var trafficScript = document.createElement('script'); trafficScript.src = 'https://img1.wsimg.com/signals/js/clients/scc-c2/scc-c2.min.js'; window.document.head.appendChild(trafficScript);</script>
6367 6368
6368<script>window.addEventListener('click', function (elem) { var _elem$target, _elem$target$dataset, _window, _window$_trfq; return (elem === null || elem === void 0 ? void 0 : (_elem$target = elem.target) === null || _elem$target === void 0 ? void 0 : (_elem$target$dataset = _elem$target.dataset) === null || _elem$target$dataset === void 0 ? void 0 : _elem$target$dataset.eid) && ((_window = window) === null || _window === void 0 ? void 0 : (_window$_trfq = _window._trfq) === null || _window$_trfq === void 0 ? void 0 : _window$_trfq.push(["cmdLogEvent", "click", elem.target.dataset.eid]));});</script>
6368 6369
6369<script src='https://img1.wsimg.com/traffic-assets/js/tccl-tti.min.js' onload="window.tti.calculateTTI()"></script>
6369 6370 </body> 6371</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.