1<!doctype html> 2<html lang="en"> 3 <head> 4 <title>Americaâs Most Romantic Cozy Getaways - Exoticca</title> 5 <meta name="description" content="Use the map below to discover the most romantic, cozy getaway in each state."> 6 7 <!-- Required meta tags --> 8 <meta charset="utf-8"> 9 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> 10 11 12
12<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
12 13 14 <!-- Bootstrap CSS --> 15 <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> 16 17 <style> 18 *{box-sizing:border-box}.graph-wrap{max-width:1480px;margin:0 auto;padding:0 1rem;width:100%}body,p{text-align:center;font-size:18px}body{overflow-x:hidden}li{font-size:18px}.mobi{display:none}h1{margin:0}h1.he-outlined{color:#fff !important}h1.he-shadowed-black{text-shadow:0 10px 20px rgba(0, 0, 0, 0.5)}h1.he-italic{font-style:italic}h1 i{font-style:normal}h1#header{padding-bottom:0 !important}.banner{width:100%}.banner img{max-width:800px;width:100%;margin:0 auto;height:auto}.subheader,.footernote{font-size:20px;max-width:800px;width:100%}.subheader{margin-bottom:1.5rem}.footernote{margin:auto}.footernote a{font-size:12px;max-width:720px;padding:0.2rem;margin:auto;display:block}.footernote a:first-child{padding-top:1rem}.table_sub{font-size:20px;max-width:800px;width:100%}.table-wrap{max-width:1480px;margin:auto}.table-wrap table{font-size:16px;width:100%}.table-wrap table td{text-align:center;padding:0.5rem !important}.table-wrap table th{align-content:center;padding:0.5rem !important;text-align:center;vertical-align:middle !important;color:#fff}.table-wrap.tabcol-2{max-width:420px}.table-wrap.tabcol-3{max-width:800px}.table-wrap.tabcol-4{max-width:800px}.table-wrap.tabcol-5{max-width:1480px}.table-wrap.tabcol-6{max-width:1480px}.table-wrap.table-wrap.tabcol-8 table th{width:12.5%}.table-wrap.tabcol-8{width:100%;overflow-x:auto}@media (max-width:550px){.table-wrap table{font-size:12px;font-weight:500}.table-wrap{overflow:auto;width:100%}.table-wrap.mobile-table table th{display:none}.table-wrap.mobile-table table tr{display:block;border:1px solid #ccc}.table-wrap.mobile-table table tr:hover{background:transparent}.table-wrap.mobile-table table tr td{text-align:left;display:flex;border:none;padding:0 !important;justify-content:flex-start;align-items:center;font-size:14px}.table-wrap.mobile-table table tr td:hover{background:transparent !important}.table-wrap.mobile-table table tr td::before{content:attr(data-title) ": ";font-weight:bold;font-size:13px;height:60px;display:block;padding:10px;width:25%;color:#fff;text-align:center;align-content:center}.table-wrap.mobile-table table tr td span{display:block;width:75%;padding:10px}.table-wrap.mobile-table table tr:nth-child(even){background-color:#f3f3f3}}.extra-html{max-width:800px;margin:3rem auto 0}.extra-html h3,.extra-html h4,.extra-html b{font-weight:700}.extra-html p{padding:2rem;background:#fff;box-shadow:0px 0px 60px #ebe7e7;border-radius:10px;margin:1.5rem 0}a.logo-link{display:block;width:100%}a.logo-link img{width:100%;transition:0.2s}a.logo-link:hover img{transform:scale(1.1)}body.modal-open .modal-backdrop.fade.in{opacity:0.3}body.modal-open .modal.active{display:block;opacity:1}#shareModal .vertical-alignment-helper .modal-dialog.vertical-align-bottom,#embedModal .vertical-alignment-helper .modal-dialog.vertical-align-bottom{position:fixed;bottom:200px !important;left:0;right:0;margin:auto}.share-links .btn{background-color:#000;color:#fff;padding:0.75rem 3rem;margin:0 0.3rem;border:none;text-transform:uppercase;transition:0.2s;font-weight:600}.share-links .btn:hover{opacity:0.75}.info-box{display:flex;background:#eee;border-radius:40px;min-height:200px;max-width:800px;margin:2rem auto;padding:3rem;align-items:center;flex-direction:row}.info-box.reversed{flex-direction:row-reverse}.info-box .col{width:50%}.info-box .col img{width:100%;margin:auto;display:block}.notice{display:block;background:#f3f3f3;padding:3rem 4rem;color:#000;position:relative;margin:auto;z-index:9999;transition:0.2s}.notice i{display:block;height:40px;width:40px;background:#fff;color:black;line-height:40px;position:absolute;right:2%;top:1rem;bottom:1rem;margin:auto;font-style:normal;border-radius:50%;font-size:32px;transform:rotate(45deg);cursor:pointer}.notice.disabled{height:0;overflow:hidden;padding:0}.rounded .box{border-radius:2rem;overflow:hidden}.rounded .box span.num{border-radius:1rem}.credits{font-size:14px}.credits ul{padding:0}.credits i{display:block}.credits ul li{font-size:14px;list-style:none}@media screen and (max-width:800px){h1{font-size:4rem !important}.footernote p{font-size:0.75rem !important}}
18@media screen and (max-width:580px){a#openembed{display:none}a#openshare{width:80%;margin:0}h1{font-size:3rem !important}.he-retro-shaddow{letter-spacing:0.1em;font-size:2.5rem !important}.info-box{display:flex;flex-direction:column !important}.info-box .col{width:100%}.info-box .col img{margin-bottom:2rem !important}.credits ul li{font-size:11px !important;list-style:none}.graph-wrap{padding:1rem}.mobi{display:block}}@media screen and (max-width:401px){h1{font-size:2rem !important}.subheader,.footernote{font-size:18px;margin:1.5rem auto;max-width:800px;width:100%}.graph-wrap{padding:0.5rem}} </style> 19 20 21 <!-- GRAPH VAR CSS --> 22 <style> 23 24 @font-face { 25 font-family: 'OakesGrotesk-Bold' ; 26 src: url('assets/OakesGrotesk-Bold.woff'); 27 } 28 29 @font-face { 30 font-family: 'OakesGrotesk-Regular' ; 31 src: url('assets/OakesGrotesk-Regular.woff'); 32 } 33 34 h1#header{ 35 font-size: 3rem ; 36 line-height: 1em ; 37 color: #000000 ; 38 font-family: 'OakesGrotesk-Bold', serif; 39 font-weight: 100; 40 } 41 42 h1#header i, h1#header span, h1#header b{ 43 color: } 44 45 h2,h3,h4{ 46 font-family: 'OakesGrotesk-Bold', serif !important; 47 font-weight: 100; 48 49 } 50 51 /* Fonts*/ 52 body,p,.subheader, .footernote{ 53 font-family: 'OakesGrotesk-Regular', serif; 54 color: ; 55 font-weight: 100; 56 font-size: 20px; 57 } 58 59 60 61 .logo-link{background: #000000; padding: 2rem; } 62 .logo-link{ border-radius: 1rem; } 63 64 .he-shadowed-colour{ text-shadow: 0 10px 20px #000000cc ;} 65 .he-outlined{ text-shadow: -2px -2px 0 #000000 , 2px -2px 0 #000000 , -2px 2px 0 #000000 , 2px 2px 0 #000000 ;} 66 .he-outlined-shaddow{ color: #fff !important; text-shadow: 0 5px 30px #000000cc , -2px -2px 0 #000000 , 2px -2px 0 #000000 , -2px 2px 0 #000000 , 2px 2px 0 #000000 ;} 67 .he-retro-shaddow { 68 letter-spacing: .15em; text-transform: uppercase; 69 text-shadow: 3px 3px 0px #ffffff, 7px 7px 0px #00000035; 70 } 71 .he-retro-shaddow i{ 72 letter-spacing: .15em; text-transform: uppercase; 73 text-shadow: 3px 3px 0px #ffffff, 7px 7px 0px 35; 74 } 75 .share-links .btn { background-color: #000000 !important;} 76 .share-links .btn { background-color: !important;} 77 b,strong{ color: #000000; } 78 b,strong{ color: ; } 79 .keys span img{ width: !important;} 80 81 /*Spacing*/ 82 h1#header,.subheader, .logo-link, .footer, .share-links, .graph-wrap{ padding: 2rem; padding: 1.5rem; margin: auto;} 83 .banner{ padding-top: 1.5rem;} 84 .table-wrap{ padding-top: 1.5rem; padding-bottom: 1.5rem;} 85 .box{margin-top: calc(1.5rem*2); margin-bottom: calc(1.5rem*2);} 86 .footernote p{margin-bottom: 0rem ; margin-bottom: calc(1.5rem*0.5); } 87 88 /*Border Radius*/ 89 a.logo-link, .share-links .btn, .popradius ,.rounded .box, .rounded .box .col2 span.num{ border-radius:0; border-radius: 15px !important;} 90 91 /*Logo padding*/ 92 a.logo-link{padding: 0; padding: 30px } 93 94 95 /*Tables*/ 96 table th{ background-color: #000000 !important;} 97 table th{ background-color: !important;} 98 99 @media (max-width: 550px) { 100 .table-wrap table tr td::before { 101 background-color: #000000; 102 background-color: ; 103 } 104 } 105 106 107</style> 108 </head> 109 <body> 110 111 112 113<style> 114 .table_sub{margin: 1.5rem auto 1rem;} 115</style> 116 117<div class="preloader d-none"> 118 <img src='assets/Banner2-1.svg' /></div> 119 120<div class="graph-wrap"> 121 122 <!--[if lt IE 10]> 123 <p class="browserupgrade">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade 124 your browser</a> to improve your experience.</p> 125 <![endif]--> 126 127 <div class="graph map"> 128 129 <h1 id="header" data-heading="Americaâs Most Romantic Cozy Getaways" class=" "> 130 Americaâs Most Romantic Cozy Getaways </h1> 131 132 <div class="banner"> 133 <img src='assets/Banner2-1.svg' width='100' height='100' alt='' /> </div> 134 135 <p class="subheader"> 136 Use the map below to discover the most romantic, cozy getaway in each state. </p> 137 138 139 140<!-- CSS --> 141<style> 142 143 :root{ 144 /* === EDIT YOUR MAP COLORS HERE === */ 145 --state-fill: #f8d447; 146 --state-fill-hover: #f7efd9; 147 --state-stroke: #fff; 148 --state-stroke-hover: #fff; 149 } 150 151 :root{--state-stroke-width: 1;--label-color: rgba(0, 0, 0, 1)}body{margin:0;font-family:system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif}.wrap{max-width:1100px;margin:0 auto;padding:2rem 1rem}.map-card{padding:0rem;overflow:hidden}svg{width:100%;height:100%;display:block;touch-action:manipulation}.state{cursor:pointer;vector-effect:non-scaling-stroke;stroke:var(--state-stroke);stroke-width:var(--state-stroke-width);
151fill:var(--state-fill);transition:fill 120ms ease, stroke 120ms ease;outline:none}.state:hover,.state.is-active,.state:focus-visible{fill:var(--state-fill-hover);stroke:var(--state-stroke-hover)}.state-label{font-size:11px;font-weight:700;fill:var(--label-color);paint-order:stroke;stroke:var(--label-outline);stroke-width:3px;stroke-linejoin:round;pointer-events:none;user-select:none}.tooltip{position:fixed;z-index:9999;max-width:min(320px, calc(100vw - 24px));padding:2rem;border-radius:12px;background:white;color:#000000;box-shadow:0 10px 30px rgba(0, 0, 0, 0.2);pointer-events:none;opacity:0;transform:translateY(6px);transition:opacity 120ms ease, transform 120ms ease}.tooltip.is-open{opacity:1;transform:translateY(0)}.tooltip-close{position:absolute;top:8px;right:8px;width:28px;height:28px;border-radius:50%;border:none;background:#000;color:#fff;font-size:18px;line-height:1;cursor:pointer;display:none}@media (hover:none) and (pointer:coarse){.tooltip-close{display:block}}.tt-head{display:flex;align-items:center;gap:8px;width:100%;margin-bottom:6px;justify-content:center}.tt-icon{height:auto;display:block;object-fit:contain;position:absolute;top:-25px;left:0;right:0;margin:auto}.tooltip h4{margin:0;font-size:20px;line-height:1.2}.tooltip p{margin:0;font-size:16px;line-height:1.35;opacity:0.95}#map{width:100%;height:min(70vh, 600px);min-height:300px;position:relative}@media screen and (max-width:800px){#map{width:100%;height:min(50vh, 400px);min-height:250px;position:relative}}@media screen and (max-width:520px){#map{width:100%;height:min(30vh, 250px);min-height:200px;position:relative}} 152 .stats-layer .heading, .stats-layer h4{ color: #f8d447;} 153 .tooltip{ 154 padding-top: calc(60px + 10px); 155 } 156 157 div#Hawaii{ 158p{ display: none} 159&::after{content: "No Data"; text-align: center; display:block;} 160} 161</style> 162 163 164<!-- HTML --> 165 <div class="preloader d-none"> 166 <img src="assets/Icon-4.svg "> 167 </div> 168 169 170 <div class="wrap"> 171 <div class="map-card"> 172 <div id="map"></div> 173 </div> 174 </div> 175 176 <div id="tooltip" class="tooltip stats-layer popradius " role="dialog" aria-live="polite"></div> 177 178 179 180<!-- JS --> 181 182<!-- Non-module, works on file:// as well --> 183
183<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
183 184
184<script src="https://cdn.jsdelivr.net/npm/topojson-client@3"></script>
184 185 186
186<script> 187 const TOPO_URL = "https://cdn.jsdelivr.net/npm/us-atlas@3/states-10m.json"; 188 189 // Global tooltip icon (PNG/JPG/SVG) 190 const TOOLTIP_ICON_URL = 'assets/Icon-4.svg'; 191 192 // Map TopoJSON FIPS codes to state abbreviations 193 const FIPS_TO_ABBR = { 194 1:"AL",2:"AK",4:"AZ",5:"AR",6:"CA",8:"CO",9:"CT",10:"DE", 195 12:"FL",13:"GA",15:"HI",16:"ID",17:"IL",18:"IN", 196 19:"IA",20:"KS",21:"KY",22:"LA",23:"ME",24:"MD",25:"MA", 197 26:"MI",27:"MN",28:"MS",29:"MO",30:"MT",31:"NE",32:"NV", 198 33:"NH",34:"NJ",35:"NM",36:"NY",37:"NC",38:"ND",39:"OH", 199 40:"OK",41:"OR",42:"PA",44:"RI",45:"SC",46:"SD",47:"TN", 200 48:"TX",49:"UT",50:"VT",51:"VA",53:"WA",54:"WV",55:"WI", 201 56:"WY" 202 }; 203 204 // State data keyed by abbreviation 205 const DATA = { 206 AL: { name:'Alabama',inhabitant :'Alabamians',stat_1:'Mentone' }, 207AK: { name:'Alaska',inhabitant :'Alaskans',stat_1:'Homer' }, 208AZ: { name:'Arizona',inhabitant :'Arizonans',stat_1:'Pinetop-Lakeside' }, 209AR: { name:'Arkansas',inhabitant :' Arkansans',stat_1:'Eureka Springs' }, 210CA: { name:'California',inhabitant :'Californians',stat_1:'Idyllwild' }, 211CO: { name:'Colorado',inhabitant :'Coloradans',stat_1:'Ouray' }, 212CT: { name:'Connecticut',inhabitant :'Nutmeggers',stat_1:'Litchfield' }, 213DE: { name:'Delaware',inhabitant :'Delawareans',stat_1:'Lewes' }, 214FL: { name:'Florida',inhabitant :'Floridians',stat_1:'Apalachicola' }, 215GA: { name:'Georgia',inhabitant :'Georgians',stat_1:'Helen' }, 216HI: { name:'Hawaii',inhabitant :'Hawaii residents',stat_1:'' }, 217ID: { name:'Idaho',inhabitant :'Idahoans',stat_1:'Sandpoint' }, 218IL: { name:'Illinois',inhabitant :'Illinoisans',stat_1:'Ottawa' }, 219IN: { name:'Indiana',inhabitant :'Hoosiers',stat_1:'Nashville' }, 220IA: { name:'Iowa',inhabitant :'Iowans',stat_1:'Spirit Lake' }, 221KS: { name:'Kansas',inhabitant :'Kansans',stat_1:'Cottonwood Falls' }, 222KY: { name:'Kentucky',inhabitant :'Kentuckians',stat_1:'Pine Mountain' }, 223LA: { name:'Louisiana',inhabitant :'Louisianans',stat_1:'Abita Springs' }, 224ME: { name:'Maine',inhabitant :'Mainers',stat_1:'Camden' }, 225MD: { name:'Maryland',inhabitant :'Marylanders',stat_1:'St. Michaels' }, 226MA: { name:'Massachusetts',inhabitant :'Bay Staters',stat_1:'Stockbridge' }, 227MI: { name:'Michigan',inhabitant :'Michiganders',stat_1:'Harbor Springs' }, 228MN: { name:'Minnesota',inhabitant :'Minnesotans',stat_1:'Ely' }, 229MS: { name:'Mississippi',inhabitant :'Mississippians',stat_1:'Natchez' }, 230MO: { name:'Missouri',inhabitant :'Missourians',stat_1:'Arrow Rock' }, 231MT: { name:'Montana',inhabitant :'Montanans',stat_1:'Philipsburg' }, 232NE: { name:'Nebraska',inhabitant :'Nebraskans',stat_1:'Brownville' }, 233NV: { name:'Nevada',inhabitant :'Nevadans',stat_1:'Ely' }, 234NH: { name:'New Hampshire',inhabitant :'Granite Staters',stat_1:'Sugar Hill' }, 235NJ: { name:'New Jersey',inhabitant :'New Jerseyans',stat_1:'Cape May' }, 236NM: { name:'New Mexico',inhabitant :'New Mexicans',stat_1:'Taos' }, 237NY: { name:'New York', inhabitant :'New Yorkers',stat_1:'Lake Placid' }, 238NC: { name:'North Carolina', inhabitant :'North Carolinians',stat_1:'Highlands' }, 239ND: { name:'North Dakota',inhabitant :'North Dakotans',stat_1:'Bottineau' }, 240OH: { name:'Ohio',inhabitant :'Ohioans',stat_1:'Granville' }, 241OK: { name:'Oklahoma',inhabitant :'Oklahomans',stat_1:'Broken Bow' }, 242OR: { name:'Oregon',inhabitant :'Oregonians',stat_1:'Sisters' }, 243PA: { name:'Pennsylvania',inhabitant :'Pennsylvanians',stat_1:'Lititz' }, 244RI: { name:'Rhode Island', inhabitant :'Rhode Islanders',stat_1:'Bristol' }, 245SC: { name:'South Carolina',inhabitant :'South Carolinians',stat_1:'Travelers Rest' }, 246SD: { name:'South Dakota',inhabitant :'South Dakotans',stat_1:'Deadwood' }, 247TN: { name:'Tennessee',inhabitant :'Tennesseans',stat_1:'Gatlinburg' }, 248TX: { name:'Texas',inhabitant :'Texans',stat_1:'Alpine' }, 249UT: { name:'Utah',inhabitant :'Utahns',stat_1:'Midway' }, 250VT: { name:'Vermont',inhabitant :'Vermonters',stat_1:'Waitsfield' }, 251VA: { name:'Virginia',inhabitant :'Virginians',stat_1:'Floyd' }, 252WA: { name:'Washington',inhabitant :'Washingtonians',stat_1:'La Conner' },
253WV: { name:'West Virginia',inhabitant :'West Virginians',stat_1:'Lewisburg' }, 254WI: { name:'Wisconsin',inhabitant :'Wisconsinites',stat_1:'Minocqua' }, 255WY: { name:'Wyoming',inhabitant :'Wyomingites',stat_1:'Dubois' }, 256 }; 257 258 259 const mapEl = document.getElementById("map"); 260 const tooltip = document.getElementById("tooltip"); 261 262 let activeEl = null; 263 let stickyOpen = false; 264 let cachedStates = null; 265 let cachedTopoFeature = null; 266 267 function getMeta(d){ 268 const fips = +d.id; 269 const abbr = FIPS_TO_ABBR[fips]; 270 const row = abbr ? DATA[abbr] : null; 271 272 if (!abbr || !row) { 273 return { abbr: abbr || "", name: "Unknown", inhabitant: "", stat_1: "" }; 274 } 275 276 return { 277 abbr, 278 name: row.name, 279 inhabitant: row.inhabitant, 280 stat_1: row.stat_1, 281 stat_2: row.stat_2, 282 stat_3: row.stat_3, 283 stat_4: row.stat_4, 284 }; 285 } 286 287 function tooltipHtml(a){ 288 289 var popuptext = a.stat_1+' was voted the coziest romantic getaway in '+a.name+'.'; 290 return ` 291 292 <button class="tooltip-close" aria-label="Close tooltip">Ã</button> 293 <img class="tt-icon" src="${TOOLTIP_ICON_URL}" width="120" alt="" aria-hidden="true" style="top: -60px" /> 294 <div id="${a.name}"> 295 <div class="tt-head text-center "> 296 <h4>${a.name} (${a.abbr})</h4> 297 </div> 298 <p>`+ popuptext +`</p> 299 </div> 300 `; 301 } 302 303 function openTooltipAt(x, y, html) { 304 tooltip.innerHTML = html; 305 tooltip.classList.add("is-open"); 306 307 const offset = 14; 308 const vw = window.innerWidth, vh = window.innerHeight; 309 310 tooltip.style.left = "0px"; 311 tooltip.style.top = "0px"; 312 const rect = tooltip.getBoundingClientRect(); 313 314 let left = x + offset; 315 let top = y + offset; 316 317 if (left + rect.width > vw - 10) left = vw - rect.width - 10; 318 if (top + rect.height > vh - 10) top = vh - rect.height - 10; 319 if (left < 10) left = 10; 320 if (top < 10) top = 10; 321 322 tooltip.style.left = left + "px"; 323 tooltip.style.top = top + "px"; 324 } 325 326 // Delegate close button click 327 tooltip.addEventListener("click", (e) => { 328 if (e.target && e.target.classList.contains("tooltip-close")) { 329 closeTooltip(); 330 } 331 }); 332 333 function closeTooltip() { 334 tooltip.classList.remove("is-open"); 335 stickyOpen = false; 336 if (activeEl) activeEl.classList.remove("is-active"); 337 activeEl = null; 338 } 339 340 function setActive(el) { 341 if (activeEl && activeEl !== el) activeEl.classList.remove("is-active"); 342 activeEl = el; 343 if (activeEl) activeEl.classList.add("is-active"); 344 } 345 346 function render(statesGeoJson) { 347 mapEl.innerHTML = ""; 348 const width = mapEl.clientWidth; 349 const height = mapEl.clientHeight; 350 351 const svg = d3.select(mapEl) 352 .append("svg") 353 .attr("viewBox", `0 0 ${width} ${height}`) 354 .attr("preserveAspectRatio", "xMidYMid meet"); 355 356 const projection = d3.geoAlbersUsa().fitSize([width, height], statesGeoJson); 357 const path = d3.geoPath(projection); 358 359 const g = svg.append("g"); 360 361 // Draw states 362 const states = g.selectAll("path.state") 363 .data(statesGeoJson.features) 364 .enter() 365 .append("path") 366 .attr("class", "state") 367 .attr("d", path) 368 .attr("tabindex", 0) 369 .attr("role", "button") 370 .attr("aria-label", d => getMeta(d).name); 371 372 // Hover / move (desktop) 373 states.on("pointerenter", (event, d) => { 374 if (event.pointerType && event.pointerType !== "mouse") return; 375 if (stickyOpen) return; 376 const meta = getMeta(d); 377 setActive(event.currentTarget); 378 openTooltipAt(event.clientX, event.clientY, tooltipHtml(meta)); 379 }); 380 381 states.on("pointermove", (event, d) => { 382 if (event.pointerType && event.pointerType !== "mouse") return; 383 if (stickyOpen) return; 384 const meta = getMeta(d); 385 openTooltipAt(event.clientX, event.clientY, tooltipHtml(meta)); 386 }); 387 388 states.on("pointerleave", (event) => { 389 if (event.pointerType && event.pointerType !== "mouse") return; 390 if (stickyOpen) return; 391 closeTooltip(); 392 }); 393 394 // Tap/click (mobile + desktop) 395 // Use pointerdown for touch because mobile Safari/Chrome can drop or delay click events. 396 function openFromEvent(event, d) { 397 event.preventDefault(); 398 event.stopPropagation(); 399 400 const meta = getMeta(d); 401 const el = event.currentTarget; 402 403 // toggle same state 404 if (activeEl === el && stickyOpen) { 405 closeTooltip(); 406 return; 407 } 408 409 stickyOpen = true;
410 setActive(el); 411 412 // On touch devices, lock tooltip to state centroid 413 let x, y; 414 415 if (event.pointerType === "touch" || event.pointerType === "pen") { 416 const [cx, cy] = path.centroid(d); 417 const svgRect = mapEl.querySelector("svg").getBoundingClientRect(); 418 x = svgRect.left + cx; 419 y = svgRect.top + cy; 420 } else { 421 // Mouse: follow cursor 422 x = event.clientX; 423 y = event.clientY; 424 } 425 426 openTooltipAt(x, y, tooltipHtml(meta)); 427 } 428 429 // Touch devices: pointerdown is the most reliable 430 states.on("pointerdown", (event, d) => { 431 if (event.pointerType === "touch" || event.pointerType === "pen") { 432 openFromEvent(event, d); 433 } 434 }); 435 436 // Mouse devices: keep click as a fallback 437 states.on("click", (event, d) => { 438 if (event.pointerType === "mouse" || !event.pointerType) { 439 openFromEvent(event, d); 440 } 441 }); 442 443 // Keyboard 444 states.on("keydown", (event, d) => { 445 if (event.key !== "Enter" && event.key !== " ") return; 446 event.preventDefault(); 447 const meta = getMeta(d); 448 449 stickyOpen = true; 450 setActive(event.currentTarget); 451 452 const [cx, cy] = path.centroid(d); 453 const svgRect = mapEl.querySelector("svg").getBoundingClientRect(); 454 openTooltipAt(svgRect.left + cx, svgRect.top + cy, tooltipHtml(meta)); 455 }); 456 457 // Labels (abbreviations) 458 // Some features can produce NaN centroids with the path centroid calc (projection returns null for some points). 459 // We compute a safe label position and only render labels when we have finite coordinates. 460 function safeLabelPos(feature) { 461 // First try the projected path centroid 462 const c = path.centroid(feature); 463 if (Number.isFinite(c[0]) && Number.isFinite(c[1])) return c; 464 465 // Fallback: project the geographic centroid 466 const gc = d3.geoCentroid(feature); 467 const p = projection(gc); 468 if (p && Number.isFinite(p[0]) && Number.isFinite(p[1])) return p; 469 470 // No safe position 471 return null; 472 } 473 474 const labelData = statesGeoJson.features 475 .map(f => { 476 const meta = getMeta(f); 477 if (!meta || meta.abbr === "??") return null; // skip DC / unknowns 478 479 const pos = safeLabelPos(f); 480 if (!pos) return null; 481 482 return { feature: f, pos, abbr: meta.abbr }; 483 }) 484 .filter(Boolean); 485 486 g.selectAll("text.state-label") 487 .data(labelData) 488 .enter() 489 .append("text") 490 .attr("class", "state-label") 491 .attr("x", d => d.pos[0]) 492 .attr("y", d => d.pos[1]) 493 .attr("text-anchor", "middle") 494 .attr("dominant-baseline", "central") 495 .text(d => d.abbr); 496 497 // cache for resize 498 cachedStates = statesGeoJson; 499 } 500 501 // Close when tapping/clicking outside a state 502 document.addEventListener( 503 "pointerdown", 504 (e) => { 505 // If the tap is on a state, let the state handler open the tooltip 506 if (e.target && e.target.closest && e.target.closest("path.state")) return; 507 closeTooltip(); 508 }, 509 { passive: true } 510 ); 511 512 // Resize: re-render on container resize (keeps labels + paths correct) 513 new ResizeObserver(() => { 514 if (cachedStates) render(cachedStates); 515 }).observe(mapEl); 516 517 // Load and draw 518 d3.json(TOPO_URL).then(topo => { 519 const statesGeoJson = topojson.feature(topo, topo.objects.states); 520 cachedTopoFeature = statesGeoJson; 521 render(statesGeoJson); 522 }); 523 524 </script>
524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 </div> 541 542 <a class="logo-link" style="max-width:282px" href="https://www.exoticca.com/" target="_blank" 543 rel="Exoticca"> 544 <img src='assets/exoticca-logo-white.svg' alt='' /> </a> 545 546 <div class="footer"> 547 <div class="footernote"> 548 <p>Methodology: This study is based on a survey of 3,012 respondents conducted in January 2026, exploring which towns people consider the coziest romantic getaways in each state. Respondents were asked to identify destinations they associate with warmth, comfort, and an inviting romantic atmosphere. Responses were collected via an online panel and balanced across age, gender, and geographic region to reflect a broad cross-section of travellers nationwide. To ensure the results were representative, a two-step methodology was used, combining stratified sampling with post-stratification weighting aligned to national population benchmarks.</p> 549 550 </div> 551 </div> 552 553 <div class="share-links"> 554 555 <a data-toggle="modal" href="#shareModal" class="btn btn-lg btn-primary btn-inline" id="openshare">Share</a> 556 557 <a data-toggle="modal" href="#embedModal" class="btn btn-lg btn-primary btn-inline" id="openembed">Embed</a> 558 </div> 559 560 561 562</div> 563 <!-- Modal - Share --> 564<div class="modal fade" id="shareModal" role="dialog"> 565 <div class="vertical-alignment-helper"> 566 <div class="modal-dialog vertical-align-bottom"> 567 <!-- Modal content--> 568 <div class="modal-content"> 569 <div class="modal-header" style="padding:20px 20px"> 570 571 <h4> 572 <strong><span class="fa fa-code"></span> Share this on your website or blog</strong> 573 </h4> 574 <button type="button" class="close close-share" data-dismiss="modal">×</button> 575 </div> 576 <div class="modal-body"> 577 578 <!-- AddToAny BEGIN --> 579 <div id="share-btns" class="a2a_kit a2a_kit_size_32 a2a_default_style" data-a2a-url="#"> 580 <a class="a2a_button_facebook"></a> 581 <a class="a2a_button_twitter"></a> 582 <a class="a2a_button_whatsapp"></a> 583 <!--<a class="a2a_button_pinterest"></a>--> 584 <a class="a2a_button_linkedin"></a> 585 <a class="a2a_button_facebook_messenger"></a> 586 <a class="a2a_button_telegram"></a> 587 <a class="a2a_button_snapchat"></a> 588 <!--<a class="a2a_button_viber"></a>--> 589 <a class="a2a_button_wechat"></a> 590 <a class="a2a_button_email"></a> 591 <!--<a class="a2a_dd" href="https://www.addtoany.com/share"></a>--> 592 </div> 593 594 </div> 595 <div class="modal-footer"> 596 <button type="submit" class="btn btn-default close-share" data-dismiss="modal"> 597 × Close 598 </button> 599 </div> 600 </div> 601 </div> 602 </div> 603</div><!-- Modal - Embed --> 604<div class="modal fade" id="embedModal" role="dialog"> 605 <div class="vertical-alignment-helper"> 606 <div class="modal-dialog vertical-align-bottom"> 607 <!-- Modal content--> 608 <div class="modal-content"> 609 <div class="modal-header" style="padding:20px 20px"> 610 <h4> 611 <strong><span class="fa fa-code"></span> Embed this on your website or blog</strong> 612 </h4> 613 <button type="button" class="close close-embed" data-dismiss="modal">×</button> 614 </div> 615 <div class="modal-body"> 616 <form role="form"> 617 <p>Copy the code below and paste it into the body of the HTML of your web page.</p> 618 <div class="form-group"> 619 <textarea name="embedder" id="embedder" class="embedder form-control" cols="30" rows="6"><iframe src="[SOURCE_URL]" width="[FRAME_WIDTH]" height="[FRAME_HEIGHT]"id="data_map_iframe" allowfullscreen allowtransparency="" style="border:0;"frameborder="0"></iframe><p style="text-align:center"><small>Created by <a href="[BRAND_URL]" target="_blank">[BRAND]</a>&nbsp;&bull;&nbsp;<a href="[FULL_SIZE_URL]" target="_blank">Viewlarger version</a> </small></p><script>window.addEventListener("message",function(e){e.data.hasOwnProperty("FrameHeight")&& (document.getElementById("data_map_iframe").style.height = e.data.FrameHeight+ 'px')}),document.getElementById("data_map_iframe").onload=function(){ document.getElementById("data_map_iframe").contentWindow.postMessage("FrameHeight","*")}; </script></textarea> 620 </div> 621 </form> 622 </div> 623 <div class="modal-footer"> 624 <button type="submit" class="btn btn-default close-embed" data-dismiss="modal"> 625 × Close 626 </button> 627 </div> 628 </div> 629 </div> 630 </div> 631</div> 632 633 634
634<script src="settings.js"></script>
634 635
635<script async src="https://static.addtoany.com/menu/page.js"></script>
635 636
636<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
636 637
637<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
637 638 639
639<script> 640 641 $('.close-share').on('click touch', function(){ 642 $('body').removeClass('modal-open'); 643 $('#shareModal').removeClass('active'); 644 $('.modal-backdrop').remove(); 645}) 646 647$('#openshare').on('click touch', function(){ 648 $('#shareModal').addClass('active'); 649}) 650 651$('#share-btns').attr('data-a2a-url',projectSettings.fullSizeUrl); 652 653window.addEventListener("message", function (t) { 654 if ("FrameHeight" == t.data) { 655 var a = document.body, 656 i = document.documentElement, 657 e = Math.max(a.scrollHeight, a.offsetHeight, i.clientHeight, i.scrollHeight, i.offsetHeight); 658 e += 20, t.source.postMessage({ 659 FrameHeight: e 660 }, "*") 661 } 662 }); 663 664 665//Table JS 666jQuery(document).ready(function($) { 667 if ($('.table-wrap').length > 0) { 668 $('.table-wrap table').each(function() { 669 var $table = $(this); 670 var headings = []; 671 672 // Get headers from first row 673 $table.find('tbody tr:first-child th').each(function(index) { 674 headings[index] = $(this).text().trim(); 675 }); 676 677 // Loop through all rows after the first 678 $table.find('tbody tr:gt(0)').each(function() { 679 $(this).find('td').each(function(index) { 680 var $td = $(this); 681 if (headings[index]) { 682 $td.attr('data-title', headings[index]); 683 } 684 685 // Wrap existing content in a span if not already wrapped 686 if (!$td.children('span').length) { 687 $td.html('<span>' + $td.html().trim() + '</span>'); 688 } 689 }); 690 }); 691 }); 692 } 693}); 694 $(document).ready(function(){ 695 embedThis = function () { 696 if (void 0 != projectSettings) { 697 var t = $(".embedder"), 698 a = projectSettings.sourceUrl ? projectSettings.sourceUrl : window.location.href, 699 i = projectSettings.brandUrl ? projectSettings.brandUrl : window.location.hostname, 700 e = projectSettings.fullSizeUrl ? projectSettings.fullSizeUrl : window.location.href, 701 s = t.html().replace(/\[URL\]/g, a).replace(/\[SOURCE_URL\]/g, a).replace(/\[BRAND_URL\]/g, i).replace(/\[FULL_SIZE_URL\]/g, e).replace(/\[BRAND\]/g, projectSettings.brandTitle).replace(/\[FRAME_WIDTH\]/g, projectSettings.frameWidth ? projectSettings.frameWidth : "100%").replace(/\[FRAME_HEIGHT\]/g, projectSettings.frameHeight); 702 t.html(s); 703 var n = $(".site_link"); 704 n.each(function () { 705 $(this).attr("href", projectSettings.brandUrl).attr("target", "_blank") 706 }) 707 } else console.log("no project settings"); 708 t.on("click", function () { 709 $(this).select() 710 }) 711}; 712 713 714$('.close-embed').on('click touch', function(){ 715 $('body').removeClass('modal-open'); 716 $('#embedModal').removeClass('active'); 717 $('.modal-backdrop').remove(); 718}) 719 720 721 722$('#openembed').on('click touch', function(){ 723 $('#embedModal').addClass('active'); 724 embedThis(); 725}) 726 if ($('#embedModal').length){ embedThis(); } 727 728 if (projectSettings.google_analytics.match('UA-')) { 729 // Google Analytics: change UA-XXXXX-X to be your site's ID. 730 (function (b, o, i, l, e, r) { 731 b.GoogleAnalyticsObject = l; 732 b[l] || (b[l] = function () { 733 (b[l].q = b[l].q || []).push(arguments) 734 }); 735 b[l].l = +new Date; 736 e = o.createElement(i); 737 r = o.getElementsByTagName(i)[0]; 738 e.src = 'https://www.google-analytics.com/analytics.js'; 739 r.parentNode.insertBefore(e, r) 740 }(window, document, 'script', 'ga')); 741 ga('create', projectSettings.google_analytics); 742 ga('send', 'pageview'); 743 744 if ($('.notice').length){ 745 console.log('notice is true'); 746 $('.notice i').on('touch click', function(){ 747 $('.notice').addClass('disabled'); 748 }) 749 } 750 } 751 752 }); 753 754 755 </script>
755 756 <!--// export--> 757 758 </body> 759</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.