1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, shrink-to-fit=no, viewport-fit=cover"> 6 <title>Your Saved Names | Nameoffice.com</title> 7<meta name="description" content="View your saved business names, compare favorites, and jump back into the NameOffice catalog when you are ready to buy."><link rel="canonical" href="https://qovax.com/favorites"> <link rel="alternate" href="https://qovax.com/favorites" hreflang="en"> 8 <link rel="alternate" href="https://qovax.com/favorites" hreflang="x-default"> 9 10<meta property="og:type" content="website"> 11<meta property="og:site_name" content="NameOffice"> 12<meta property="og:title" content="Your Saved Names | Nameoffice.com"> 13<meta property="og:description" content="View your saved business names, compare favorites, and jump back into the NameOffice catalog when you are ready to buy."><meta property="og:url" content="https://qovax.com/favorites"> <meta property="og:image" content="https://qovax.com/build/img/nameoffice-logo-og.png"> 14 <meta property="og:image:secure_url" content="https://qovax.com/build/img/nameoffice-logo-og.png"> 15 16<meta name="twitter:card" content="summary_large_image"> 17<meta name="twitter:title" content="Your Saved Names | Nameoffice.com"> 18<meta name="twitter:description" content="View your saved business names, compare favorites, and jump back into the NameOffice catalog when you are ready to buy."> <meta name="twitter:image" content="https://qovax.com/build/img/nameoffice-logo-og.png"> 19 20 <link rel="icon" type="image/png" href="/brand/nameoffice-symbol.png"> 21
21<script src="https://cdn.tailwindcss.com"></script>
21 22 <link rel="preconnect" href="https://fonts.googleapis.com"> 23 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 24 <link href="https://fonts.googleapis.com/css2?family=Inter:[email protected]&display=swap" rel="stylesheet"> 25 <link rel="stylesheet" href="https://unicons.iconscout.com/release/v4.0.8/css/line.css"> 26 <link rel="stylesheet" href="/build/front.fa79a3e3.css"> 27
27<script> 28 tailwind.config = { theme: { extend: { 29 colors: { navy: { DEFAULT: '#01102E', deep: '#080e1f' }, mint: { DEFAULT: '#74F7B2' } }, 30 fontFamily: { sans: ['Inter','system-ui','sans-serif'] }, 31 }}} 32 </script>
32 33 <style> body { font-feature-settings:"ss01","cv11"; -webkit-font-smoothing:antialiased; } </style> 34
34<script> 35 window.dataLayer = window.dataLayer || []; 36 function gtag(){ dataLayer.push(arguments); } 37 38 // Must run before gtag.js loads, otherwise the first hit ignores consent. 39 // Denied by default where prior opt-in is required, granted where consent 40 // is implied until the visitor declines. Decided server side from the 41 // country, the same call the banner and the pixel make. 42 (function () { 43 var requireOptIn = true; 44 var start = requireOptIn ? 'denied' : 'granted'; 45 46 gtag('consent', 'default', { 47 ad_storage: start, 48 ad_user_data: start, 49 ad_personalization: start, 50 analytics_storage: start, 51 functionality_storage: start, 52 personalization_storage: start, 53 security_storage: 'granted', 54 wait_for_update: 500 55 }); 56 })(); 57 58 (function () { 59 function readConsent() { 60 var m = document.cookie.match('(^|;)\\s*cookie_consent\\s*=\\s*([^;]+)'); 61 if (!m) return null; 62 try { return JSON.parse(decodeURIComponent(m[2])); } catch (e) { return null; } 63 } 64 65 function apply(consent) { 66 if (!consent) return; 67 var analytics = consent.analytics ? 'granted' : 'denied'; 68 // No marketing category yet means ads storage stays denied. When a 69 // marketing toggle is added, `consent.marketing` drives these three. 70 var marketing = consent.marketing ? 'granted' : 'denied'; 71 gtag('consent', 'update', { 72 analytics_storage: analytics, 73 functionality_storage: analytics, 74 personalization_storage: marketing, 75 ad_storage: marketing, 76 ad_user_data: marketing, 77 ad_personalization: marketing 78 }); 79 } 80 81 apply(readConsent()); 82 window.addEventListener('cookieconsent:changed', function (e) { apply(e.detail || readConsent()); }); 83 })(); 84 85 gtag('js', new Date()); 86 gtag('config', 'G-R1P1T58PSS'); 87</script>
vendor: 64 bytes, lines 87-88
87 88<script async src="https://www.googletagmanager.com/gtag/js?id=
88G-R1P1T58PSS
vendor: 13 bytes, lines 88-89
88"></script> 89
90<script> 91(function () { 92 var PIXEL_ID = "1386794040301436"; 93 var loaded = false; 94 95 /* Read the consent cookie directly rather than window.cookieConsent. 96 This partial runs in the head and the consent script runs at the end of 97 the body, so that global does not exist yet. A returning visitor who 98 already agreed would otherwise never get the pixel, because the change 99 event only fires when someone changes their mind. */ 100 /* Decided server side, the same way the banner decides it, so the two can 101 never disagree about whether this visitor has to be asked first. */ 102 var REQUIRE_OPTIN = true; 103 104 function allowed() { 105 try { 106 if (window.cookieConsent) return !!window.cookieConsent.marketing; 107 108 var m = document.cookie.match('(^|;)\\s*cookie_consent\\s*=\\s*([^;]+)'); 109 if (m) { 110 var c = JSON.parse(decodeURIComponent(m[2])); 111 return !!(c && c.v === 1 && c.marketing); 112 } 113 114 /* No stored choice. Where prior opt-in is required that means no, 115 and it keeps meaning no until they say otherwise. */ 116 return !REQUIRE_OPTIN; 117 } catch (e) { 118 return false; 119 } 120 } 121 122 function load() { 123 if (loaded || !allowed()) return; 124 loaded = true; 125 126 /* Meta's standard loader. */ 127 !function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod? 128 n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n; 129 n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0; 130 t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window, 131 document,'script','https://connect.facebook.net/en_US/fbevents.js'); 132 133 fbq('init', PIXEL_ID); 134 fbq('track', 'PageView'); 135 136 /* Anything queued before consent, such as a purchase on this very page, 137 is sent once rather than lost. */ 138 var pending = window.__fbPending || []; 139 window.__fbPending = null; 140 pending.forEach(function (args) { 141 try { fbq.apply(null, args); } catch (e) {} 142 }); 143 } 144 145 /* Queue an event whether or not the pixel is up yet. Callers do not have to 146 know about consent, which keeps that decision in one place. */ 147 window.fbTrack = function () { 148 var args = Array.prototype.slice.call(arguments); 149 if (loaded && window.fbq) { 150 try { fbq.apply(null, args); } catch (e) {} 151 return; 152 } 153 window.__fbPending = window.__fbPending || []; 154 window.__fbPending.push(args); 155 }; 156 157 load(); 158 window.addEventListener('cookieconsent:changed', load); 159})(); 160</script>
vendor: 1 bytes, line 160
160
161<script> 162(function () { 163 var PIXEL_ID = "a2_jqulur9sdzbk"; 164 var MATCH = null; 165 var loaded = false; 166 167 /* Read the consent cookie directly rather than window.cookieConsent. This 168 partial runs in the head and the consent script runs at the end of the 169 body, so that global does not exist yet. A returning visitor who already 170 agreed would otherwise never get the pixel, because the change event only 171 fires when someone changes their mind. */ 172 /* Decided server side, the same way the banner decides it, so the two can 173 never disagree about whether this visitor has to be asked first. */ 174 var REQUIRE_OPTIN = true; 175 176 function allowed() { 177 try { 178 if (window.cookieConsent) return !!window.cookieConsent.marketing; 179 180 var m = document.cookie.match('(^|;)\\s*cookie_consent\\s*=\\s*([^;]+)'); 181 if (m) { 182 var c = JSON.parse(decodeURIComponent(m[2])); 183 return !!(c && c.v === 1 && c.marketing); 184 } 185 186 /* No stored choice. Where prior opt-in is required that means no, 187 and it keeps meaning no until they say otherwise. */ 188 return !REQUIRE_OPTIN; 189 } catch (e) { 190 return false; 191 } 192 } 193 194 function load() { 195 if (loaded || !allowed()) return; 196 loaded = true; 197 198 /* Reddit's standard loader, with the id read from a variable instead of 199 hard-coded into the query string. */ 200 !function(w,d){if(!w.rdt){var p=w.rdt=function(){p.sendEvent?p.sendEvent.apply(p,arguments):p.callQueue.push(arguments)};p.callQueue=[]; 201 var t=d.createElement("script");t.src="https://www.redditstatic.com/ads/pixel.js?pixel_id="+encodeURIComponent(PIXEL_ID),t.async=!0; 202 var s=d.getElementsByTagName("script")[0];s.parentNode.insertBefore(t,s)}}(window,document); 203 204 /* Reddit only accepts the third argument when there is something in it. */ 205 if (MATCH) { 206 rdt('init', PIXEL_ID, MATCH); 207 } else { 208 rdt('init', PIXEL_ID); 209 } 210 rdt('track', 'PageVisit'); 211 212 /* Anything queued before consent, such as a purchase on this very page, 213 is sent once rather than lost. */ 214 var pending = window.__rdtPending || []; 215 window.__rdtPending = null; 216 pending.forEach(function (args) { 217 try { rdt.apply(null, args); } catch (e) {} 218 }); 219 } 220 221 /* Queue an event whether or not the pixel is up yet. Callers do not have to 222 know about consent, which keeps that decision in one place. */ 223 window.rdtTrack = function () { 224 var args = Array.prototype.slice.call(arguments); 225 if (loaded && window.rdt) { 226 try { rdt.apply(null, args); } catch (e) {} 227 return; 228 } 229 window.__rdtPending = window.__rdtPending || []; 230 window.__rdtPending.push(args); 231 }; 232 233 load(); 234 window.addEventListener('cookieconsent:changed', load); 235})(); 236</script>
236 237</head> 238<body class="min-h-screen bg-[#f8f8fa] font-sans text-[#1a1a2e]"> 239 240 <header class="topbar-v2"> 241 <div class="topbar-v2__main"> 242 <div class="topbar-v2__inner"> 243 <a href="/" class="topbar-v2__logo"> 244 <img src="/brand/nameoffice-logo.png" alt="NameOffice"> 245 </a> 246 247 <nav class="topbar-v2__nav"> 248 <a href="/business-names" 249 class=""> 250 Browse Names 251 </a> 252 <a href="/guides" 253 class=""> 254 Guides 255 </a> 256 <a href="/reviews" 257 class=""> 258 Reviews 259 </a> 260 <a href="/contact" 261 class=""> 262 Contact 263 </a> 264 <a href="/about" 265 class=""> 266 About NameOffice 267 </a> 268 </nav> 269 270 <div class="topbar-v2__actions"> 271 <details class="topbar-v2__phone-menu"> 272 <summary class="topbar-v2__phone-trigger" aria-label="Call or text NameOffice"> 273 <svg class="topbar-v2__phone-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 16.9v3a2 2 0 0 1-2.2 2 19.8 19.8 0 0 1-8.6-3.1 19.5 19.5 0 0 1-6-6A19.8 19.8 0 0 1 2.1 4.2 2 2 0 0 1 4.1 2h3a2 2 0 0 1 2 1.7c.1 1 .4 1.9.7 2.8a2 2 0 0 1-.5 2.1L8.1 9.9a16 16 0 0 0 6 6l1.3-1.3a2 2 0 0 1 2.1-.4c.9.3 1.8.5 2.8.6a2 2 0 0 1 1.7 2Z"/></svg> 274 <span class="topbar-v2__phone-number">(972) 914-8485</span> 275 </summary> 276 <div class="topbar-v2__phone-panel"> 277 <a href="tel:+19729148485" class="topbar-v2__phone-link"> 278 <span class="topbar-v2__phone-link-label">Call NameOffice</span> 279 <span>(972) 914-8485</span> 280 </a> 281 <a href="sms:+19729148485" class="topbar-v2__phone-link"> 282 <span class="topbar-v2__phone-link-label">Text NameOffice</span> 283 <span>(972) 914-8485</span> 284 </a> 285 </div> 286 </details> 287 <a href="/favorites" class="topbar-v2__icon-btn" aria-label="Favorites" id="v2-fav-btn" data-favorites-link="true"> 288 <i class="uil uil-heart"></i> 289 <span class="topbar-v2__badge topbar-v2__badge--hidden" id="v2-fav-badge">0</span> 290 </a> 291 <a href="/cart" class="topbar-v2__icon-btn" aria-label="Cart" id="v2-cart-btn"> 292 <i class="uil uil-shopping-bag"></i> 293 <span class="topbar-v2__badge topbar-v2__badge--hidden" id="v2-cart-badge">0</span> 294 </a> 295 </div> 296 </div> 297 298 </div> 299 300 <div class="topbar-v2__sub" role="navigation" aria-label="Secondary navigation"> 301 <div class="topbar-v2__sub-inner"> 302 <nav class="topbar-v2__mobile-links" aria-label="Primary site links"> 303 <a href="/business-names" class="">Browse</a> 304 <a href="/guides" class="">Guides</a> 305 <a href="/reviews" class="">Reviews</a> 306 <a href="/contact" class="">Contact</a> 307 <a href="/about" class="">About</a> 308 </nav> 309 <p class="topbar-v2__sub-line"> 310 Department of Premium Names 311 <span class="topbar-v2__sub-sep" aria-hidden="true">|</span> 312 Est. 2014 313 </p> 314 </div> 315 </div> 316</header> 317
318<script> 319(function() { 320 var marketplaceBaseUrl = ""; 321 var limits = { cartDomains: 25, shortlistedDomains: 100 }; 322 323 function getCookie(name) { 324 if (marketplaceBaseUrl) return null; 325 var m = document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)'); 326 return m ? decodeURIComponent(m[2]) : null; 327 } 328 329 function normalizeList(values, limit) { 330 if (!Array.isArray(values)) return []; 331 var seen = {}; 332 return values.map(function(value) { return parseInt(value, 10) || 0; }) 333 .filter(function(value) { 334 if (!value || seen[value]) return false; 335 seen[value] = true; 336 return true; 337 }) 338 .slice(0, limit); 339 } 340 341 function readList(name) { 342 if (marketplaceBaseUrl) return []; 343 var raw = getCookie(name); 344 if (!raw) return []; 345 if (raw.length > 3800) { 346 writeList(name, []); 347 return []; 348 } 349 try { 350 var values = JSON.parse(raw); 351 var normalized = normalizeList(values, limits[name] || 25); 352 if (!Array.isArray(values) || JSON.stringify(values) !== JSON.stringify(normalized)) { 353 writeList(name, normalized); 354 } 355 return normalized; 356 } catch (e) { 357 writeList(name, []); 358 return []; 359 } 360 } 361 362 function writeList(name, values) { 363 if (marketplaceBaseUrl) return []; 364 var normalized = normalizeList(values, limits[name] || 25); 365 var expires = new Date(Date.now() + 365 * 24 * 60 * 60 * 1000); 366 var secure = location.protocol === 'https:' ? ';Secure' : ''; 367 document.cookie = name + '=' + encodeURIComponent(JSON.stringify(normalized)) 368 + ';expires=' + expires.toUTCString() + ';path=/;SameSite=Lax' + secure; 369 return normalized; 370 } 371 372 window.NameOfficeSelectionState = { 373 read: readList, 374 write: writeList, 375 limits: limits 376 }; 377 378 function updateBadge(id, count) { 379 var el = document.getElementById(id); 380 if (!el) return; 381 if (count > 0) { 382 el.textContent = count; 383 el.classList.remove('topbar-v2__badge--hidden'); 384 } else { 385 el.classList.add('topbar-v2__badge--hidden'); 386 } 387 } 388 function refresh() { 389 // Favourites are stored in this hostname's cookie and the heart now links 390 // to this hostname's list, so the count is meaningful on a pointed domain 391 // too â it used to bail out here and the badge simply never appeared. 392 updateBadge('v2-fav-badge', readList('shortlistedDomains').length); 393 394 // The cart still hands off to the marketplace to check out, so its cookie 395 // lives over there and there is nothing local to count. 396 if (!marketplaceBaseUrl) { 397 updateBadge('v2-cart-badge', readList('cartDomains').length); 398 } 399 } 400 401 document.addEventListener('click', function(event) { 402 document.querySelectorAll('.topbar-v2__phone-menu[open]').forEach(function(menu) { 403 if (!menu.contains(event.target)) { 404 menu.removeAttribute('open'); 405 } 406 }); 407 }); 408 409 document.addEventListener('keydown', function(event) { 410 if (event.key !== 'Escape') { 411 return; 412 } 413 document.querySelectorAll('.topbar-v2__phone-menu[open]').forEach(function(menu) { 414 menu.removeAttribute('open'); 415 }); 416 }); 417 418 refresh(); 419 document.addEventListener('shortlist:update', refresh); 420 document.addEventListener('cart:update', refresh); 421})(); 422</script>
422 423 424 <main class="mx-auto max-w-7xl px-5 py-12 md:px-8 md:py-16"> 425 <div class="mb-8"> 426 <div class="text-xs uppercase tracking-[0.28em] text-gray-400">Your shortlist</div> 427 <h1 class="mt-2 text-3xl font-semibold tracking-tight md:text-4xl">Saved names</h1> 428 <p id="resultCount" class="mt-1 text-sm text-gray-500">Loadingâ¦</p> 429 </div> 430 431 <section 432 id="favorites-share-tools" 433 class="mb-6 rounded-[26px] border border-[#d7e5f6] bg-[linear-gradient(180deg,rgba(255,255,255,0.94)_0%,rgba(244,249,255,0.98)_100%)] p-4 shadow-[0_18px_38px_rgba(7,23,51,0.06)] sm:p-5" 434 data-create-url="/api/favorites/share-links" 435 data-share-url-template="/api/favorites/share-links/00000000000000000000000000000000/share" 436 data-share-token-placeholder="00000000000000000000000000000000" 437 data-initial-expanded="0"> 438 <div id="favorites-share-summary" class="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between"> 439 <div class="flex flex-wrap items-center gap-2"> 440 <button type="button" id="favorites-share-toggle" aria-expanded="false" class="inline-flex items-center gap-2 rounded-full bg-navy px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-white shadow-[0_10px_24px_rgba(1,16,46,0.18)] transition hover:bg-[#011845]"> 441 Share favorites 442 <span id="favorites-share-toggle-icon" aria-hidden="true" class="shrink-0"> 443 <svg width="12" height="12" viewBox="0 0 20 20" fill="none"><path d="M5 7.5 10 12.5l5-5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> 444 </span> 445 </button> 446 <span class="rounded-full border border-[#d7e5f6] bg-white/90 px-3 py-1 text-xs font-medium text-[#62708a]">Private shareable shortlist link</span> 447 </div> 448 <div class="text-sm text-[#62708a] lg:text-right">Send your saved names to a partner, client, or teammate.</div> 449 </div> 450 451 <div id="favorites-share-panel" class="mt-4 hidden border-t border-[#dbe6f3] pt-4"> 452 <div class="max-w-2xl"> 453 <h2 class="font-display text-xl font-bold tracking-tight text-gray-900">Share this exact shortlist with one private link</h2> 454 <p class="mt-1.5 text-sm leading-relaxed text-gray-500">Generate a link that opens this same selection of saved names. Useful for sending your shortlist to a partner, client, or team member.</p> 455 </div> 456 457 <div class="mt-4 flex flex-wrap items-center gap-3"> 458 <button type="button" id="favorites-share-create" class="inline-flex items-center gap-2 rounded-xl bg-navy px-4 py-3 text-sm font-bold text-white transition hover:brightness-110"> 459 <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#74F7B2" stroke-width="2"><path d="M10 13a5 5 0 0 0 7.07 0l1.41-1.41a5 5 0 0 0-7.07-7.07L10.59 5"/><path d="M14 11a5 5 0 0 0-7.07 0L5.5 12.43a5 5 0 1 0 7.07 7.07L13.41 19"/></svg> 460 Create shareable link 461 </button> 462 <div class="text-xs text-[#72819a]">Creates a private URL for your current saved names only.</div> 463 </div> 464 465 <div id="favorites-share-result" class="mt-4 hidden rounded-[22px] border border-[#d7e5f6] bg-white/85 p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.92)]"> 466 <label for="favorites-share-url" class="mb-2 block text-[11px] font-semibold uppercase tracking-[0.16em] text-[#8b97ad]">Share URL</label> 467 <div class="flex flex-col gap-2 lg:flex-row"> 468 <input id="favorites-share-url" type="text" readonly class="min-w-0 flex-1 rounded-xl border border-[#d7e5f6] bg-[#f8fbff] px-4 py-3 text-sm font-medium text-navy outline-none focus:border-navy/40" value=""> 469 <div class="flex flex-wrap gap-2"> 470 <button type="button" id="favorites-share-copy" class="inline-flex items-center gap-2 rounded-xl border border-[#d1dcec] bg-white px-4 py-3 text-sm font-semibold text-[#62708a] transition hover:border-navy/30 hover:text-navy"> 471 Copy link 472 </button> 473 <button type="button" id="favorites-share-native" class="inline-flex items-center gap-2 rounded-xl border border-[#d1dcec] bg-white px-4 py-3 text-sm font-semibold text-[#62708a] transition hover:border-navy/30 hover:text-navy"> 474 Share 475 </button> 476 <a id="favorites-share-open" href="#" target="_blank" rel="noopener" class="inline-flex items-center gap-2 rounded-xl border border-[#d1dcec] bg-white px-4 py-3 text-sm font-semibold text-[#62708a] transition hover:border-navy/30 hover:text-navy"> 477 Open link 478 </a> 479 </div> 480 </div> 481 <p id="favorites-share-status" class="mt-3 text-sm text-gray-500">Create a private link, then copy or share it from here.</p> 482 </div> 483 </div> 484 </section> 485 486 <div id="grid" class="grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4"></div> 487 488 <div id="sentinel" class="h-12"></div> 489 <div id="loadingRow" class="hidden py-8 text-center text-sm text-gray-400">Loading moreâ¦</div> 490 <div id="emptyRow" class="hidden rounded-3xl border border-dashed border-gray-200 bg-white p-16 text-center"> 491 <p class="text-sm text-gray-500">You haven't saved any names yet.</p> 492 <a href="/business-names" class="mt-3 inline-block text-sm font-semibold text-navy underline underline-offset-4">Browse domains â</a> 493 </div> 494 </main> 495 496 <footer class="footer-v2"> 497 <div class="footer-v2__inner"> 498 <div class="footer-v2__top"> 499 <a href="/" class="footer-v2__logo"> 500 <img src="/brand/nameoffice-symbol.png" alt="NameOffice icon">
501 <span>NameOffice</span> 502 </a> 503 <p class="footer-v2__tagline">Curated premium domains for ambitious brands.</p> 504 </div> 505 506 <section class="footer-landing" aria-labelledby="footer-landing-title"> 507 <div class="footer-landing__head"> 508 <p class="footer-landing__kicker">Explore by topic</p> 509 <h3 id="footer-landing-title">Business naming pages, guides & buying paths</h3> 510 <p class="footer-landing__intro">Pick a category to browse the NameOffice landing-page library, then jump into the live catalog for the names you want next.</p> 511 </div> 512 513 <div class="footer-landing__tabs" role="tablist" aria-label="Landing page categories"> 514 <button type="button" 515 class="footer-landing__tab is-active" 516 role="tab" 517 id="footer-landing-tab-1" 518 aria-controls="footer-landing-panel-1" 519 aria-selected="true" 520 tabindex="0" 521 data-footer-landing-tab="1"> 522 <span class="footer-landing__tab-label">Business Name Ideas</span> 523 <span class="footer-landing__tab-count">44</span> 524 </button> 525 <button type="button" 526 class="footer-landing__tab" 527 role="tab" 528 id="footer-landing-tab-2" 529 aria-controls="footer-landing-panel-2" 530 aria-selected="false" 531 tabindex="-1" 532 data-footer-landing-tab="2"> 533 <span class="footer-landing__tab-label">Buy Domain Names</span> 534 <span class="footer-landing__tab-count">14</span> 535 </button> 536 <button type="button" 537 class="footer-landing__tab" 538 role="tab" 539 id="footer-landing-tab-3" 540 aria-controls="footer-landing-panel-3" 541 aria-selected="false" 542 tabindex="-1" 543 data-footer-landing-tab="3"> 544 <span class="footer-landing__tab-label">Name Generators</span> 545 <span class="footer-landing__tab-count">15</span> 546 </button> 547 <button type="button" 548 class="footer-landing__tab" 549 role="tab" 550 id="footer-landing-tab-4" 551 aria-controls="footer-landing-panel-4" 552 aria-selected="false" 553 tabindex="-1" 554 data-footer-landing-tab="4"> 555 <span class="footer-landing__tab-label">Naming Guides</span> 556 <span class="footer-landing__tab-count">17</span> 557 </button> 558 </div> 559 560 <div class="footer-landing__panels"> 561 <div class="footer-landing__panel is-active" 562 role="tabpanel" 563 id="footer-landing-panel-1" 564 aria-labelledby="footer-landing-tab-1" 565 data-footer-landing-panel="1"> 566 <ul class="footer-landing__links"> 567 <li> 568 <a href="/businesses-names"> 569 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 570 <span>Businesses Names</span> 571 </a> 572 </li> 573 <li> 574 <a href="/candy-shop-name-ideas"> 575 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
576 <span>Candy Shop Name Ideas</span> 577 </a> 578 </li> 579 <li> 580 <a href="/car-lot-names"> 581 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 582 <span>Car Lot Names</span> 583 </a> 584 </li> 585 <li> 586 <a href="/catchy-business-name-suggestions"> 587 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 588 <span>Catchy Business Name Suggestions</span> 589 </a> 590 </li> 591 <li> 592 <a href="/catering-company-name-ideas"> 593 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 594 <span>Catering Company Name Ideas</span> 595 </a> 596 </li> 597 <li> 598 <a href="/clever-business-names"> 599 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 600 <span>Clever Business Names</span> 601 </a> 602 </li> 603 <li> 604 <a href="/club-name-suggestions"> 605 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 606 <span>Club Name Suggestions</span> 607 </a> 608 </li> 609 <li> 610 <a href="/coffee-name-suggestions"> 611 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 612 <span>Coffee Name Suggestions</span> 613 </a> 614 </li> 615 <li> 616 <a href="/company-name-examples"> 617 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 618 <span>Company Name Examples</span> 619 </a> 620 </li> 621 <li> 622 <a href="/consulting-company-name-suggestions"> 623 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 624 <span>Consulting Company Name Suggestions</span> 625 </a> 626 </li> 627 <li> 628 <a href="/consulting-llc-names"> 629 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
630 <span>Consulting Llc Names</span> 631 </a> 632 </li> 633 <li> 634 <a href="/cool-travel-agency-names"> 635 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 636 <span>Cool Travel Agency Names</span> 637 </a> 638 </li> 639 <li> 640 <a href="/cosmetic-name-ideas"> 641 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 642 <span>Cosmetic Name Ideas</span> 643 </a> 644 </li> 645 <li> 646 <a href="/cute-brand-names"> 647 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 648 <span>Cute Brand Names</span> 649 </a> 650 </li> 651 <li> 652 <a href="/dating-app-logos-and-names"> 653 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 654 <span>Dating App Logos And Names</span> 655 </a> 656 </li> 657 <li> 658 <a href="/design-business-names"> 659 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 660 <span>Design Business Names</span> 661 </a> 662 </li> 663 <li> 664 <a href="/event-planner-business-names"> 665 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 666 <span>Event Planner Business Names</span> 667 </a> 668 </li> 669 <li> 670 <a href="/fashion-line-name-ideas"> 671 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 672 <span>Fashion Line Name Ideas</span> 673 </a> 674 </li> 675 <li> 676 <a href="/find-me-a-company-name"> 677 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 678 <span>Find Me A Company Name</span> 679 </a> 680 </li> 681 <li> 682 <a href="/firm-name-suggestion"> 683 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
684 <span>Firm Name Suggestion</span> 685 </a> 686 </li> 687 <li> 688 <a href="/gambling-names"> 689 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 690 <span>Gambling Names</span> 691 </a> 692 </li> 693 <li> 694 <a href="/good-app-names"> 695 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 696 <span>Good App Names</span> 697 </a> 698 </li> 699 <li> 700 <a href="/good-names-for-a-company"> 701 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 702 <span>Good Names For A Company</span> 703 </a> 704 </li> 705 <li> 706 <a href="/good-title-for-dating-site"> 707 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 708 <span>Good Title For Dating Site</span> 709 </a> 710 </li> 711 <li> 712 <a href="/hardware-shop-name"> 713 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 714 <span>Hardware Shop Name</span> 715 </a> 716 </li> 717 <li> 718 <a href="/home-care-business-names"> 719 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 720 <span>Home Care Business Names</span> 721 </a> 722 </li> 723 <li> 724 <a href="/ideas-for-non-profit-organization-names"> 725 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 726 <span>Ideas For Non Profit Organization Names</span> 727 </a> 728 </li> 729 <li> 730 <a href="/ideas-for-product-names"> 731 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 732 <span>Ideas For Product Names</span> 733 </a> 734 </li> 735 <li> 736 <a href="/list-of-company-name-ideas"> 737 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
738 <span>List Of Company Name Ideas</span> 739 </a> 740 </li> 741 <li> 742 <a href="/made-up-business-names"> 743 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 744 <span>Made Up Business Names</span> 745 </a> 746 </li> 747 <li> 748 <a href="/name-for-small-business"> 749 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 750 <span>Name For Small Business</span> 751 </a> 752 </li> 753 <li> 754 <a href="/name-ideas-for-companies"> 755 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 756 <span>Name Ideas For Companies</span> 757 </a> 758 </li> 759 <li> 760 <a href="/names-for-a-cafe"> 761 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 762 <span>Names For A Cafe</span> 763 </a> 764 </li> 765 <li> 766 <a href="/names-for-corporations"> 767 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 768 <span>Names For Corporations</span> 769 </a> 770 </li> 771 <li> 772 <a href="/names-for-home-health-care-business"> 773 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 774 <span>Names For Home Health Care Business</span> 775 </a> 776 </li> 777 <li> 778 <a href="/names-for-investment-companies"> 779 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 780 <span>Names For Investment Companies</span> 781 </a> 782 </li> 783 <li> 784 <a href="/names-for-it-companies-with-meaning"> 785 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 786 <span>Names For It Companies With Meaning</span> 787 </a> 788 </li> 789 <li> 790 <a href="/renovation-company-names"> 791 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
792 <span>Renovation Company Names</span> 793 </a> 794 </li> 795 <li> 796 <a href="/staffing-agency-names"> 797 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 798 <span>Staffing Agency Names</span> 799 </a> 800 </li> 801 <li> 802 <a href="/tech-company-name-suggestions"> 803 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 804 <span>Tech Company Name Suggestions</span> 805 </a> 806 </li> 807 <li> 808 <a href="/transport-company-names"> 809 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 810 <span>Transport Company Names</span> 811 </a> 812 </li> 813 <li> 814 <a href="/unique-project-names-list"> 815 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 816 <span>Unique Project Names List</span> 817 </a> 818 </li> 819 <li> 820 <a href="/what-should-i-name-my-company"> 821 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 822 <span>What Should I Name My Company</span> 823 </a> 824 </li> 825 <li> 826 <a href="/whats-a-good-company-name"> 827 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 828 <span>Whats A Good Company Name</span> 829 </a> 830 </li> 831 </ul> 832 <button type="button" class="footer-landing__more" data-footer-landing-more aria-expanded="false" hidden> 833 <span class="footer-landing__more-show">Show all 44 business name ideas</span> 834 <span class="footer-landing__more-hide">Show fewer</span> 835 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg> 836 </button> 837 </div> 838 <div class="footer-landing__panel" 839 role="tabpanel" 840 id="footer-landing-panel-2" 841 aria-labelledby="footer-landing-tab-2" 842 data-footer-landing-panel="2"> 843 <ul class="footer-landing__links"> 844 <li> 845 <a href="/buy-a-site"> 846 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
847 <span>Buy A Site</span> 848 </a> 849 </li> 850 <li> 851 <a href="/buy-brand-name"> 852 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 853 <span>Buy Brand Name</span> 854 </a> 855 </li> 856 <li> 857 <a href="/buy-your-domain"> 858 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 859 <span>Buy Your Domain</span> 860 </a> 861 </li> 862 <li> 863 <a href="/company-names-not-taken"> 864 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 865 <span>Company Names Not Taken</span> 866 </a> 867 </li> 868 <li> 869 <a href="/cool-web-domain-names"> 870 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 871 <span>Cool Web Domain Names</span> 872 </a> 873 </li> 874 <li> 875 <a href="/creative-domain-names"> 876 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 877 <span>Creative Domain Names</span> 878 </a> 879 </li> 880 <li> 881 <a href="/domain-selling-sites"> 882 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 883 <span>Domain Selling Sites</span> 884 </a> 885 </li> 886 <li> 887 <a href="/how-do-i-buy-a-taken-domain-name"> 888 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 889 <span>How Do I Buy A Taken Domain Name</span> 890 </a> 891 </li> 892 <li> 893 <a href="/most-expensive-domain-names-for-sale"> 894 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 895 <span>Most Expensive Domain Names For Sale</span> 896 </a> 897 </li> 898 <li> 899 <a href="/premium-domain-checker"> 900 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 901 <span>Premium Domain Checker</span> 902 </a> 903 </li> 904 <li> 905 <a href="/purchase-my-domain-name"> 906 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
907 <span>Purchase My Domain Name</span> 908 </a> 909 </li> 910 <li> 911 <a href="/short-domains-for-sale"> 912 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 913 <span>Short Domains For Sale</span> 914 </a> 915 </li> 916 <li> 917 <a href="/where-can-i-buy-domains"> 918 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 919 <span>Where Can I Buy Domains</span> 920 </a> 921 </li> 922 <li> 923 <a href="/where-to-buy-website-domains"> 924 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 925 <span>Where To Buy Website Domains</span> 926 </a> 927 </li> 928 </ul> 929 <button type="button" class="footer-landing__more" data-footer-landing-more aria-expanded="false" hidden> 930 <span class="footer-landing__more-show">Show all 14 buy domain names</span> 931 <span class="footer-landing__more-hide">Show fewer</span> 932 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg> 933 </button> 934 </div> 935 <div class="footer-landing__panel" 936 role="tabpanel" 937 id="footer-landing-panel-3" 938 aria-labelledby="footer-landing-tab-3" 939 data-footer-landing-panel="3"> 940 <ul class="footer-landing__links"> 941 <li> 942 <a href="/catchy-website-name-generator"> 943 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 944 <span>Catchy Website Name Generator</span> 945 </a> 946 </li> 947 <li> 948 <a href="/company-name-generator-from-keywords"> 949 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 950 <span>Company Name Generator From Keywords</span> 951 </a> 952 </li> 953 <li> 954 <a href="/corporate-name-generator"> 955 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 956 <span>Corporate Name Generator</span> 957 </a> 958 </li> 959 <li> 960 <a href="/create-business-name-generator"> 961 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
962 <span>Create Business Name Generator</span> 963 </a> 964 </li> 965 <li> 966 <a href="/ecommerce-name-generator"> 967 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 968 <span>Ecommerce Name Generator</span> 969 </a> 970 </li> 971 <li> 972 <a href="/generate-a-name-for-my-business"> 973 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 974 <span>Generate A Name For My Business</span> 975 </a> 976 </li> 977 <li> 978 <a href="/handmade-business-name-generator"> 979 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 980 <span>Handmade Business Name Generator</span> 981 </a> 982 </li> 983 <li> 984 <a href="/new-business-name-generator"> 985 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 986 <span>New Business Name Generator</span> 987 </a> 988 </li> 989 <li> 990 <a href="/product-name-generator-free"> 991 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 992 <span>Product Name Generator Free</span> 993 </a> 994 </li> 995 <li> 996 <a href="/product-namer"> 997 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 998 <span>Product Namer</span> 999 </a> 1000 </li> 1001 <li> 1002 <a href="/real-estate-business-name-generator"> 1003 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1004 <span>Real Estate Business Name Generator</span> 1005 </a> 1006 </li> 1007 <li> 1008 <a href="/software-name-generator"> 1009 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1010 <span>Software Name Generator</span> 1011 </a> 1012 </li> 1013 <li> 1014 <a href="/startup-business-name-generator"> 1015 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
1016 <span>Startup Business Name Generator</span> 1017 </a> 1018 </li> 1019 <li> 1020 <a href="/travel-company-names-generator"> 1021 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1022 <span>Travel Company Names Generator</span> 1023 </a> 1024 </li> 1025 <li> 1026 <a href="/web-name-generator"> 1027 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1028 <span>Web Name Generator</span> 1029 </a> 1030 </li> 1031 </ul> 1032 <button type="button" class="footer-landing__more" data-footer-landing-more aria-expanded="false" hidden> 1033 <span class="footer-landing__more-show">Show all 15 name generators</span> 1034 <span class="footer-landing__more-hide">Show fewer</span> 1035 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg> 1036 </button> 1037 </div> 1038 <div class="footer-landing__panel" 1039 role="tabpanel" 1040 id="footer-landing-panel-4" 1041 aria-labelledby="footer-landing-tab-4" 1042 data-footer-landing-panel="4"> 1043 <ul class="footer-landing__links"> 1044 <li> 1045 <a href="/coming-up-with-business-name"> 1046 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1047 <span>Coming Up With Business Name</span> 1048 </a> 1049 </li> 1050 <li> 1051 <a href="/company-name-services"> 1052 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1053 <span>Company Name Services</span> 1054 </a> 1055 </li> 1056 <li> 1057 <a href="/company-naming-agency"> 1058 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1059 <span>Company Naming Agency</span> 1060 </a> 1061 </li> 1062 <li> 1063 <a href="/creating-a-brand-name"> 1064 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1065 <span>Creating A Brand Name</span> 1066 </a> 1067 </li> 1068 <li> 1069 <a href="/for-seo-should-my-company-name-be-in-html"> 1070 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
1071 <span>For Seo Should My Company Name Be In Html</span> 1072 </a> 1073 </li> 1074 <li> 1075 <a href="/good-project-names"> 1076 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1077 <span>Good Project Names</span> 1078 </a> 1079 </li> 1080 <li> 1081 <a href="/how-do-i-pick-a-name-for-my-business"> 1082 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1083 <span>How Do I Pick A Name For My Business</span> 1084 </a> 1085 </li> 1086 <li> 1087 <a href="/how-to-choose-business-name"> 1088 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1089 <span>How To Choose Business Name</span> 1090 </a> 1091 </li> 1092 <li> 1093 <a href="/how-to-come-up-with-business-names"> 1094 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1095 <span>How To Come Up With Business Names</span> 1096 </a> 1097 </li> 1098 <li> 1099 <a href="/how-to-pick-a-name-for-my-business"> 1100 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1101 <span>How To Pick A Name For My Business</span> 1102 </a> 1103 </li> 1104 <li> 1105 <a href="/how-to-think-of-a-business-name"> 1106 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1107 <span>How To Think Of A Business Name</span> 1108 </a> 1109 </li> 1110 <li> 1111 <a href="/make-brand-name"> 1112 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1113 <span>Make Brand Name</span> 1114 </a> 1115 </li> 1116 <li> 1117 <a href="/naming-a-marketing-agency"> 1118 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1119 <span>Naming A Marketing Agency</span> 1120 </a> 1121 </li> 1122 <li> 1123 <a href="/picking-a-name-for-your-business"> 1124 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
1125 <span>Picking A Name For Your Business</span> 1126 </a> 1127 </li> 1128 <li> 1129 <a href="/what-is-a-business-name"> 1130 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1131 <span>What Is A Business Name</span> 1132 </a> 1133 </li> 1134 <li> 1135 <a href="/what-to-name-a-company"> 1136 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1137 <span>What To Name A Company</span> 1138 </a> 1139 </li> 1140 <li> 1141 <a href="/what-to-name-my-business"> 1142 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg> 1143 <span>What To Name My Business</span> 1144 </a> 1145 </li> 1146 </ul> 1147 <button type="button" class="footer-landing__more" data-footer-landing-more aria-expanded="false" hidden> 1148 <span class="footer-landing__more-show">Show all 17 naming guides</span> 1149 <span class="footer-landing__more-hide">Show fewer</span> 1150 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg> 1151 </button> 1152 </div> 1153 </div> 1154 1155
1155<script> 1156 (function () { 1157 var root = document.querySelector('.footer-landing'); 1158 if (!root || root.dataset.footerLandingInit) { return; } 1159 root.dataset.footerLandingInit = '1'; 1160 root.classList.add('footer-landing--js'); 1161 1162 var CAP = 184; 1163 var tabs = Array.prototype.slice.call(root.querySelectorAll('[data-footer-landing-tab]')); 1164 var panels = Array.prototype.slice.call(root.querySelectorAll('[data-footer-landing-panel]')); 1165 1166 function panelFor(id) { return root.querySelector('[data-footer-landing-panel="' + id + '"]'); } 1167 1168 function syncMore(panel) { 1169 if (!panel) { return; } 1170 var links = panel.querySelector('.footer-landing__links'); 1171 var more = panel.querySelector('[data-footer-landing-more]'); 1172 if (!links || !more) { return; } 1173 panel.classList.remove('is-clamped'); 1174 var overflowing = links.scrollHeight > CAP + 8; 1175 if (overflowing && !panel.classList.contains('is-expanded')) { 1176 panel.classList.add('is-clamped'); 1177 } 1178 more.hidden = !overflowing; 1179 } 1180 1181 function activate(id, focusTab) {
1182 tabs.forEach(function (t) { 1183 var on = t.dataset.footerLandingTab === id; 1184 t.classList.toggle('is-active', on); 1185 t.setAttribute('aria-selected', on ? 'true' : 'false'); 1186 t.setAttribute('tabindex', on ? '0' : '-1'); 1187 if (on && focusTab) { t.focus(); } 1188 }); 1189 panels.forEach(function (p) { 1190 p.classList.toggle('is-active', p.dataset.footerLandingPanel === id); 1191 }); 1192 syncMore(panelFor(id)); 1193 } 1194 1195 tabs.forEach(function (tab) { 1196 tab.addEventListener('click', function () { activate(tab.dataset.footerLandingTab); }); 1197 tab.addEventListener('keydown', function (e) { 1198 var i = tabs.indexOf(tab); 1199 if (e.key === 'ArrowRight' || e.key === 'ArrowDown') { 1200 e.preventDefault(); 1201 activate(tabs[(i + 1) % tabs.length].dataset.footerLandingTab, true); 1202 } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') { 1203 e.preventDefault(); 1204 activate(tabs[(i - 1 + tabs.length) % tabs.length].dataset.footerLandingTab, true); 1205 } 1206 }); 1207 }); 1208 1209 panels.forEach(function (panel) { 1210 var more = panel.querySelector('[data-footer-landing-more]'); 1211 if (!more) { return; } 1212 more.addEventListener('click', function () { 1213 var expanded = panel.classList.toggle('is-expanded'); 1214 more.setAttribute('aria-expanded', expanded ? 'true' : 'false'); 1215 if (expanded) { panel.classList.remove('is-clamped'); } 1216 else { syncMore(panel); } 1217 }); 1218 }); 1219 1220 syncMore(root.querySelector('.footer-landing__panel.is-active')); 1221 var resizeTimer; 1222 window.addEventListener('resize', function () { 1223 window.clearTimeout(resizeTimer); 1224 resizeTimer = window.setTimeout(function () { 1225 var active = root.querySelector('.footer-landing__panel.is-active'); 1226 if (active && !active.classList.contains('is-expanded')) { syncMore(active); } 1227 }, 150); 1228 }); 1229 })(); 1230 </script>
1230 1231 </section> 1232 1233 <div class="footer-v2__columns"> 1234 <div class="footer-v2__col"> 1235 <h4>Products & Pricing</h4> 1236 <ul> 1237 <li><a href="/business-names">Domain Names</a></li> 1238 </ul> 1239 </div> 1240 1241 <div class="footer-v2__col"> 1242 <h4>Learn</h4> 1243 <ul> 1244 <li><a href="/guides">Naming Guides</a></li> 1245 </ul> 1246 </div> 1247 1248 <div class="footer-v2__col"> 1249 <h4>What is NameOffice?</h4> 1250 <ul> 1251 <li><a href="/about">About Us</a></li> 1252 <li><a href="/faq">FAQ</a></li> 1253 <li><a href="/contact">Contact</a></li> 1254 </ul> 1255 </div> 1256 1257 <div class="footer-v2__col"> 1258 <h4>Follow Us</h4> 1259 <ul> 1260 <li><a href="mailto:[email protected]" target="_blank" rel="noopener noreferrer">Email</a></li> 1261 <li><a href="https://x.com/nameOffice" target="_blank" rel="noopener noreferrer">Twitter / X</a></li> 1262 <li><a href="https://www.linkedin.com/company/nameoffice" target="_blank" rel="noopener noreferrer">LinkedIn</a></li> 1263 <li><a href="https://www.facebook.com/nameoffices" target="_blank" rel="noopener noreferrer">Facebook</a></li> 1264 </ul> 1265 </div> 1266 </div> 1267 1268 <div class="footer-v2__bottom"> 1269 <span>© 2026 NameOffice LLC. All rights reserved.</span> 1270 <span class="footer-v2__legal"> 1271 <a href="/terms">Terms</a> 1272 <a href="/privacy-policy">Privacy</a> 1273 <a href="/cookie-policy">Cookie Policy</a> 1274 <button type="button" data-cc-action="open-settings">Cookie settings</button> 1275 </span> 1276 </div> 1277 </div> 1278</footer> 1279 1280 <div id="cookie-consent" class="cookie-consent" hidden role="region" aria-label="Cookie consent"> 1281 <div class="cookie-consent__inner"> 1282 <div class="cookie-consent__text"> 1283 <strong>We value your privacy</strong> 1284 <p> 1285 We use cookies to run the site, remember your cart and saved names, and understand how the 1286 site is used. Nothing beyond the essentials runs until you choose. You can accept all, 1287 reject non-essential cookies, or pick what to allow. 1288 Read our <a href="/cookie-policy">Cookie Policy</a>. 1289 </p> 1290 </div> 1291 <div class="cookie-consent__actions"> 1292 <button type="button" class="cookie-btn cookie-btn--ghost" data-cc-action="customize">Customize</button> 1293 <button type="button" class="cookie-btn cookie-btn--outline" data-cc-action="reject">Reject all</button> 1294 <button type="button" class="cookie-btn cookie-btn--mint" data-cc-action="accept">Accept all</button> 1295 </div> 1296 </div> 1297</div> 1298 1299<div id="cookie-prefs" class="cookie-prefs" hidden role="dialog" aria-modal="true" aria-labelledby="cookie-prefs-title"> 1300 <div class="cookie-prefs__backdrop" data-cc-action="close-prefs"></div> 1301 <div class="cookie-prefs__panel" role="document"> 1302 <div class="cookie-prefs__head"> 1303 <h2 id="cookie-prefs-title">Cookie preferences</h2> 1304 <button type="button" class="cookie-prefs__close" data-cc-action="close-prefs" aria-label="Close">×</button> 1305 </div> 1306 <p class="cookie-prefs__intro"> 1307 Choose which cookies NameOffice can use. Strictly necessary cookies are always on because the site 1308 cannot work without them. You can change your choice anytime from the “Cookie settings” 1309 link in the footer. 1310 </p> 1311 1312 <div class="cookie-prefs__group"> 1313 <div class="cookie-prefs__group-head"> 1314 <span class="cookie-prefs__group-title">Strictly necessary</span>
1315 <span class="cookie-prefs__always">Always on</span> 1316 </div> 1317 <p>Required for core features such as your cart, saved names, and secure checkout. These do not track you for advertising.</p> 1318 </div> 1319 1320 <div class="cookie-prefs__group"> 1321 <div class="cookie-prefs__group-head"> 1322 <label class="cookie-prefs__group-title" for="cc-analytics">Analytics</label> 1323 <label class="cookie-switch"> 1324 <input type="checkbox" id="cc-analytics"> 1325 <span class="cookie-switch__slider"></span> 1326 </label> 1327 </div> 1328 <p>Help us understand which names and pages are popular so we can improve the site. Set only with your consent.</p> 1329 </div> 1330 1331 <div class="cookie-prefs__group"> 1332 <div class="cookie-prefs__group-head"> 1333 <label class="cookie-prefs__group-title" for="cc-marketing">Marketing</label> 1334 <label class="cookie-switch"> 1335 <input type="checkbox" id="cc-marketing"> 1336 <span class="cookie-switch__slider"></span> 1337 </label> 1338 </div> 1339 <p>Let us show you the names you looked at in ads on other sites, and measure whether those ads work. Set only with your consent.</p> 1340 </div> 1341 1342 <div class="cookie-prefs__foot"> 1343 <button type="button" class="cookie-btn cookie-btn--outline" data-cc-action="reject">Reject all</button> 1344 <button type="button" class="cookie-btn cookie-btn--mint" data-cc-action="save">Save preferences</button> 1345 </div> 1346 </div> 1347</div> 1348
1349<script> 1350(function(){ 1351 /* Set server side from Cloudflare's country header. Inside the EU, EEA, 1352 UK and Switzerland nothing non-essential may run before someone agrees. 1353 Elsewhere it runs on arrival and they may decline. */ 1354 var REQUIRE_OPTIN = true; 1355 var COOKIE_NAME = 'cookie_consent'; 1356 var PROMPT_NAME = 'cookie_consent_prompted'; 1357 var VERSION = 1; 1358 var MAX_AGE = 60 * 60 * 24 * 365; // 1 year 1359 var REVEAL_DELAY_MS = 4000; 1360 var ANIMATION_MS = 1050; 1361 var bannerTimer = null; 1362 1363 /* The visitor's stored choice, or null if they have not made one. */ 1364 function storedConsent(){ 1365 var m = document.cookie.match('(^|;)\\s*' + COOKIE_NAME + '\\s*=\\s*([^;]+)'); 1366 if (!m) return null; 1367 try { 1368 var c = JSON.parse(decodeURIComponent(m[2])); 1369 return (c && c.v === VERSION) ? c : null; 1370 } catch (e) { return null; } 1371 } 1372 1373 /* What applies right now. Where prior opt-in is not required, no stored 1374 choice means consent is implied until they say otherwise, and the 1375 implied flag marks it so nothing mistakes it for a decision they made. */ 1376 function readConsent(){ 1377 var stored = storedConsent(); 1378 if (stored) return stored; 1379 if (REQUIRE_OPTIN) return null; 1380 1381 return { v: VERSION, necessary: true, analytics: true, marketing: true, implied: true }; 1382 } 1383 1384 function hasPrompted(){ 1385 return document.cookie.indexOf(PROMPT_NAME + '=1') !== -1; 1386 } 1387 1388 function markPrompted(){ 1389 var secure = location.protocol === 'https:' ? ';Secure' : ''; 1390 document.cookie = PROMPT_NAME + '=1;Max-Age=' + MAX_AGE + ';Path=/;SameSite=Lax' + secure; 1391 } 1392 1393 function writeConsent(consent){ 1394 consent.v = VERSION; 1395 consent.ts = Date.now(); 1396 consent.necessary = true; 1397 var secure = location.protocol === 'https:' ? ';Secure' : ''; 1398 document.cookie = COOKIE_NAME + '=' + encodeURIComponent(JSON.stringify(consent)) + 1399 ';Max-Age=' + MAX_AGE + ';Path=/;SameSite=Lax' + secure; 1400 window.cookieConsent = consent; 1401 // Other scripts (e.g. analytics, added later) can listen for this. 1402 window.dispatchEvent(new CustomEvent('cookieconsent:changed', { detail: consent })); 1403 } 1404 1405 var banner = document.getElementById('cookie-consent'); 1406 var prefs = document.getElementById('cookie-prefs'); 1407 var analyticsToggle = document.getElementById('cc-analytics'); 1408 var marketingToggle = document.getElementById('cc-marketing'); 1409 1410 function hideBanner(){ 1411 if (!banner) return; 1412 if (bannerTimer) { 1413 window.clearTimeout(bannerTimer); 1414 bannerTimer = null; 1415 } 1416 banner.classList.remove('is-in'); // slide down 1417 setTimeout(function(){ banner.hidden = true; }, ANIMATION_MS); 1418 } 1419 function showBanner(){ 1420 bannerTimer = null; 1421 if (!banner || storedConsent() || hasPrompted()) return; 1422 // Record that we've prompted this visitor so the banner only ever 1423 // appears once per user, even if they ignore it. 1424 markPrompted(); 1425 banner.hidden = false; // in layout but translated off-screen via CSS 1426 // Two frames guarantee the off-screen state paints before the slow 1427 // upward transition starts. 1428 window.requestAnimationFrame(function () { 1429 window.requestAnimationFrame(function () { 1430 banner.classList.add('is-in'); 1431 }); 1432 }); 1433 } 1434 // Let visitors see and begin using the page before asking for consent. 1435 function scheduleBanner(){ 1436 var go = function(){ bannerTimer = window.setTimeout(showBanner, REVEAL_DELAY_MS); }; 1437 if (document.readyState === 'complete') go(); 1438 else window.addEventListener('load', go, { once: true }); 1439 } 1440 function openPrefs(){ 1441 var c = readConsent(); 1442 if (analyticsToggle) analyticsToggle.checked = c ? !!c.analytics : false; 1443 if (marketingToggle) marketingToggle.checked = c ? !!c.marketing : false; 1444 if (prefs) { prefs.hidden = false; document.body.style.overflow = 'hidden'; } 1445 } 1446 function closePrefs(){ if (prefs) { prefs.hidden = true; document.body.style.overflow = ''; } } 1447 1448 function acceptAll(){ writeConsent({ analytics: true, marketing: true }); hideBanner(); closePrefs(); }
1449 function rejectAll(){ writeConsent({ analytics: false, marketing: false }); hideBanner(); closePrefs(); } 1450 function savePrefs(){ 1451 writeConsent({ 1452 analytics: analyticsToggle ? !!analyticsToggle.checked : false, 1453 marketing: marketingToggle ? !!marketingToggle.checked : false 1454 }); 1455 hideBanner(); closePrefs(); 1456 } 1457 1458 document.addEventListener('click', function(e){ 1459 var el = e.target.closest('[data-cc-action]'); 1460 if (!el) return; 1461 var a = el.getAttribute('data-cc-action'); 1462 if (a === 'accept') acceptAll(); 1463 else if (a === 'reject') rejectAll(); 1464 else if (a === 'customize') openPrefs(); 1465 else if (a === 'save') savePrefs(); 1466 else if (a === 'close-prefs') closePrefs(); 1467 else if (a === 'open-settings') { e.preventDefault(); openPrefs(); } 1468 }); 1469 document.addEventListener('keydown', function(e){ if (e.key === 'Escape') closePrefs(); }); 1470 1471 // Expose current consent so other scripts can read it. 1472 window.cookieConsent = readConsent(); 1473 1474 // Show once per visitor: only if they have neither made a choice nor been 1475 // prompted before. Animates up after the page has loaded. 1476 if (!storedConsent() && !hasPrompted()) scheduleBanner(); 1477})(); 1478</script>
1478 1479 1480 <div id="mobileMenu" class="fixed left-0 right-0 z-50 hidden border-b border-white/10 bg-navy-deep md:hidden"> 1481 <nav class="mx-auto flex max-w-7xl flex-col gap-1 px-5 py-3 text-sm text-white/85 md:px-8"> 1482 <a class="rounded-lg px-2 py-2.5 transition hover:bg-white/5" href="/business-names">Browse</a> 1483 <a class="rounded-lg px-2 py-2.5 transition hover:bg-white/5" href="/favorites">Favorites</a> 1484 <a class="rounded-lg px-2 py-2.5 transition hover:bg-white/5" href="/cart">Cart</a> 1485 <a class="rounded-lg px-2 py-2.5 transition hover:bg-white/5" href="/guides">Guides</a> 1486 <a class="rounded-lg px-2 py-2.5 transition hover:bg-white/5" href="/reviews">Reviews</a> 1487 <a class="rounded-lg px-2 py-2.5 transition hover:bg-white/5" href="/contact">Contact</a> 1488 </nav> 1489</div>
1490<script> 1491 (function () { 1492 var btn = document.getElementById('mobileMenuBtn'); 1493 var menu = document.getElementById('mobileMenu'); 1494 if (!btn || !menu) return; 1495 var hdr = document.querySelector('header'); 1496 btn.addEventListener('click', function () { 1497 if (menu.classList.contains('hidden')) { 1498 if (hdr) menu.style.top = hdr.getBoundingClientRect().bottom + 'px'; 1499 menu.classList.remove('hidden'); 1500 } else { 1501 menu.classList.add('hidden'); 1502 } 1503 }); 1504 })(); 1505</script>
1505 1506 1507 1508
1508<script> 1509 (function () { 1510 function count(name, decode) { 1511 try { 1512 var m = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;]*)')); 1513 if (!m) return 0; 1514 var v = decode ? decodeURIComponent(m[1]) : m[1]; 1515 var a = JSON.parse(v); 1516 return Array.isArray(a) ? a.length : 0; 1517 } catch (e) { return 0; } 1518 } 1519 function setBadge(id, n) { 1520 var b = document.getElementById(id); 1521 if (!b) return; 1522 if (n > 0) { b.textContent = n; b.classList.remove('topbar-v2__badge--hidden'); } 1523 else { b.classList.add('topbar-v2__badge--hidden'); } 1524 } 1525 window.refreshCartBadge = function () { setBadge('v2-cart-badge', count('cartDomains', true)); }; 1526 window.refreshFavBadge = function () { setBadge('v2-fav-badge', count('shortlistedDomains', true)); }; 1527 function run(){ window.refreshCartBadge(); window.refreshFavBadge(); } 1528 if (document.readyState !== 'loading') run(); 1529 else document.addEventListener('DOMContentLoaded', run); 1530 })(); 1531</script>
1531 1532 1533 1534 <style> 1535 .dcard .reveal-top { transform: translateY(-100%); transition: transform .5s ease; } 1536 .dcard .reveal-bottom { transform: translateY(100%); transition: transform .5s ease; } 1537 .dcard .logo-wrap { transition: all .5s ease; } 1538 1539 /* Card header: name + category tag, with the favorite button overlapping the 1540 top-right corner. The tag used to be shown/hidden on viewport width, but the 1541 grid runs 2 -> 3 -> 4 columns, so card width does not track viewport: cards at 1542 1024px (4 cols) are narrower than at 1023px (3 cols). The tag could not shrink, 1543 so the row overflowed its reserved right padding and slid under the heart. 1544 Size the tag off the card itself instead. */ 1545 [data-domain-card] { container-type: inline-size; container-name: dcard; } 1546 .dcard-head { min-width: 0; } 1547 .dcard-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } 1548 .dcard-tag { 1549 display: none; /* shown only when the card is wide enough (below) */ 1550 flex: none; /* never let it squeeze the name */ 1551 max-width: 60%; 1552 overflow: hidden; 1553 text-overflow: ellipsis; 1554 white-space: nowrap; 1555 } 1556 /* name (~84px) + gap (12px) + tag (~86px) needs ~182px of content box, and the 1557 content box is cardWidth - 20 (left pad) - 64 (right pad reserved for the heart). */ 1558 @container dcard (min-width: 268px) { 1559 .dcard-tag { display: inline-flex; } 1560 } 1561 1562 @media (min-width: 1024px) and (hover: hover) and (pointer: fine) { 1563 .dcard:hover .reveal-top { transform: translateY(0); } 1564 .dcard:hover .reveal-bottom { transform: translateY(0); } 1565 .dcard:hover .logo-wrap { top: 60px !important; bottom: 184px !important; } 1566 .dcard:hover { box-shadow: 0 40px 80px -15px rgba(1,16,46,0.18); } 1567 } 1568 1569 /* Bootstrap compatibility: on pages that also load Bootstrap (base_v2), 1570 Bootstrap's !important spacing/position utilities (.p-5, .mb-4, .py-3, 1571 .top-0/.bottom-0) clobber the card's Tailwind utilities, inflating the 1572 panels and pinning .logo-wrap so it can't reveal the logo on hover. 1573 Re-assert the intended Tailwind values. No-op where Bootstrap is absent. */ 1574 .dcard .reveal-top { padding: 1.25rem !important; padding-right: 4rem !important; } 1575 .dcard .reveal-bottom { padding: 1.25rem !important; } 1576 .dcard .reveal-bottom .line-clamp-2 { margin-bottom: 1rem !important; } 1577 .dcard [data-buy] { padding-top: 0.75rem !important; padding-bottom: 0.75rem !important; } 1578 .dcard { border-color: rgba(0,0,0,0.05) !important; } 1579 1580 /* Below desktop: no hover, cards are expanded by default so all info shows. 1581 Use position:relative (not static) so the panels' z-index is honored and 1582 they stay above the full-bleed background image on logo-less cards. */ 1583 @media (max-width: 1023px) { 1584 .dcard { aspect-ratio: auto !important; } 1585 .dcard .reveal-top, 1586 .dcard .reveal-bottom { position: relative !important; transform: none !important; } 1587 .dcard .logo-wrap { position: relative !important; inset: auto !important; top: auto !important; bottom: auto !important; min-height: 150px; padding: 1.25rem 0 !important; } 1588 .dcard:hover .logo-wrap { top: auto !important; bottom: auto !important; } 1589 } 1590</style> 1591
1592<script> 1593window.NameOfficeGrid = (function () { 1594 function esc(s){ return (s||'').replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); } 1595 function money(n){ return '$' + (n||0).toLocaleString(); } 1596 1597 function getFavs(){ return window.NameOfficeSelectionState ? window.NameOfficeSelectionState.read('shortlistedDomains') : []; } 1598 function setFavs(a){ return window.NameOfficeSelectionState ? window.NameOfficeSelectionState.write('shortlistedDomains', a) : []; } 1599 function getCart(){ return window.NameOfficeSelectionState ? window.NameOfficeSelectionState.read('cartDomains') : []; } 1600 function setCart(a){ return window.NameOfficeSelectionState ? window.NameOfficeSelectionState.write('cartDomains', a) : []; } 1601 function trackDomain(id, type, action){ 1602 if (window.NameOfficeAnalytics && window.NameOfficeAnalytics.trackDomainInteraction) { 1603 return window.NameOfficeAnalytics.trackDomainInteraction(id, type, action); 1604 } 1605 return Promise.resolve(null); 1606 } 1607 1608 function refreshCartBadge(){ if (window.refreshCartBadge) window.refreshCartBadge(); } 1609 1610 function buyNow(id){ const c=getCart(); if(!c.includes(id)) c.push(id); setCart(c); trackDomain(id, 'cart', 'add'); window.location.href='/cart'; } 1611 1612 // GA4 add_to_cart. Reads name/price off the card so it works for both the 1613 // server-rendered cards and the ones this file builds during infinite scroll. 1614 function gaAddToCart(id, cardEl){ 1615 if (typeof gtag !== 'function') return; 1616 let name = '', price = 0; 1617 if (cardEl){ 1618 const nameEl = cardEl.querySelector('.dcard-name'); 1619 if (nameEl) name = nameEl.textContent.trim(); 1620 const priceEl = cardEl.querySelector('.reveal-bottom span'); 1621 if (priceEl) price = Number(String(priceEl.textContent).replace(/[^0-9.]/g, '')) || 0; 1622 } 1623 gtag('event', 'add_to_cart', { 1624 currency: 'USD', 1625 value: price, 1626 items: [{ item_id: name || String(id), item_name: name || String(id), price: price, quantity: 1 }] 1627 }); 1628 } 1629 1630 // Add to cart WITHOUT leaving the page (used by the gallery's lean-back mode). 1631 function addToCart(id){ const c=getCart(); const had=c.includes(id); if(!had) c.push(id); setCart(c); if (!had) trackDomain(id, 'cart', 'add'); if (window.refreshCartBadge) window.refreshCartBadge(); return !had; } 1632 1633 // Re-paint every visible card heart from the shortlist cookie. Lets changes 1634 // made inside the gallery show up on the grid behind it. 1635 function syncFavButtons(){ 1636 const favs = getFavs(); 1637 document.querySelectorAll('[data-fav]').forEach(btn => { 1638 const on = favs.includes(parseInt(btn.dataset.fav)); 1639 btn.classList.toggle('text-rose-500', on); 1640 btn.classList.toggle('text-black/30', !on); 1641 const s = btn.querySelector('svg'); if (s) s.setAttribute('fill', on ? 'currentColor' : 'none'); 1642 }); 1643 } 1644 function toggleFav(id, btn){ 1645 let f=getFavs(); 1646 const removing=f.includes(id); 1647 if(removing){ 1648 f=f.filter(x=>x!==id); 1649 if(btn){ 1650 btn.classList.remove('text-rose-500'); 1651 const s=btn.querySelector('svg'); if(s) s.setAttribute('fill','none'); 1652 } 1653 trackDomain(id, 'favorite', 'remove'); 1654 } else { 1655 f.push(id); 1656 if(btn){ 1657 btn.classList.add('text-rose-500'); 1658 const s=btn.querySelector('svg'); if(s) s.setAttribute('fill','currentColor'); 1659 } 1660 trackDomain(id, 'favorite', 'add'); 1661 } 1662 setFavs(f); 1663 if (!btn) syncFavButtons(); 1664 if (window.refreshFavBadge) window.refreshFavBadge(); 1665 return !removing; 1666 } 1667 1668 function cardHTML(d){ 1669 const fav = getFavs().includes(d.id); 1670 const desc = d.shortDescription ? esc(d.shortDescription) : ''; 1671 const hasLogo = !!d.logoPath; 1672 const nameNoTld = d.rootName || d.formattedName || ''; 1673 1674 // Center area: a real logo on white, or the category image filling the 1675 // card with the domain name overlaid in the middle. 1676 const centerInner = hasLogo 1677 ? `<img src="/${esc(d.logoPath)}" alt="${esc(d.imageSeoTitle || d.formattedName)}" title="${esc(d.imageSeoTitle || d.formattedName)}" loading="lazy" class="h-auto w-[80%] max-w-[240px] rounded-lg object-contain">` 1678 : `<span class="px-3 text-center text-2xl font-bold tracking-tight text-white" style="text-shadow:0 2px 12px rgba(0,0,0,0.55)">${esc(nameNoTld)}</span>`;
1679 1680 // Full-bleed category background + scrim, only when there's no logo. 1681 const bgLayer = hasLogo ? '' : ` 1682 <div class="absolute inset-0 z-0"> 1683 <img src="${esc(d.categoryImagePath || '/categoryImages/default.jpg')}" alt="" loading="lazy" class="h-full w-full object-cover"> 1684 <div class="absolute inset-0 bg-gradient-to-t from-black/70 via-black/25 to-black/40"></div> 1685 </div>`; 1686 1687 return ` 1688 <article data-domain-card class="dcard group relative aspect-[3/4] overflow-hidden rounded-2xl border border-black/5 bg-white transition-all duration-700"> 1689 ${bgLayer} 1690 <button type="button" data-fav="${d.id}" aria-label="Save favorite" class="absolute right-4 top-4 z-30 grid h-9 w-9 place-items-center rounded-full bg-white/80 backdrop-blur transition-colors hover:text-rose-500 ${fav?'text-rose-500':'text-black/30'}"> 1691 <svg width="16" height="16" viewBox="0 0 24 24" fill="${fav?'currentColor':'none'}" stroke="currentColor" stroke-width="2"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg> 1692 </button> 1693 <div class="reveal-top absolute left-0 right-0 top-0 z-20 border-b border-black/5 bg-white p-5 pr-16"> 1694 <div class="dcard-head flex items-center justify-between gap-3"> 1695 <a href="${esc(d.path)}" class="dcard-name text-[10px] font-semibold uppercase tracking-[0.2em] text-black/50 underline-offset-2 hover:underline">${esc(d.formattedName)}</a> 1696 <span class="dcard-tag rounded-full bg-mint/20 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider text-navy">${esc(d.categoryName||'Premium')}</span> 1697 </div> 1698 </div> 1699 <div class="logo-wrap pointer-events-none absolute inset-x-0 top-0 bottom-0 z-10 flex items-center justify-center px-4"> 1700 <a href="${esc(d.path)}" class="pointer-events-auto flex w-full items-center justify-center"> 1701 ${centerInner} 1702 </a> 1703 </div> 1704 <div class="reveal-bottom absolute bottom-0 left-0 right-0 z-20 border-t border-black/5 bg-white p-5"> 1705 <div class="mb-2 flex items-center justify-between"> 1706 <button type="button" data-gallery="${d.id}" aria-label="Play this name in the gallery" class="inline-flex items-center gap-1.5 text-[11px] font-semibold text-black/60 transition-colors hover:text-emerald-600"> 1707 <svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><path d="M6 4l14 8-14 8z"/></svg> 1708 Gallery 1709 </button> 1710 <button type="button" data-inquire="${esc(d.name)}" data-inquire-name="${esc(d.formattedName)}" class="inline-flex items-center gap-0.5 text-[11px] font-semibold text-black/60 transition-colors hover:text-navy hover:underline"> 1711 Inquire
1712 <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M7 17 17 7M7 7h10v10"/></svg> 1713 </button> 1714 </div> 1715 <p class="mb-4 line-clamp-2 text-xs leading-relaxed text-black/60">${desc}</p> 1716 <div class="flex items-stretch justify-between gap-2"> 1717 <span class="flex items-center px-1 text-lg font-bold tracking-tight">${money(d.price)}</span> 1718 <button type="button" data-buy="${d.id}" class="inline-flex shrink-0 items-center justify-center whitespace-nowrap rounded-md bg-navy px-3 py-3 text-[10px] font-bold uppercase tracking-[0.12em] text-white transition-colors hover:bg-[#011845] sm:px-4 sm:text-[11px] sm:tracking-[0.15em]">Buy Now</button> 1719 </div> 1720 </div> 1721 </article>`; 1722 } 1723 1724 // Switchable vibe facets (values mirror the backend enums). 1725 // Single-select "character" facets, shown as dropdowns. 1726 const CHARACTER_FACETS = [ 1727 { key: 'soundWeight', label: 'Sound', options: ['hard','balanced','soft'] }, 1728 { key: 'soundBrightness', label: 'Tone', options: ['bright','neutral','dark'] }, 1729 { key: 'soundEnergy', label: 'Energy', options: ['punchy','steady','flowing'] }, 1730 { key: 'premiumFeel', label: 'Feel', options: ['everyday','professional','premium','luxury'] }, 1731 { key: 'formality', label: 'Style', options: ['playful','neutral','professional'] }, 1732 ]; 1733 const PERSONALITY_OPTIONS = ['innovative','trustworthy','bold','friendly','premium','playful','minimal','technical','human','authoritative']; 1734 const NAME_TYPES = [ 1735 { value: 'abstract', label: 'Abstract' }, 1736 { value: 'evocative', label: 'Evocative' }, 1737 { value: 'lexical', label: 'Lexical' }, 1738 ]; 1739 const QUICK_TLDS = [ 1740 { value: '', label: 'All' }, 1741 { value: 'com', label: '.com' }, 1742 { value: 'ai', label: '.ai' }, 1743 ]; 1744 const cap = function(s){ return s ? s.charAt(0).toUpperCase() + s.slice(1) : s; }; 1745 1746 function create(opts){ 1747 const cfg = Object.assign({ 1748 apiUrl: '/names-api', limit: 24, countLabel: 'curated identities', 1749 grid: 'grid', sentinel: 'sentinel', loadingRow: 'loadingRow', emptyRow: 'emptyRow', countEl: 'resultCount', filterEl: 'vibeFilter', 1750 }, opts || {}); 1751 1752 const grid = document.getElementById(cfg.grid); 1753 const sentinel = document.getElementById(cfg.sentinel); 1754 const loadingRow = document.getElementById(cfg.loadingRow); 1755 const emptyRow = document.getElementById(cfg.emptyRow); 1756 const countEl = cfg.countEl ? document.getElementById(cfg.countEl) : null; 1757 const filterEl = cfg.filterEl ? document.getElementById(cfg.filterEl) : null; 1758 function formatCount(total){ return Number(total || 0).toLocaleString() + (cfg.countLabel ? ' ' + cfg.countLabel : ''); } 1759 function renderedDomainCount(){ 1760 return grid ? grid.querySelectorAll('[data-domain-card]').length : 0; 1761 } 1762 1763 const hasInitialItems = Number.isFinite(cfg.initialPage) && cfg.initialPage > 0; 1764 const initialPage = hasInitialItems ? cfg.initialPage : 0; 1765 const initialTotal = Number.isFinite(cfg.initialTotal) ? cfg.initialTotal : 0; 1766 const initialRenderedCount = Number.isFinite(cfg.initialRenderedCount) ? cfg.initialRenderedCount : 0; 1767 const state = { 1768 page: hasInitialItems ? initialPage + 1 : 1, 1769 params: cfg.params || {}, 1770 loading: false, 1771 reloadPending: false, 1772 done: hasInitialItems ? initialRenderedCount >= initialTotal : false, 1773 total: hasInitialItems ? initialTotal : 0, 1774 // Live list of every loaded domain object, in grid order. Seeded with 1775 // the server-rendered initial items so the gallery has data before any 1776 // API call. Kept in sync as infinite scroll loads more. 1777 items: Array.isArray(cfg.initialItems) ? cfg.initialItems.slice() : [], 1778 overrides: cfg.initialOverrides ? Object.assign({}, cfg.initialOverrides) : {}, 1779 vibe: cfg.vibe || null, 1780 abstractness: Array.isArray(cfg.initialAbstractness) && cfg.initialAbstractness.length 1781 ? cfg.initialAbstractness.slice() 1782 : NAME_TYPES.map(function(t){ return t.value; }), // all name types shown by default 1783 tld: typeof cfg.initialTld === 'string' && cfg.initialTld ? cfg.initialTld.toLowerCase() : null, 1784 filterExpanded: false, 1785 }; 1786 const itemsListeners = [];
1787 function emitItemsChanged(){ itemsListeners.forEach(fn => { try { fn(state.items, state.done); } catch(e){} }); } 1788 1789 if (countEl && hasInitialItems) { 1790 countEl.textContent = formatCount(state.total); 1791 } 1792 if (emptyRow && hasInitialItems && initialRenderedCount === 0) { 1793 emptyRow.classList.remove('hidden'); 1794 } 1795 1796 function buildUrl(){ 1797 let u = cfg.apiUrl + (cfg.apiUrl.includes('?') ? '&' : '?') + 'page=' + state.page + '&limit=' + cfg.limit; 1798 if (state.params.term) u += '&term=' + encodeURIComponent(state.params.term); 1799 (state.params.types || []).forEach(t => { u += '&type[]=' + encodeURIComponent(t); }); 1800 if (state.tld) u += '&tld=' + encodeURIComponent(state.tld); 1801 Object.keys(state.overrides).forEach(function(key){ 1802 const val = state.overrides[key]; 1803 if (key === 'personality'){ (val || []).forEach(function(x){ u += '&personality[]=' + encodeURIComponent(x); }); } 1804 else if (val){ u += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(val); } 1805 }); 1806 if (state.abstractness && state.abstractness.length && state.abstractness.length < NAME_TYPES.length){ 1807 state.abstractness.forEach(function(v){ u += '&abstractness[]=' + encodeURIComponent(v); }); 1808 } 1809 return u; 1810 } 1811 1812 // ---- Refine filter ---- 1813 function currentFacet(key){ 1814 if (state.overrides[key] != null) return state.overrides[key]; 1815 return state.vibe ? state.vibe[key] : null; 1816 } 1817 function personalityActive(opt){ 1818 const p = (state.overrides.personality && state.overrides.personality.length) 1819 ? state.overrides.personality 1820 : (state.vibe && state.vibe.personality ? state.vibe.personality : []); 1821 return p.indexOf(opt) >= 0; 1822 } 1823 1824 function renderFilter(){ 1825 if (!filterEl) return; 1826 const v = state.vibe; 1827 filterEl.classList.remove('hidden'); 1828 const isCustom = !!(v && v.source === 'custom'); 1829 const hasManualState = Object.keys(state.overrides).length > 0 || state.abstractness.length < NAME_TYPES.length || !!state.tld; 1830 const canReset = isCustom || hasManualState; 1831 const hasVibe = !!(v && (v.soundWeight || v.soundBrightness || v.soundEnergy || v.premiumFeel || v.formality || (v.personality && v.personality.length))); 1832 const isExpanded = !!state.filterExpanded; 1833 const chevron = isExpanded 1834 ? '<svg width="12" height="12" viewBox="0 0 20 20" fill="none" aria-hidden="true" class="shrink-0"><path d="M5 12.5 10 7.5l5 5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>' 1835 : '<svg width="12" height="12" viewBox="0 0 20 20" fill="none" aria-hidden="true" class="shrink-0"><path d="M5 7.5 10 12.5l5-5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'; 1836 1837 let h = '<div class="rounded-[26px] border border-[#d7e5f6] bg-[linear-gradient(180deg,rgba(255,255,255,0.94)_0%,rgba(244,249,255,0.98)_100%)] p-4 shadow-[0_18px_38px_rgba(7,23,51,0.06)] sm:p-5">'; 1838 1839 h += '<div class="' + (isExpanded ? 'mb-4 border-b border-[#dbe6f3] pb-4 ' : '') + 'flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">'; 1840 h += '<div class="flex flex-wrap items-center gap-2">'; 1841 h += '<button type="button" data-refine-toggle aria-expanded="' + (isExpanded ? 'true' : 'false') + '" class="inline-flex items-center gap-2 rounded-full bg-navy px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-white shadow-[0_10px_24px_rgba(1,16,46,0.18)] transition hover:bg-[#011845]">Refine ' + chevron + '</button>'; 1842 if (state.params.term) h += '<span class="rounded-full border border-[#d7e5f6] bg-white/90 px-3 py-1 text-xs font-medium text-[#62708a]">' + (hasVibe ? 'Suggested for ' : 'Search term ') + '“' + esc(state.params.termLabel || state.params.term) + '”</span>'; 1843 h += '</div>'; 1844 h += '<div class="flex flex-wrap items-center gap-2">'; 1845 h += '<span class="mr-0.5 text-[10px] font-semibold uppercase tracking-[0.16em] text-[#8b97ad]">Domain ending</span>'; 1846 QUICK_TLDS.forEach(function(option){
1847 const active = (state.tld || '') === option.value; 1848 const cls = active 1849 ? 'border-navy bg-navy text-white shadow-[0_8px_20px_rgba(1,16,46,0.16)]' 1850 : 'border-[#cbd8e8] bg-white text-[#52627d] hover:border-navy/35 hover:text-navy'; 1851 h += '<button type="button" data-tld-filter="' + option.value + '" aria-pressed="' + (active ? 'true' : 'false') + '" class="inline-flex min-h-9 items-center justify-center rounded-full border px-3.5 py-1.5 text-xs font-bold transition ' + cls + '">' + option.label + '</button>'; 1852 }); 1853 if (canReset) h += '<button type="button" data-vibe-reset class="ml-1 inline-flex min-h-9 items-center justify-center rounded-full border border-[#d1dcec] bg-white px-3 py-1.5 text-[11px] font-semibold uppercase tracking-[0.16em] text-[#62708a] transition hover:border-navy/20 hover:text-navy">Reset</button>'; 1854 h += '</div>'; 1855 h += '</div>'; 1856 1857 if (isExpanded) { 1858 h += '<div class="mb-2 text-[11px] font-semibold uppercase tracking-[0.18em] text-[#8b97ad]">Character</div>'; 1859 h += '<div class="mb-4 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-5">'; 1860 CHARACTER_FACETS.forEach(function(f){ 1861 const cur = currentFacet(f.key); 1862 h += '<label class="flex flex-col gap-1.5 rounded-[18px] border border-[#d7e5f6] bg-white/85 p-3 shadow-[inset_0_1px_0_rgba(255,255,255,0.92)]"><span class="text-[11px] font-semibold uppercase tracking-[0.14em] text-[#72819a]">' + f.label + '</span>'; 1863 h += '<select data-facet-select="' + f.key + '" class="rounded-xl border border-[#d7e5f6] bg-[#f8fbff] px-3 py-2 text-sm font-semibold text-navy focus:border-navy/40 focus:outline-none">'; 1864 h += '<option value=""' + (cur ? '' : ' selected') + '>Any</option>'; 1865 f.options.forEach(function(opt){ 1866 h += '<option value="' + opt + '"' + (cur === opt ? ' selected' : '') + '>' + cap(opt) + '</option>'; 1867 }); 1868 h += '</select></label>'; 1869 }); 1870 h += '</div>'; 1871 1872 h += '<div class="mb-2 text-[11px] font-semibold uppercase tracking-[0.18em] text-[#8b97ad]">Personality</div>'; 1873 h += '<div class="mb-4 flex flex-wrap gap-2">'; 1874 PERSONALITY_OPTIONS.forEach(function(opt){ 1875 const cls = personalityActive(opt) 1876 ? 'rounded-full bg-navy px-3 py-1.5 text-xs font-semibold text-white capitalize shadow-[0_10px_24px_rgba(1,16,46,0.18)]' 1877 : 'rounded-full border border-[#d1dcec] bg-white/90 px-3 py-1.5 text-xs font-semibold text-[#62708a] capitalize transition hover:border-navy/30 hover:text-navy'; 1878 h += '<button type="button" class="' + cls + '" data-facet="personality" data-opt="' + opt + '">' + opt + '</button>'; 1879 }); 1880 h += '</div>'; 1881 1882 h += '<div class="border-t border-[#dbe6f3] pt-4">'; 1883 h += '<div class="mb-2 text-[11px] font-semibold uppercase tracking-[0.18em] text-[#8b97ad]">Name type <span class="font-normal normal-case tracking-normal text-[#aab4c5]">· all shown by default</span></div>'; 1884 h += '<div class="flex flex-wrap gap-2">'; 1885 NAME_TYPES.forEach(function(t){ 1886 const on = state.abstractness.indexOf(t.value) >= 0; 1887 const cls = on 1888 ? 'inline-flex items-center gap-1 rounded-full bg-navy px-3 py-1.5 text-xs font-semibold text-white shadow-[0_10px_24px_rgba(1,16,46,0.18)]' 1889 : 'inline-flex items-center gap-1 rounded-full border border-[#d1dcec] bg-white/90 px-3 py-1.5 text-xs font-semibold text-[#62708a] transition hover:border-navy/30 hover:text-navy'; 1890 h += '<button type="button" class="' + cls + '" data-type-toggle="' + t.value + '">' + (on ? '<span aria-hidden="true">✓</span> ' : '') + t.label + '</button>'; 1891 }); 1892 h += '</div></div>'; 1893 } 1894 1895 h += '</div>'; 1896 filterEl.innerHTML = h; 1897 } 1898 1899 function toggleFilterExpanded(){ 1900 state.filterExpanded = !state.filterExpanded; 1901 renderFilter(); 1902 } 1903 function setFacetValue(key, value){ if (value) state.overrides[key] = value; else delete state.overrides[key]; load(true); } 1904 function togglePersonality(opt){ 1905 let base = (state.overrides.personality && state.overrides.personality.length) 1906 ? state.overrides.personality.slice() 1907 : ((state.vibe && state.vibe.personality) ? state.vibe.personality.slice() : []); 1908 const i = base.indexOf(opt); 1909 if (i >= 0) base.splice(i, 1); else base.push(opt); 1910 state.overrides.personality = base; 1911 load(true); 1912 } 1913 function setFacet(key, opt){ if (key === 'personality') togglePersonality(opt); else setFacetValue(key, opt); } 1914 function setTld(value){ state.tld = value || null; load(true); } 1915 function toggleType(value){ 1916 const i = state.abstractness.indexOf(value); 1917 if (i >= 0){ if (state.abstractness.length > 1) state.abstractness.splice(i, 1); } // keep at least one 1918 else state.abstractness.push(value); 1919 load(true); 1920 } 1921 function resetFacets(){ 1922 state.overrides = {}; 1923 state.abstractness = NAME_TYPES.map(function(t){ return t.value; }); 1924 state.tld = null; 1925 load(true); 1926 } 1927 1928 if (filterEl){ 1929 filterEl.addEventListener('change', function(e){ 1930 const sel = e.target.closest('select[data-facet-select]'); 1931 if (sel){ setFacetValue(sel.dataset.facetSelect, sel.value); } 1932 }); 1933 filterEl.addEventListener('click', function(e){ 1934 const toggle = e.target.closest('[data-refine-toggle]'); 1935 if (toggle){ e.preventDefault(); toggleFilterExpanded(); return; } 1936 const reset = e.target.closest('[data-vibe-reset]'); 1937 if (reset){ e.preventDefault(); resetFacets(); return; } 1938 const tld = e.target.closest('[data-tld-filter]'); 1939 if (tld){ e.preventDefault(); setTld(tld.dataset.tldFilter); return; } 1940 const type = e.target.closest('[data-type-toggle]'); 1941 if (type){ e.preventDefault(); toggleType(type.dataset.typeToggle); return; } 1942 const chip = e.target.closest('[data-facet="personality"]'); 1943 if (chip){ e.preventDefault(); togglePersonality(chip.dataset.opt); } 1944 }); 1945 } 1946 renderFilter(); 1947 1948 async function load(reset){ 1949 if (state.loading){ 1950 if (reset) state.reloadPending = true; 1951 return; 1952 } 1953 if (state.done && !reset) return; 1954 if (reset){ state.page = 1; state.done = false; grid.innerHTML = ''; state.items = []; emitItemsChanged(); } 1955 state.loading = true; 1956 if (loadingRow) loadingRow.classList.remove('hidden'); 1957 if (emptyRow) emptyRow.classList.add('hidden'); 1958 try { 1959 const res = await fetch(buildUrl()); 1960 const data = await res.json(); 1961 state.total = data.totalItemCount || 0; 1962 if (typeof data.vibe !== 'undefined') { state.vibe = data.vibe; renderFilter(); } 1963 if (countEl) countEl.textContent = formatCount(state.total); 1964 if (data.items && data.items.length){ 1965 grid.insertAdjacentHTML('beforeend', data.items.map(cardHTML).join('')); 1966 state.items.push.apply(state.items, data.items); 1967 state.page++; 1968 if (renderedDomainCount() >= state.total) state.done = true; 1969 emitItemsChanged(); 1970 } else { 1971 state.done = true; 1972 emitItemsChanged(); 1973 } 1974 if (renderedDomainCount() === 0 && emptyRow) emptyRow.classList.remove('hidden'); 1975 } catch(e){ console.error(e); } 1976 state.loading = false;
1977 if (loadingRow) loadingRow.classList.add('hidden'); 1978 if (state.reloadPending){ 1979 state.reloadPending = false; 1980 load(true); 1981 return; 1982 } 1983 if (!state.done && document.body.offsetHeight <= window.innerHeight + 400){ setTimeout(()=>load(false), 50); } 1984 } 1985 1986 // Infinite scroll 1987 function maybeLoadMore(){ if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 900) load(false); } 1988 window.addEventListener('scroll', maybeLoadMore, { passive: true }); 1989 if (sentinel) new IntersectionObserver(es => { if (es[0].isIntersecting) load(false); }, { rootMargin: '800px' }).observe(sentinel); 1990 1991 // Delegated card actions 1992 grid.addEventListener('click', (e)=>{ 1993 const fav = e.target.closest('[data-fav]'); 1994 const buy = e.target.closest('[data-buy]'); 1995 const gallery = e.target.closest('[data-gallery]'); 1996 const inquire = e.target.closest('[data-inquire]'); 1997 if (inquire){ e.preventDefault(); if (window.NameOfficeInquiry) window.NameOfficeInquiry.open(inquire.dataset.inquire, inquire.dataset.inquireName); } 1998 else if (gallery){ e.preventDefault(); if (window.NameOfficeGallery) window.NameOfficeGallery.openById(parseInt(gallery.dataset.gallery)); } 1999 else if (fav){ 2000 e.preventDefault(); 2001 const id = parseInt(fav.dataset.fav); 2002 toggleFav(id, fav); 2003 // On the favorites page, un-saving a name takes it off the list. 2004 if (cfg.removeOnUnfavorite && !getFavs().includes(id)){ 2005 const card = fav.closest('[data-domain-card]'); 2006 if (card){ 2007 card.style.transition = 'opacity .2s, transform .2s'; 2008 card.style.opacity = '0'; 2009 card.style.transform = 'scale(.96)'; 2010 setTimeout(()=>{ 2011 card.remove(); 2012 state.items = state.items.filter(x => x.id !== id); 2013 if (state.total > 0) state.total--; 2014 if (countEl) countEl.textContent = formatCount(state.total); 2015 if (renderedDomainCount() === 0 && emptyRow) emptyRow.classList.remove('hidden'); 2016 }, 200); 2017 } 2018 } 2019 } 2020 else if (buy){ 2021 e.preventDefault(); 2022 const id = parseInt(buy.dataset.buy); 2023 gaAddToCart(id, buy.closest('[data-domain-card]')); 2024 buyNow(id); 2025 } 2026 }); 2027 2028 refreshCartBadge(); 2029 if (hasInitialItems) { 2030 if (!state.done && document.body.offsetHeight <= window.innerHeight + 400) { 2031 setTimeout(()=>load(false), 50); 2032 } 2033 } else { 2034 load(true); 2035 } 2036 2037 const api = { 2038 setParams(p){ state.params = p || {}; load(true); }, 2039 reload(){ load(true); }, 2040 getItems(){ return state.items; }, 2041 isDone(){ return state.done; }, 2042 isLoading(){ return state.loading; }, 2043 loadMore(){ return load(false); }, 2044 onItemsChanged(fn){ if (typeof fn === 'function') itemsListeners.push(fn); }, 2045 setFacet(key, opt){ setFacet(key, opt); }, 2046 resetFacets(){ resetFacets(); }, 2047 }; 2048 // Expose the most recently created grid so the gallery (and others) can 2049 // read the live, filtered item set without re-fetching. 2050 window.nameOfficeGrid = api; 2051 return api; 2052 } 2053 2054 return { create, cardHTML, refreshCartBadge, buyNow, addToCart, toggleFav, setFavs, getFavs, getCart, setCart, syncFavButtons }; 2055})(); 2056</script>
2056 2057 2058 <style> 2059 .nog-overlay { position: fixed; inset: 0; z-index: 9999; display: flex; align-items: center; justify-content: center; background: rgba(0,0,0,.92); } 2060 .nog-overlay[hidden] { display: none; } 2061 2062 .nog-frame { position: relative; height: 92vh; max-height: 900px; width: min(92vw, 480px); overflow: hidden; border-radius: 28px; box-shadow: 0 50px 120px -20px rgba(0,0,0,.6); background:#0b1020; } 2063 .nog-stage { position: absolute; inset: 0; overflow-y: scroll; overflow-x: hidden; scroll-snap-type: y mandatory; overscroll-behavior: contain; scrollbar-width: none; -webkit-overflow-scrolling: touch; } 2064 .nog-stage::-webkit-scrollbar { display: none; } 2065 2066 .nog-slide { position: relative; height: 100%; overflow: hidden; scroll-snap-align: start; scroll-snap-stop: always; container-type: inline-size; } 2067 .nog-slide__scrim { position:absolute; inset:0; background: radial-gradient(circle at 50% 28%, rgba(255,255,255,.10), transparent 60%); } 2068 .nog-slide__inner { position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center; padding: 64px 32px 168px; text-align:center; } 2069 .nog-logo { display:flex; align-items:center; justify-content:center; height: 13.5rem; width: 13.5rem; max-width: 62%; border-radius: 24px; background:#fff; padding:14px; box-shadow:0 24px 60px -12px rgba(0,0,0,.5); margin-bottom: 28px; } 2070 .nog-logo img { height:100%; width:100%; object-fit:contain; border-radius:12px; } 2071 .nog-name { color:#fff; font-weight:600; letter-spacing:-.02em; line-height:1.05; font-size: clamp(1.7rem, 8cqw, 3rem); word-break: break-word; text-decoration:none; } 2072 .nog-name .tld { color: rgba(255,255,255,.4); font-weight:600; } 2073 .nog-pron { margin-top:8px; font-family: ui-monospace, 'SFMono-Regular', Menlo, monospace; font-size:13px; letter-spacing:.04em; color: rgba(255,255,255,.55); } 2074 .nog-cat { margin-top:10px; font-size:11px; text-transform:uppercase; letter-spacing:.28em; color: rgba(255,255,255,.5); } 2075 .nog-desc { margin-top: 22px; max-width: 36ch; font-size: 15px; line-height:1.65; color: rgba(255,255,255,.82); } 2076 .nog-tags { margin-top: 20px; display:flex; flex-wrap:wrap; gap:6px; justify-content:center; } 2077 .nog-tag { border-radius:999px; background: rgba(255,255,255,.10); padding:4px 10px; font-size:11px; font-weight:500; color: rgba(255,255,255,.78); backdrop-filter: blur(4px); } 2078 2079 .nog-strip { position:absolute; inset-inline:0; bottom:0; z-index:20; border-top:1px solid rgba(255,255,255,.12); background: rgba(0,0,0,.34); backdrop-filter: blur(10px); padding:18px; } 2080 .nog-strip__meta { display:flex; align-items:center; justify-content:space-between; font-size:12px; color: rgba(255,255,255,.72); margin-bottom:12px; } 2081 .nog-strip__price { color:#fff; font-weight:600; font-variant-numeric: tabular-nums; } 2082 .nog-strip__dot { margin:0 8px; color: rgba(255,255,255,.3); } 2083 .nog-details { display:inline-flex; align-items:center; gap:4px; color: rgba(255,255,255,.72); text-decoration:none; } 2084 .nog-details:hover { color:#fff; text-decoration:underline; text-underline-offset:4px; } 2085 .nog-claim { display:block; width:100%; border:none; cursor:pointer; border-radius:14px; background:#fff; padding:14px; font-size:14px; font-weight:600; letter-spacing:.01em; color:#01102E; box-shadow:0 12px 30px -10px rgba(0,0,0,.5); transition: background .15s, transform .1s; } 2086 .nog-claim:hover { background:#74F7B2; } 2087 .nog-claim:active { transform: scale(.99); } 2088 2089 /* Right action rail â fixed over the scroller */ 2090 .nog-rail { position:absolute; right:12px; bottom: 188px; z-index:30; display:flex; flex-direction:column; align-items:center; gap:18px; } 2091 .nog-railbtn { display:flex; flex-direction:column; align-items:center; gap:5px; background:none; border:none; cursor:pointer; color:#fff; } 2092 .nog-railbtn__icon { display:grid; place-items:center; height:48px; width:48px; border-radius:50%; background: rgba(255,255,255,.12); backdrop-filter: blur(4px); transition: background .15s, transform .12s; } 2093 .nog-railbtn:hover .nog-railbtn__icon { background: rgba(255,255,255,.22); } 2094 .nog-railbtn:active .nog-railbtn__icon { transform: scale(.92); } 2095 .nog-railbtn.is-active .nog-railbtn__icon { background: rgba(255,255,255,.22); } 2096 .nog-railbtn__label { font-size:10px; font-weight:500; color: rgba(255,255,255,.72); } 2097 .nog-railbtn .heart-on { display:none; } 2098 .nog-railbtn.is-active .heart-on { display:block; } 2099 .nog-railbtn.is-active .heart-off { display:none; } 2100 2101 /* Chrome: close, counter, dots, desktop nav, hint */ 2102 .nog-close, .nog-counter { position:absolute; top:16px; z-index:50; } 2103 .nog-close { left:16px; display:grid; place-items:center; height:40px; width:40px; border-radius:50%; border:none; cursor:pointer; background: rgba(255,255,255,.12); color:#fff; backdrop-filter: blur(4px); transition: background .15s; } 2104 .nog-close:hover { background: rgba(255,255,255,.22); } 2105 .nog-counter { right:16px; border-radius:999px; background: rgba(255,255,255,.12); padding:6px 12px; font-size:12px; font-variant-numeric: tabular-nums; color: rgba(255,255,255,.82); backdrop-filter: blur(4px); } 2106 .nog-dots { position:absolute; left:10px; top:50%; transform: translateY(-50%); z-index:30; display:flex; flex-direction:column; gap:5px; pointer-events:none; } 2107 .nog-dots span { border-radius:999px; background: rgba(255,255,255,.3); height:6px; width:6px; transition: all .25s; } 2108 .nog-dots span.is-active { height:16px; background:#fff; } 2109 .nog-nav { position:absolute; right:16px; top:50%; transform: translateY(-50%); z-index:40; display:none; flex-direction:column; gap:10px; } 2110 .nog-navbtn { display:grid; place-items:center; height:44px; width:44px; border-radius:50%; border:none; cursor:pointer; background: rgba(255,255,255,.12); color:#fff; backdrop-filter: blur(4px); transition: background .15s; } 2111 .nog-navbtn:hover { background: rgba(255,255,255,.22); } 2112 .nog-navbtn:disabled { opacity:.3; cursor:default; } 2113 .nog-hint { position:absolute; bottom:14px; left:50%; transform: translateX(-50%); z-index:40; display:none; gap:12px; align-items:center; font-size:11px; text-transform:uppercase; letter-spacing:.2em; color: rgba(255,255,255,.4); white-space:nowrap; } 2114 .nog-hint span.sep { color: rgba(255,255,255,.2); } 2115 @media (min-width: 768px) { .nog-nav { display:flex; } .nog-hint { display:flex; } .nog-rail { right:16px; } } 2116 2117 .nog-toast { position: fixed; left:50%; top:24px; transform: translateX(-50%) translateY(-12px); z-index:10000; background:#01102E; color:#fff; border:1px solid rgba(255,255,255,.12); border-radius:12px; padding:10px 16px; font-size:13px; box-shadow:0 16px 40px -12px rgba(0,0,0,.5); opacity:0; transition: opacity .2s, transform .2s; pointer-events:none; } 2118 .nog-toast.is-show { opacity:1; transform: translateX(-50%) translateY(0); } 2119 .nog-toast b { color:#74F7B2; } 2120 2121 /* Slide content stays hidden until its slide is the active one, so you never 2122 see it pre-animation (no "show -> flash -> fade"). It fades in quickly the 2123 moment the slide becomes active while scrolling. The bottom strip is not 2124 animated so price/CTA are always present. */
2125 @keyframes nogFadeIn { from { opacity:0; transform: translateY(10px);} to { opacity:1; transform: translateY(0);} } 2126 .nog-slide__inner { opacity: 0; } 2127 .nog-slide.is-active .nog-slide__inner { animation: nogFadeIn 280ms ease-out both; } 2128 @media (prefers-reduced-motion: reduce) { 2129 .nog-slide__inner { opacity: 1; } 2130 .nog-slide.is-active .nog-slide__inner { animation: none; } 2131 .nog-stage { scroll-behavior: auto; } 2132 } 2133</style> 2134
2135<script> 2136window.NameOfficeGallery = (function () { 2137 var TINTS = [ 2138 'linear-gradient(135deg,#0f172a,#1e1b4b,#0f172a)', 2139 'linear-gradient(135deg,#0c2340,#134e4a,#0c2340)', 2140 'linear-gradient(135deg,#1a1a2e,#3b0764,#1a1a2e)', 2141 'linear-gradient(135deg,#0d0d0d,#2d2d2d,#0d0d0d)', 2142 'linear-gradient(135deg,#1e3a5f,#0c2340,#1e3a5f)', 2143 'linear-gradient(135deg,#3a1a1a,#5c2018,#3a1a1a)' 2144 ]; 2145 var LOAD_AHEAD = 3; // request more when this close to the end 2146 2147 var G = window.NameOfficeGrid; 2148 var overlay = null, frame = null, stage = null, counterEl = null, dotsEl = null, 2149 prevBtn = null, nextBtn = null, toastEl = null; 2150 var items = [], index = 0, total = 0, renderedCount = 0; 2151 var toastTimer = null, keyHandler = null, prevOverflow = '', scrollRaf = null, settleTimer = null; 2152 2153 function esc(s){ return (s||'').replace(/[&<>"']/g, function(c){ return ({'&':'&','<':'<','>':'>','"':'"',"'":'''})[c]; }); } 2154 function money(n){ return '$' + (Number(n)||0).toLocaleString(); } 2155 function nameNoTld(item){ 2156 if (item && typeof item === 'object' && item.rootName) return item.rootName; 2157 var fn = item && typeof item === 'object' ? item.formattedName : item; 2158 return (fn || '').replace(/\.[a-z0-9]+$/i, ''); 2159 } 2160 function tld(item){ 2161 if (item && typeof item === 'object' && item.tld) return '.' + String(item.tld).toLowerCase(); 2162 var fn = item && typeof item === 'object' ? item.formattedName : item; 2163 var m = (fn || '').match(/\.([a-z0-9]+)$/i); 2164 return m ? '.' + m[1].toLowerCase() : '.com'; 2165 } 2166 function gridDone(){ return window.nameOfficeGrid && window.nameOfficeGrid.isDone ? window.nameOfficeGrid.isDone() : true; } 2167 2168 function refreshItems(){ 2169 items = (G && window.nameOfficeGrid && window.nameOfficeGrid.getItems) ? window.nameOfficeGrid.getItems() : []; 2170 total = items.length; 2171 } 2172 2173 function toast(html){ 2174 if (!toastEl) return; 2175 toastEl.innerHTML = html; 2176 toastEl.classList.add('is-show'); 2177 clearTimeout(toastTimer); 2178 toastTimer = setTimeout(function(){ toastEl.classList.remove('is-show'); }, 2200); 2179 } 2180 2181 function isFav(id){ return G.getFavs().indexOf(id) !== -1; } 2182 2183 function toggleFav(id){ 2184 return G.toggleFav(id); 2185 } 2186 2187 function addToCart(id){ 2188 return G.addToCart(id); 2189 } 2190 2191 function slideHTML(i){ 2192 var d = items[i]; 2193 if (!d) return ''; 2194 var name = nameNoTld(d); 2195 var t = tld(d); 2196 var visual = d.logoPath 2197 ? '<div class="nog-logo"><img src="/' + esc(d.logoPath) + '" alt="' + esc(d.imageSeoTitle || d.formattedName) + '" loading="lazy"></div>' 2198 : ''; 2199 var len = name.length; 2200 return '' 2201 + '<div class="nog-slide" data-i="' + i + '" style="background:' + TINTS[i % TINTS.length] + ';">' 2202 + '<div class="nog-slide__scrim"></div>' 2203 + '<div class="nog-slide__inner">' 2204 + visual 2205 + '<a class="nog-name" href="' + esc(d.path) + '">' + esc(name) + '<span class="tld">' + esc(t) + '</span></a>' 2206 + (d.pronunciation ? '<div class="nog-pron">/' + esc(d.pronunciation) + '/</div>' : '') 2207 + '<div class="nog-cat">' + esc(d.categoryName || 'Premium') + '</div>' 2208 + (d.shortDescription ? '<p class="nog-desc">' + esc(d.shortDescription) + '</p>' : '') 2209 + '<div class="nog-tags">' 2210 + '<span class="nog-tag">' + esc(d.categoryName || 'Premium') + '</span>' 2211 + '<span class="nog-tag">' + len + ' chars</span>' 2212 + '<span class="nog-tag">' + esc(t) + '</span>' 2213 + '</div>' 2214 + '</div>' 2215 + '<div class="nog-strip">' 2216 + '<div class="nog-strip__meta">' 2217 + '<span><span class="nog-strip__price">' + money(d.price) + '</span><span class="nog-strip__dot">·</span>' + esc(t) + '<span class="nog-strip__dot">·</span>' + len + ' chars</span>' 2218 + '<a class="nog-details" href="' + esc(d.path) + '">
2218Details <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M7 17 17 7M7 7h10v10"/></svg></a>' 2219 + '</div>' 2220 + '<button type="button" class="nog-claim" data-claim="' + d.id + '">Claim ' + esc(name) + esc(t) + '</button>' 2221 + '</div>' 2222 + '</div>'; 2223 } 2224 2225 // Append slide elements for any items not yet rendered. 2226 function renderSlides(){ 2227 if (renderedCount >= total) return; 2228 var html = ''; 2229 for (var i = renderedCount; i < total; i++){ html += slideHTML(i); } 2230 stage.insertAdjacentHTML('beforeend', html); 2231 renderedCount = total; 2232 } 2233 2234 function rebuildSlides(){ 2235 stage.innerHTML = ''; 2236 renderedCount = 0; 2237 refreshItems(); 2238 renderSlides(); 2239 } 2240 2241 // Mark the current slide active (re-triggers its entrance animation). 2242 function markActive(){ 2243 var slides = stage.querySelectorAll('.nog-slide'); 2244 for (var k = 0; k < slides.length; k++){ 2245 slides[k].classList.toggle('is-active', parseInt(slides[k].dataset.i) === index); 2246 } 2247 } 2248 2249 function updateChrome(){ 2250 var done = gridDone(); 2251 counterEl.textContent = (index + 1) + ' / ' + (done ? total : total + '+'); 2252 var lo = Math.max(0, index - 2), hi = Math.min(total - 1, index + 2); 2253 var d = ''; 2254 for (var i = lo; i <= hi; i++){ d += '<span class="' + (i === index ? 'is-active' : '') + '"></span>'; } 2255 dotsEl.innerHTML = d; 2256 prevBtn.disabled = index === 0; 2257 nextBtn.disabled = index >= total - 1 && done; 2258 var cur = items[index]; 2259 var likeBtn = overlay.querySelector('[data-rail="like"]'); 2260 if (cur && likeBtn) likeBtn.classList.toggle('is-active', isFav(cur.id)); 2261 } 2262 2263 function maybeLoadAhead(){ 2264 if (index >= total - 1 - LOAD_AHEAD){ 2265 var inst = window.nameOfficeGrid; 2266 if (inst && inst.loadMore && !inst.isDone() && !inst.isLoading()){ inst.loadMore(); } 2267 } 2268 } 2269 2270 // Current-slide tracking via scroll position. Computed synchronously (cheap) 2271 // so it stays in sync even where rAF is throttled. 2272 function onScroll(){ 2273 if (!stage.clientHeight) return; 2274 var i = Math.round(stage.scrollTop / stage.clientHeight); 2275 i = Math.max(0, Math.min(i, total - 1)); 2276 if (i !== index){ 2277 // Fire the fade-in the instant we land on a new slide while scrolling, 2278 // not after the scroll settles (that caused a flash on the already 2279 // hidden->shown content). 2280 index = i; 2281 markActive(); 2282 updateChrome(); 2283 maybeLoadAhead(); 2284 } 2285 } 2286 2287 function scrollToIndex(i, smooth){ 2288 var target = Math.max(0, Math.min(i, total - 1)); 2289 stage.scrollTo({ top: target * stage.clientHeight, behavior: smooth ? 'smooth' : 'auto' }); 2290 } 2291 2292 function go(dir){ 2293 var next = index + dir; 2294 if (next < 0) return; 2295 if (next >= total){ 2296 if (gridDone()) return; 2297 maybeLoadAhead(); 2298 refreshItems(); 2299 renderSlides(); 2300 if (next >= total) return; 2301 } 2302 scrollToIndex(next, true); 2303 } 2304 2305 /* ---- build / open / close ---- */ 2306 function build(){ 2307 overlay = document.createElement('div'); 2308 overlay.className = 'nog-overlay'; 2309 overlay.setAttribute('role', 'dialog'); 2310 overlay.setAttribute('aria-modal', 'true'); 2311 overlay.setAttribute('aria-label', 'Name gallery'); 2312 overlay.innerHTML = '' 2313 + '<button type="button" class="nog-close" data-close aria-label="Close gallery"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 6 6 18M6 6l12 12"/></svg></button>' 2314 + '<div class="nog-counter"></div>' 2315 + '<div class="nog-frame">' 2316 + '<div class="nog-stage"></div>' 2317 + '<div class="nog-dots"></div>' 2318 + '<div class="nog-rail">' 2319 + '<button type="button" class="nog-railbtn" data-rail="like" aria-label="Like"><span class="nog-railbtn__icon">' 2320 + '<svg class="heart-off" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>' 2321 + '<svg class="heart-on" width="22" height="22" viewBox="0 0 24 24" fill="#f43f5e" stroke="#f43f5e" stroke-width="2"><path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/></svg>' 2322 + '</span><span class="nog-railbtn__label">Like</span></button>' 2323 + '<button type="button" class="nog-railbtn" data-rail="cart" aria-label="Add to cart"><span class="nog-railbtn__icon"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/></svg></span><span class="nog-railbtn__label">
2323Cart</span></button>' 2324 + '<button type="button" class="nog-railbtn" data-rail="share" aria-label="Share"><span class="nog-railbtn__icon"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="m8.59 13.51 6.83 3.98M15.41 6.51l-6.82 3.98"/></svg></span><span class="nog-railbtn__label">Share</span></button>' 2325 + '</div>' 2326 + '</div>' 2327 + '<div class="nog-nav">' 2328 + '<button type="button" class="nog-navbtn" data-prev aria-label="Previous name"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="m18 15-6-6-6 6"/></svg></button>' 2329 + '<button type="button" class="nog-navbtn" data-next aria-label="Next name"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="m6 9 6 6 6-6"/></svg></button>' 2330 + '</div>' 2331 + '<div class="nog-hint"><span>â â or swipe</span><span class="sep">·</span><span>L to like</span><span class="sep">·</span><span>Esc to close</span></div>'; 2332 2333 document.body.appendChild(overlay); 2334 toastEl = document.createElement('div'); 2335 toastEl.className = 'nog-toast'; 2336 document.body.appendChild(toastEl); 2337 2338 frame = overlay.querySelector('.nog-frame'); 2339 stage = overlay.querySelector('.nog-stage'); 2340 counterEl = overlay.querySelector('.nog-counter'); 2341 dotsEl = overlay.querySelector('.nog-dots'); 2342 prevBtn = overlay.querySelector('[data-prev]'); 2343 nextBtn = overlay.querySelector('[data-next]'); 2344 2345 stage.addEventListener('scroll', onScroll, { passive: true }); 2346 2347 overlay.addEventListener('click', function(e){ 2348 if (e.target.closest('[data-close]')) { close(); return; } 2349 if (e.target === overlay) { close(); return; } 2350 if (e.target.closest('[data-prev]')) { go(-1); return; } 2351 if (e.target.closest('[data-next]')) { go(1); return; } 2352 var rail = e.target.closest('[data-rail]'); 2353 if (rail) { handleRail(rail.dataset.rail); return; } 2354 var claim = e.target.closest('[data-claim]'); 2355 if (claim) { e.preventDefault(); doCart(parseInt(claim.dataset.claim)); return; } 2356 }); 2357 2358 keyHandler = function(e){ 2359 if (overlay.hidden) return; 2360 if (e.key === 'Escape') close(); 2361 else if (e.key === 'ArrowDown' || e.key === 'ArrowRight' || e.key === ' ') { e.preventDefault(); go(1); } 2362 else if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') { e.preventDefault(); go(-1); } 2363 else if (e.key.toLowerCase() === 'l') { if (items[index]) handleRail('like'); } 2364 else if (e.key.toLowerCase() === 'c') { if (items[index]) doCart(items[index].id); } 2365 }; 2366 document.addEventListener('keydown', keyHandler); 2367 2368 // Append new slides + refresh counter as the grid streams in more items. 2369 if (window.nameOfficeGrid && window.nameOfficeGrid.onItemsChanged){ 2370 window.nameOfficeGrid.onItemsChanged(function(){ 2371 if (overlay && !overlay.hidden){ refreshItems(); renderSlides(); updateChrome(); } 2372 }); 2373 } 2374 } 2375 2376 function handleRail(kind){ 2377 var cur = items[index]; 2378 if (!cur) return; 2379 if (kind === 'like'){ 2380 var on = toggleFav(cur.id); 2381 var btn = overlay.querySelector('[data-rail="like"]'); 2382 if (btn) btn.classList.toggle('is-active', on); 2383 toast(on ? 'Saved <b>' + esc(nameNoTld(cur)) + '</b>' : 'Removed from saved'); 2384 } else if (kind === 'cart'){ 2385 doCart(cur.id); 2386 } else if (kind === 'share'){ 2387 // Always the canonical landing-page URL for this name. 2388 var url = location.origin + cur.path; 2389 var isTouch = window.matchMedia && window.matchMedia('(pointer: coarse)').matches; 2390 // On touch devices the native share sheet is genuinely useful. On 2391 // desktop, copy the link straight to the clipboard so "share" reliably 2392 // puts the landing-page URL there (the OS sheet's "Copy" is not). 2393 if (navigator.share && isTouch){ 2394 navigator.share({ title: cur.formattedName, url: url }).catch(function(){}); 2395 } else { 2396 copyLink(url); 2397 } 2398 } 2399 } 2400 2401 // Copy a link to the clipboard, with a fallback for non-secure contexts. 2402 function copyLink(url){ 2403 function done(){ toast('Link copied'); } 2404 if (navigator.clipboard && navigator.clipboard.writeText){ 2405 navigator.clipboard.writeText(url).then(done).catch(function(){ legacyCopy(url); done(); }); 2406 } else { 2407 legacyCopy(url); done(); 2408 } 2409 } 2410 function legacyCopy(url){ 2411 try { 2412 var ta = document.createElement('textarea'); 2413 ta.value = url; 2414 ta.setAttribute('readonly', ''); 2415 ta.style.position = 'fixed'; 2416 ta.style.opacity = '0'; 2417 document.body.appendChild(ta); 2418 ta.select(); 2419 document.execCommand('copy'); 2420 document.body.removeChild(ta); 2421 } catch (e) {} 2422 } 2423 2424 function doCart(id){ 2425 var cur = (items[index] && items[index].id === id) ? items[index] : (items.filter(function(x){ return x.id === id; })[0]); 2426 var added = addToCart(id); 2427 toast(added ? 'Added <b>' + esc(nameNoTld(cur || {})) + '</b> to cart' : 'Already in your cart'); 2428 } 2429 2430 function openAt(i){ 2431 refreshItems(); 2432 if (!total) return; 2433 if (!overlay) build(); 2434 if (overlay.hidden) { prevOverflow = document.body.style.overflow; } 2435 document.body.style.overflow = 'hidden'; 2436 overlay.hidden = false;
2437 index = Math.max(0, Math.min(i || 0, total - 1)); 2438 rebuildSlides(); 2439 // The frame has a fixed height, so layout is known synchronously â set 2440 // everything now (don't depend on rAF, which is paused in hidden tabs). 2441 updateChrome(); 2442 markActive(); 2443 scrollToIndex(index, false); 2444 maybeLoadAhead(); 2445 // One more frame later in case fonts/images shift the metrics. 2446 requestAnimationFrame(function(){ scrollToIndex(index, false); }); 2447 } 2448 2449 function close(){ 2450 if (!overlay) return; 2451 overlay.hidden = true; 2452 document.body.style.overflow = prevOverflow; 2453 if (G && G.syncFavButtons) G.syncFavButtons(); 2454 } 2455 2456 return { 2457 open: function(i){ openAt(i); }, 2458 openById: function(id){ refreshItems(); var i = items.findIndex(function(x){ return x.id === id; }); if (i < 0) i = 0; openAt(i); }, 2459 close: close 2460 }; 2461})(); 2462</script>
2462 2463 2464 <style> 2465 .noi { position: fixed; inset: 0; z-index: 9998; display: none; align-items: center; justify-content: center; padding: 20px; font-family: 'Inter','Noto Sans',sans-serif; } 2466 .noi.is-open { display: flex; } 2467 .noi__backdrop { position: absolute; inset: 0; background: rgba(1,16,46,.62); backdrop-filter: blur(3px); } 2468 .noi__card { position: relative; width: 100%; max-width: 520px; max-height: 92vh; overflow-y: auto; background: #fff; border-radius: 24px; box-shadow: 0 40px 120px -28px rgba(0,0,0,.55); animation: noiIn 320ms cubic-bezier(.22,1,.36,1) both; } 2469 @keyframes noiIn { from { opacity: 0; transform: translateY(16px) scale(.98); } to { opacity: 1; transform: none; } } 2470 .noi__close { position: absolute; top: 16px; right: 16px; appearance: none; background: none; border: none; font-size: 26px; line-height: 1; color: #b3b9c4; cursor: pointer; z-index: 2; } 2471 .noi__close:hover { color: #01102E; } 2472 2473 .noi__head { padding: 28px 30px 0; } 2474 .noi__eyebrow { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .2em; color: #4aa77c; } 2475 .noi__title { margin: 8px 0 4px; font-size: 22px; font-weight: 700; letter-spacing: -.01em; color: #01102E; } 2476 .noi__sub { font-size: 13.5px; line-height: 1.6; color: #5b6880; } 2477 .noi__sub b { color: #01102E; } 2478 2479 .noi__form { padding: 18px 30px 28px; } 2480 .noi__row { display: grid; gap: 14px; grid-template-columns: 1fr 1fr; } 2481 @media (max-width: 520px) { .noi__row { grid-template-columns: 1fr; } } 2482 .noi__field { margin-bottom: 14px; } 2483 .noi__hp { position: absolute !important; left: -10000px !important; top: auto !important; width: 1px !important; height: 1px !important; overflow: hidden !important; } 2484 .noi__label { display: block; font-size: 12.5px; font-weight: 600; color: #01102E; margin-bottom: 6px; } 2485 .noi__input, .noi__textarea { 2486 width: 100%; border: 1px solid #e8eaee; border-radius: 12px; background: #fff; padding: 11px 13px; 2487 font-size: 14px; font-family: inherit; color: #01102E; transition: border-color .15s, box-shadow .15s; 2488 } 2489 .noi__input::placeholder, .noi__textarea::placeholder { color: #aab0bb; } 2490 .noi__input:focus, .noi__textarea:focus { outline: none; border-color: #74F7B2; box-shadow: 0 0 0 3px rgba(116,247,178,.25); } 2491 .noi__textarea { min-height: 96px; resize: vertical; } 2492 2493 .noi__subjects { display: flex; flex-wrap: wrap; gap: 8px; } 2494 .noi__chip { display: inline-flex; align-items: center; gap: 7px; border: 1px solid #e8eaee; border-radius: 999px; padding: 8px 14px; font-size: 13px; color: #4a5160; cursor: pointer; transition: all .15s; } 2495 .noi__chip input { accent-color: #01102E; margin: 0; } 2496 .noi__chip:has(input:checked) { border-color: #01102E; background: #01102E; color: #fff; } 2497 2498 .noi__btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; width: 100%; margin-top: 6px; background: #74F7B2; color: #01102E; border: none; border-radius: 999px; padding: 14px 24px; font-size: 14px; font-weight: 700; cursor: pointer; transition: filter .15s; } 2499 .noi__btn:hover { filter: brightness(1.05); } 2500 .noi__btn[disabled] { opacity: .85; cursor: default; } 2501 .noi__error { display: none; margin-top: 12px; font-size: 13px; color: #d23f3f; text-align: center; } 2502 .noi__error.is-show { display: block; } 2503 2504 .noi__dots { display: inline-flex; align-items: center; gap: 6px; height: 1em; } 2505 .noi__dots span { width: 7px; height: 7px; border-radius: 50%; background: #01102E; opacity: .35; animation: noiDot 1.2s infinite ease-in-out both; } 2506 .noi__dots span:nth-child(1){ animation-delay: -.24s; } .noi__dots span:nth-child(2){ animation-delay: -.12s; }
2507 @keyframes noiDot { 0%,80%,100%{ opacity:.25; transform: translateY(0); } 40%{ opacity:1; transform: translateY(-4px); } } 2508 2509 .noi__success { display: none; padding: 40px 32px; text-align: center; } 2510 .noi.is-done .noi__head, .noi.is-done .noi__form { display: none; } 2511 .noi.is-done .noi__success { display: block; } 2512 .noi__check { display: grid; place-items: center; width: 60px; height: 60px; margin: 0 auto 18px; border-radius: 50%; background: rgba(116,247,178,.18); color: #0f7a4f; } 2513 .noi__success h3 { margin: 0 0 10px; font-size: 22px; font-weight: 700; color: #01102E; } 2514 .noi__success p { margin: 0 auto; max-width: 38ch; font-size: 14.5px; line-height: 1.7; color: #5b6880; } 2515 .noi__done-btn { margin-top: 22px; background: #01102E; color: #fff; border: none; border-radius: 999px; padding: 12px 26px; font-size: 13.5px; font-weight: 600; cursor: pointer; } 2516 .noi__done-btn:hover { background: #011845; } 2517</style> 2518 2519 2520 2521<div class="noi" id="inquiryModal" role="dialog" aria-modal="true" aria-labelledby="noiTitle"> 2522 <div class="noi__backdrop" data-noi-close></div> 2523 <div class="noi__card"> 2524 <button type="button" class="noi__close" data-noi-close aria-label="Close">×</button> 2525 2526 <div class="noi__head"> 2527 <div class="noi__eyebrow">Domain inquiry</div> 2528 <h2 class="noi__title" id="noiTitle">Ask about this name</h2> 2529 <p class="noi__sub">You're inquiring about <b data-noi-domain>this domain</b>. Send us a note and a real person will reply, usually within a couple of hours.</p> 2530 </div> 2531 2532 <form class="noi__form" id="inquiryForm" data-analytics-context="true" novalidate> 2533 <input type="hidden" name="_analytics[sessionToken]" data-analytics-field="sessionToken"> 2534<input type="hidden" name="_analytics[pageViewToken]" data-analytics-field="pageViewToken"> 2535<input type="hidden" name="_analytics[path]" data-analytics-field="path"> 2536<input type="hidden" name="_analytics[previousPath]" data-analytics-field="previousPath"> 2537<input type="hidden" name="_analytics[landingPath]" data-analytics-field="landingPath"> 2538<input type="hidden" name="_analytics[entryReferrer]" data-analytics-field="entryReferrer"> 2539 2540 <div class="noi__row"> 2541 <div class="noi__field"> 2542 <label class="noi__label" for="noi-first">First name</label> 2543 <input class="noi__input" id="noi-first" name="inquiry[firstName]" type="text" placeholder="Jane" required> 2544 </div> 2545 <div class="noi__field"> 2546 <label class="noi__label" for="noi-last">Last name</label> 2547 <input class="noi__input" id="noi-last" name="inquiry[lastName]" type="text" placeholder="Doe" required> 2548 </div> 2549 </div> 2550 <div class="noi__row"> 2551 <div class="noi__field"> 2552 <label class="noi__label" for="noi-email">Email</label> 2553 <input class="noi__input" id="noi-email" name="inquiry[email]" type="email" placeholder="[email protected]" required> 2554 </div> 2555 <div class="noi__field"> 2556 <label class="noi__label" for="noi-phone">Phone</label> 2557 <input class="noi__input" id="noi-phone" name="inquiry[phoneNumber]" type="tel" placeholder="+1 012 3456 789" required> 2558 </div> 2559 </div> 2560 2561 <div class="noi__field"> 2562 <span class="noi__label">Subject</span> 2563 <div class="noi__subjects"> 2564 <label class="noi__chip"><input type="radio" name="inquiry[subject]" value="Domain Name" checked> Domain Name</label> 2565 <label class="noi__chip"><input type="radio" name="inquiry[subject]" value="General Inquiry"> General Inquiry</label> 2566 <label class="noi__chip"><input type="radio" name="inquiry[subject]" value="Logo Design"> Logo Design</label> 2567 </div> 2568 </div> 2569 2570 <div class="noi__field"> 2571 <label class="noi__label" for="noi-message">Message</label> 2572 <textarea class="noi__textarea" id="noi-message" name="inquiry[content]" placeholder="Write your message..." required></textarea> 2573 </div> 2574 2575 <div class="noi__hp" aria-hidden="true"> 2576 <input class="noi__input" id="noi-website" name="inquiry[website]" type="text" autocomplete="new-password" tabindex="-1" aria-hidden="true" spellcheck="false" autocapitalize="off" data-lpignore="true" data-1p-ignore="true" data-form-type="other"> 2577 </div> 2578 2579 2580 <button type="submit" class="noi__btn" id="noiSubmit">Send message</button> 2581 <div class="noi__error" id="noiError">Something went wrong. Please try again or email [email protected].</div> 2582 </form> 2583 2584 <div class="noi__success"> 2585 <div class="noi__check"> 2586 <svg width="30" height="30" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M20 6 9 17l-5-5"/></svg> 2587 </div> 2588 <h3>Thanks for reaching out.</h3> 2589 <p>We've got your message about <b data-noi-domain-success>this domain</b> and a real person will get back to you shortly.</p> 2590 <button type="button" class="noi__done-btn" data-noi-close>Keep browsing</button> 2591 </div> 2592 </div> 2593</div> 2594
2595<script> 2596window.NameOfficeInquiry = (function () { 2597 var SITE_KEY = null; 2598 var ACTION_TEMPLATE = "\/name\/__DOMAIN__\/inquire"; 2599 var modal, form, submitBtn, errorEl, captchaEl, domainEls, domainSuccessEls, defaultErrorMessage; 2600 var currentName = null; 2601 2602 function init() { 2603 modal = document.getElementById('inquiryModal'); 2604 if (!modal) return false; 2605 form = document.getElementById('inquiryForm'); 2606 submitBtn = document.getElementById('noiSubmit'); 2607 errorEl = document.getElementById('noiError'); 2608 defaultErrorMessage = errorEl ? errorEl.textContent : 'Something went wrong. Please try again or email [email protected].'; 2609 captchaEl = document.getElementById('noi-captcha'); 2610 domainEls = modal.querySelectorAll('[data-noi-domain]'); 2611 domainSuccessEls = modal.querySelectorAll('[data-noi-domain-success]'); 2612 2613 modal.addEventListener('click', function (e) { 2614 if (e.target.closest('[data-noi-close]')) close(); 2615 }); 2616 document.addEventListener('keydown', function (e) { 2617 if (e.key === 'Escape' && modal.classList.contains('is-open')) close(); 2618 }); 2619 form.addEventListener('submit', onSubmit); 2620 return true; 2621 } 2622 2623 function open(name, display, prefill) { 2624 if (!modal && !init()) return; 2625 currentName = name; 2626 form.action = ACTION_TEMPLATE.replace('__DOMAIN__', encodeURIComponent(name)); 2627 var label = display || name; 2628 domainEls.forEach(function (el) { el.textContent = label; }); 2629 domainSuccessEls.forEach(function (el) { el.textContent = label; }); 2630 modal.classList.remove('is-done'); 2631 errorEl.classList.remove('is-show'); 2632 errorEl.textContent = defaultErrorMessage; 2633 form.reset(); 2634 if (window.NameOfficeAnalytics && window.NameOfficeAnalytics.fillForm) { 2635 window.NameOfficeAnalytics.fillForm(form); 2636 } 2637 // Optional prefill, used by the offer prompt so the visitor lands on a 2638 // part-written offer rather than an empty box. Applied after reset(). 2639 if (prefill && typeof prefill === 'object') { 2640 if (prefill.subject) { 2641 var subjectInput = form.querySelector('input[name="inquiry[subject]"][value="' + prefill.subject + '"]'); 2642 if (subjectInput) subjectInput.checked = true; 2643 } 2644 if (prefill.message) { 2645 var messageEl = document.getElementById('noi-message'); 2646 if (messageEl) messageEl.value = prefill.message; 2647 } 2648 } 2649 2650 document.body.style.overflow = 'hidden'; 2651 modal.classList.add('is-open'); 2652 // With a prefilled message the name fields are what still need filling. 2653 var first = document.getElementById('noi-first'); 2654 if (first) setTimeout(function () { first.focus(); }, 50); 2655 } 2656 2657 function close() { 2658 if (!modal) return; 2659 modal.classList.remove('is-open'); 2660 document.body.style.overflow = ''; 2661 } 2662 2663 function setLoading(on) { 2664 submitBtn.disabled = on; 2665 submitBtn.innerHTML = on 2666 ? '<span class="noi__dots"><span></span><span></span><span></span></span>' 2667 : 'Send message'; 2668 } 2669 2670 function showError(message) { 2671 if (!errorEl) return; 2672 errorEl.textContent = message || defaultErrorMessage; 2673 errorEl.classList.add('is-show'); 2674 } 2675 2676 function getToken() { 2677 return new Promise(function (resolve) { 2678 if (!SITE_KEY || typeof grecaptcha === 'undefined' || !grecaptcha.execute) { resolve(''); return; } 2679 try { 2680 grecaptcha.ready(function () { 2681 grecaptcha.execute(SITE_KEY, { action: 'inquiry' }).then(resolve).catch(function () { resolve(''); }); 2682 }); 2683 } catch (e) { resolve(''); } 2684 }); 2685 } 2686 2687 function onSubmit(e) { 2688 e.preventDefault(); 2689 if (submitBtn.disabled) return; 2690 if (!form.checkValidity()) { form.reportValidity(); return; } 2691 errorEl.classList.remove('is-show');
2692 setLoading(true); 2693 2694 getToken().then(function (token) { 2695 if (captchaEl) { 2696 captchaEl.value = token; 2697 } 2698 return fetch(form.action, { 2699 method: 'POST', 2700 headers: { 'X-Requested-With': 'XMLHttpRequest' }, 2701 body: new FormData(form) 2702 }); 2703 }).then(function (res) { 2704 return res.json().then(function (data) { return { ok: res.ok, data: data }; }); 2705 }).then(function (r) { 2706 setLoading(false); 2707 if (r.ok && r.data && r.data.ok) { 2708 modal.classList.add('is-done'); 2709 /* Reddit's Lead, on the server's confirmation rather than the 2710 click, so a rejected or spam-filtered enquiry is not a lead. */ 2711 if (typeof window.rdtTrack === 'function') { 2712 window.rdtTrack('track', 'Lead'); 2713 } 2714 } else { 2715 showError(r.data && Array.isArray(r.data.errors) && r.data.errors.length ? r.data.errors[0] : defaultErrorMessage); 2716 } 2717 }).catch(function () { 2718 setLoading(false); 2719 showError(defaultErrorMessage); 2720 }); 2721 } 2722 2723 if (document.readyState !== 'loading') init(); 2724 else document.addEventListener('DOMContentLoaded', init); 2725 2726 return { open: open, close: close }; 2727})(); 2728</script>
2728 2729 2730 2731
2731<script> 2732 NameOfficeGrid.create({ apiUrl: '/favorites-api', countLabel: 'saved names', removeOnUnfavorite: true }); 2733 </script>
2733 2734
2734<script> 2735 (function () { 2736 var root = document.getElementById('favorites-share-tools'); 2737 if (!root) return; 2738 2739 var panel = document.getElementById('favorites-share-panel'); 2740 var toggleBtn = document.getElementById('favorites-share-toggle'); 2741 var toggleIcon = document.getElementById('favorites-share-toggle-icon'); 2742 var createBtn = document.getElementById('favorites-share-create'); 2743 var result = document.getElementById('favorites-share-result'); 2744 var input = document.getElementById('favorites-share-url'); 2745 var copyBtn = document.getElementById('favorites-share-copy'); 2746 var nativeBtn = document.getElementById('favorites-share-native'); 2747 var openBtn = document.getElementById('favorites-share-open'); 2748 var status = document.getElementById('favorites-share-status'); 2749 var createUrl = root.getAttribute('data-create-url') || ''; 2750 var shareUrlTemplate = root.getAttribute('data-share-url-template') || ''; 2751 var placeholderToken = root.getAttribute('data-share-token-placeholder') || ''; 2752 var currentToken = ''; 2753 var isExpanded = root.getAttribute('data-initial-expanded') === '1'; 2754 2755 function renderExpandedState() { 2756 if (!panel || !toggleBtn || !toggleIcon) return; 2757 panel.classList.toggle('hidden', !isExpanded); 2758 toggleBtn.setAttribute('aria-expanded', isExpanded ? 'true' : 'false'); 2759 toggleIcon.innerHTML = isExpanded 2760 ? '<svg width="12" height="12" viewBox="0 0 20 20" fill="none"><path d="M5 12.5 10 7.5l5 5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>' 2761 : '<svg width="12" height="12" viewBox="0 0 20 20" fill="none"><path d="M5 7.5 10 12.5l5-5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'; 2762 } 2763 2764 function analyticsPayload() { 2765 try { 2766 if (window.NameOfficeAnalytics && typeof window.NameOfficeAnalytics.getContext === 'function') { 2767 return window.NameOfficeAnalytics.getContext(); 2768 } 2769 } catch (e) {} 2770 return {}; 2771 } 2772 2773 function setStatus(message, tone) { 2774 if (!status) return; 2775 status.textContent = message; 2776 status.className = 'mt-3 text-sm ' + (tone === 'success' 2777 ? 'text-emerald-700' 2778 : tone === 'error' 2779 ? 'text-red-600' 2780 : 'text-gray-500'); 2781 } 2782 2783 function applyShareLink(url, token) { 2784 currentToken = token || ''; 2785 if (input) input.value = url || ''; 2786 if (openBtn) openBtn.href = url || '#'; 2787 if (result) result.classList.remove('hidden'); 2788 isExpanded = true; 2789 renderExpandedState(); 2790 } 2791 2792 async function postShareAction() { 2793 if (!currentToken || !shareUrlTemplate || !placeholderToken || !window.fetch) return; 2794 2795 try { 2796 await fetch(shareUrlTemplate.replace(placeholderToken, currentToken), { 2797 method: 'POST', 2798 headers: { 'Content-Type': 'application/json' }, 2799 body: JSON.stringify(analyticsPayload()), 2800 keepalive: true 2801 }); 2802 } catch (e) {} 2803 } 2804 2805 async function copyCurrentLink() { 2806 if (!input || !input.value) return false; 2807 2808 try { 2809 if (navigator.clipboard && navigator.clipboard.writeText) { 2810 await navigator.clipboard.writeText(input.value); 2811 } else { 2812 input.focus(); 2813 input.select(); 2814 input.setSelectionRange(0, input.value.length); 2815 document.execCommand('copy'); 2816 } 2817 return true; 2818 } catch (e) { 2819 try { 2820 input.focus(); 2821 input.select(); 2822 input.setSelectionRange(0, input.value.length); 2823 } catch (selectionError) {} 2824 return false;
2825 } 2826 } 2827 2828 if (!navigator.share && nativeBtn) { 2829 nativeBtn.classList.add('hidden'); 2830 } 2831 2832 renderExpandedState(); 2833 2834 if (toggleBtn) { 2835 toggleBtn.addEventListener('click', function () { 2836 isExpanded = !isExpanded; 2837 renderExpandedState(); 2838 }); 2839 } 2840 2841 if (createBtn) { 2842 createBtn.addEventListener('click', async function () { 2843 if (!createUrl || !window.fetch) return; 2844 2845 createBtn.disabled = true; 2846 createBtn.classList.add('opacity-60', 'cursor-not-allowed'); 2847 setStatus('Creating your private favorites linkâ¦', 'neutral'); 2848 2849 try { 2850 var response = await fetch(createUrl, { 2851 method: 'POST', 2852 headers: { 'Content-Type': 'application/json' }, 2853 body: JSON.stringify(analyticsPayload()) 2854 }); 2855 var data = await response.json(); 2856 if (!response.ok || !data.shareUrl || !data.token) { 2857 throw new Error(data.error || 'Could not create a shareable link.'); 2858 } 2859 2860 applyShareLink(data.shareUrl, data.token); 2861 setStatus('Share link ready. Copy it or send it directly from here.', 'success'); 2862 } catch (error) { 2863 setStatus(error && error.message ? error.message : 'Could not create the share link right now. Please try again.', 'error'); 2864 } finally { 2865 createBtn.disabled = false; 2866 createBtn.classList.remove('opacity-60', 'cursor-not-allowed'); 2867 } 2868 }); 2869 } 2870 2871 if (copyBtn) { 2872 copyBtn.addEventListener('click', async function () { 2873 var copied = await copyCurrentLink(); 2874 if (copied) { 2875 setStatus('Link copied. Anyone with it can open this exact shortlist.', 'success'); 2876 } else { 2877 setStatus('The link is selected. Copy it manually from the field above.', 'neutral'); 2878 } 2879 postShareAction(); 2880 }); 2881 } 2882 2883 if (nativeBtn) { 2884 nativeBtn.addEventListener('click', async function () { 2885 if (!navigator.share || !input || !input.value) return; 2886 2887 try { 2888 await navigator.share({ 2889 title: 'NameOffice saved names', 2890 text: 'Premium domain shortlist from NameOffice', 2891 url: input.value 2892 }); 2893 setStatus('Share sheet opened for this shortlist link.', 'success'); 2894 postShareAction(); 2895 } catch (error) {} 2896 }); 2897 } 2898 })(); 2899 </script>
2899 2900 <div id="page-loader" class="page-loader" aria-hidden="true"> 2901 <img class="page-loader__symbol" src="/brand/nameoffice-symbol.png" alt="" width="56" height="56"> 2902</div> 2903<style> 2904 .page-loader { 2905 position: fixed; 2906 inset: 0; 2907 z-index: 99999; 2908 display: flex; 2909 align-items: center; 2910 justify-content: center; 2911 background: rgba(0, 0, 0, 0.5); 2912 opacity: 0; 2913 visibility: hidden; 2914 pointer-events: none; 2915 transition: opacity .18s ease, visibility .18s ease; 2916 } 2917 .page-loader.is-active { opacity: 1; visibility: visible; pointer-events: auto; } 2918 .page-loader__symbol { 2919 width: 56px; 2920 height: 56px; 2921 animation: page-loader-spin .9s linear infinite; 2922 } 2923 @keyframes page-loader-spin { to { transform: rotate(360deg); } } 2924 @media (prefers-reduced-motion: reduce) { 2925 .page-loader__symbol { animation-duration: 2.4s; } 2926 } 2927</style>
2928<script> 2929(function () { 2930 var loader = document.getElementById('page-loader'); 2931 if (!loader) return; 2932 var safetyTimer = null; 2933 2934 function show() { 2935 loader.classList.add('is-active'); 2936 window.clearTimeout(safetyTimer); 2937 // Failsafe: if navigation never happens (JS-handled action), hide it. 2938 safetyTimer = window.setTimeout(hide, 12000); 2939 } 2940 function hide() { 2941 loader.classList.remove('is-active'); 2942 window.clearTimeout(safetyTimer); 2943 } 2944 2945 // Bubble phase so anything that calls preventDefault (modals, carousels, 2946 // drag handlers) marks the event as handled before we see it. 2947 document.addEventListener('click', function (e) { 2948 if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return; 2949 var a = e.target.closest('a'); 2950 if (!a) return; 2951 var href = a.getAttribute('href'); 2952 if (!href || href.charAt(0) === '#') return; 2953 if (a.target === '_blank' || a.hasAttribute('download')) return; 2954 var schemeMatch = href.match(/^([a-z][a-z0-9+.-]*):/i); 2955 if (schemeMatch && !/^https?$/i.test(schemeMatch[1])) return; 2956 if (a.host && a.host !== window.location.host) return; // external link 2957 show(); 2958 }); 2959 2960 document.addEventListener('submit', function (e) { 2961 if (e.defaultPrevented) return; 2962 var form = e.target; 2963 if (form && form.hasAttribute && form.hasAttribute('data-no-loader')) return; // form shows its own progress 2964 show(); 2965 }); 2966 2967 // Page restored from back/forward cache: make sure the loader is cleared. 2968 window.addEventListener('pageshow', function (e) { if (e.persisted) hide(); }); 2969})(); 2970</script>
2970 2971
2971<script> 2972(function () { 2973 if (window.NameOfficeAnalytics && window.NameOfficeAnalytics.__initialized) return; 2974 if (!('sendBeacon' in navigator) && !('fetch' in window)) return; 2975 2976 var SESSION_TIMEOUT_MS = 30 * 60 * 1000; 2977 var LS_PREFIX = 'nameoffice.analytics.'; 2978 var SS_LAST_PATH = 'nameoffice.analytics.last_path'; 2979 2980 function uuid() { 2981 if (window.crypto && crypto.randomUUID) return crypto.randomUUID(); 2982 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { 2983 var r = (Math.random() * 16) | 0, v = c === 'x' ? r : (r & 0x3) | 0x8; 2984 return v.toString(16); 2985 }); 2986 } 2987 2988 function read(storage, key) { 2989 try { return storage.getItem(key) || ''; } catch (e) { return ''; } 2990 } 2991 2992 function write(storage, key, value) { 2993 try { storage.setItem(key, value); } catch (e) {} 2994 } 2995 2996 function sameHost(a, b) { 2997 return String(a || '').replace(/^www\./i, '').toLowerCase() === String(b || '').replace(/^www\./i, '').toLowerCase(); 2998 } 2999 3000 function currentPath() { 3001 return window.location.pathname + window.location.search; 3002 } 3003 3004 function externalReferrer(url) { 3005 if (!url) return ''; 3006 try { 3007 var parsed = new URL(url, window.location.origin); 3008 if (!/^https?:$/i.test(parsed.protocol)) return ''; 3009 if (sameHost(parsed.host, window.location.host)) return ''; 3010 return parsed.href; 3011 } catch (e) { 3012 return ''; 3013 } 3014 } 3015 3016 function ensureSession(pathNow) { 3017 var token = read(window.localStorage, LS_PREFIX + 'session_token'); 3018 var lastSeen = parseInt(read(window.localStorage, LS_PREFIX + 'last_seen_at') || '0', 10) || 0; 3019 var isFresh = token && (Date.now() - lastSeen) < SESSION_TIMEOUT_MS; 3020 var created = false; 3021 3022 if (!isFresh) { 3023 token = uuid(); 3024 created = true; 3025 write(window.localStorage, LS_PREFIX + 'session_token', token); 3026 write(window.localStorage, LS_PREFIX + 'started_at', String(Date.now())); 3027 write(window.localStorage, LS_PREFIX + 'landing_path', pathNow); 3028 write(window.localStorage, LS_PREFIX + 'entry_referrer', externalReferrer(document.referrer)); 3029 } else { 3030 if (!read(window.localStorage, LS_PREFIX + 'landing_path')) { 3031 write(window.localStorage, LS_PREFIX + 'landing_path', pathNow); 3032 } 3033 } 3034 3035 write(window.localStorage, LS_PREFIX + 'last_seen_at', String(Date.now())); 3036 3037 return { 3038 created: created, 3039 sessionToken: token, 3040 landingPath: read(window.localStorage, LS_PREFIX + 'landing_path') || pathNow, 3041 entryReferrer: read(window.localStorage, LS_PREFIX + 'entry_referrer') || '' 3042 }; 3043 } 3044 3045 var path = currentPath(); 3046 var session = ensureSession(path); 3047 var previousPath = session.created ? '' : read(window.sessionStorage, SS_LAST_PATH); 3048 if (previousPath === path) previousPath = ''; 3049 write(window.sessionStorage, SS_LAST_PATH, path); 3050 3051 var token = uuid(); 3052 var viewUrl = "\/api\/track\/view"; 3053 var pingUrl = "\/api\/track\/ping"; 3054 3055 var active = 0; // accumulated active seconds 3056 var lastSent = -1; // last duration value we pushed 3057 3058 function send(url, body) { 3059 var json = JSON.stringify(body); 3060 try { 3061 if (navigator.sendBeacon) { 3062 navigator.sendBeacon(url, new Blob([json], { type: 'application/json' })); 3063 return; 3064 } 3065 } catch (e) { /* fall through to fetch */ } 3066 if (window.fetch) { 3067 fetch(url, { method: 'POST', body: json, keepalive: true, headers: { 'Content-Type': 'application/json' } }); 3068 } 3069 } 3070 3071 function context() { 3072 write(window.localStorage, LS_PREFIX + 'last_seen_at', String(Date.now())); 3073 return { 3074 token: token, 3075 pageViewToken: token, 3076 sessionToken: session.sessionToken || null, 3077 path: path, 3078 previousPath: previousPath || null, 3079 landingPath: read(window.localStorage, LS_PREFIX + 'landing_path') || path, 3080 entryReferrer: read(window.localStorage, LS_PREFIX + 'entry_referrer') || '' 3081 }; 3082 } 3083 3084 function fillForm(form) { 3085 if (!form) return; 3086 var data = context(); 3087 form.querySelectorAll('[data-analytics-field]').forEach(function (input) { 3088 var field = input.getAttribute('data-analytics-field'); 3089 if (!field) return; 3090 input.value = data[field] || ''; 3091 }); 3092 } 3093 3094 function bindForms(root) { 3095 (root || document).querySelectorAll('form[data-analytics-context]').forEach(function (form) { 3096 if (form.dataset.analyticsBound === '1') return; 3097 form.dataset.analyticsBound = '1'; 3098 fillForm(form); 3099 form.addEventListener('submit', function () { 3100 fillForm(form); 3101 }); 3102 }); 3103 } 3104 3105 function trackDomainInteraction(domainId, type, action) { 3106 /* Reddit's AddToCart. Reported here rather than at each button because 3107 every path into the cart comes through this function, so a new one 3108 gets the event without anybody remembering to add it. rdtTrack queues 3109 until the pixel is up, and drops the event if consent never comes. */ 3110 if (type === 'cart' && (action || 'add') === 'add' && typeof window.rdtTrack === 'function') { 3111 window.rdtTrack('track', 'AddToCart', { 3112 itemCount: 1, 3113 products: [{ id: String(domainId) }] 3114 }); 3115 } 3116 3117 if (!window.fetch) return Promise.resolve(null); 3118 var body = context(); 3119 body.type = type; 3120 body.action = action || 'add'; 3121 return fetch('/api/domain/' + domainId + '/interact', { 3122 method: 'POST', 3123 keepalive: true, 3124 headers: { 'Content-Type': 'application/json' }, 3125 body: JSON.stringify(body) 3126 }).then(function (response) { 3127 return response.ok ? response.json() : null; 3128 }).catch(function () { 3129 return null; 3130 }); 3131 } 3132 3133 window.NameOfficeAnalytics = { 3134 __initialized: true, 3135 getContext: context, 3136 fillForm: fillForm, 3137 bindForms: bindForms, 3138 trackDomainInteraction: trackDomainInteraction 3139 }; 3140 3141 // Register the view as soon as possible. 3142 send(viewUrl, context()); 3143 3144 // Count a second of attention only while the tab is actually visible. 3145 window.setInterval(function () { 3146 if (document.visibilityState === 'visible') active += 1; 3147 }, 1000); 3148 3149 function flush() { 3150 if (active === lastSent) return; 3151 lastSent = active; 3152 send(pingUrl, { token: token, seconds: active }); 3153 } 3154 3155 // Send time-on-page only when the visitor leaves or hides the tab. No 3156 // periodic polling, so each page view costs just two small requests (the 3157 // initial view + one duration report) instead of one every few seconds. 3158 document.addEventListener('visibilitychange', function () { 3159 if (document.visibilityState === 'hidden') flush(); 3160 }); 3161 window.addEventListener('pagehide', flush); 3162 3163 if (document.readyState !== 'loading') bindForms(document); 3164 else document.addEventListener('DOMContentLoaded', function () { bindForms(document); }); 3165})(); 3166</script>
3166 3167 <div id="no-sold-toast" class="no-sold-toast" role="status" aria-live="polite"> 3168 <div class="no-sold-toast__logo" data-toast-logo aria-hidden="true"></div> 3169 <div class="no-sold-toast__body"> 3170 <div class="no-sold-toast__msg" data-toast-msg></div> 3171 <div class="no-sold-toast__sub"><span class="no-sold-toast__dot"></span>Verified sale on NameOffice</div> 3172 </div> 3173 <button type="button" class="no-sold-toast__close" data-toast-close aria-label="Dismiss notification">×</button> 3174</div> 3175<style> 3176.no-sold-toast{position:fixed;left:20px;bottom:20px;z-index:9990;display:flex;align-items:center;gap:13px;width:352px;max-width:calc(100vw - 40px);padding:12px 42px 12px 14px;background:#fff;border:1px solid rgba(1,16,46,.08);border-radius:14px;box-shadow:0 14px 38px rgba(1,16,46,.20);font-family:'Inter',system-ui,-apple-system,sans-serif;transform:translateY(150%);opacity:0;transition:transform .5s cubic-bezier(.16,1,.3,1),opacity .4s ease;pointer-events:none;} 3177.no-sold-toast.is-visible{transform:translateY(0);opacity:1;pointer-events:auto;} 3178.no-sold-toast__logo{flex:none;display:flex;align-items:center;justify-content:center;min-width:54px;} 3179.no-sold-toast__logo img{height:54px;width:auto;max-width:112px;object-fit:contain;display:block;} 3180.no-sold-toast__logo.is-mono{width:54px;height:54px;border-radius:12px;background:#01102E;color:#74F7B2;font-weight:700;font-size:22px;line-height:1;} 3181.no-sold-toast__body{min-width:0;} 3182.no-sold-toast__msg{font-size:14px;color:#101828;line-height:1.35;} 3183.no-sold-toast__msg strong{font-weight:600;} 3184.no-sold-toast__sub{margin-top:3px;font-size:11px;color:#15803d;font-weight:600;display:flex;align-items:center;gap:5px;} 3185.no-sold-toast__dot{width:7px;height:7px;border-radius:50%;background:#22c55e;box-shadow:0 0 0 0 rgba(34,197,94,.6);animation:noSoldPulse 2s infinite;} 3186@keyframes noSoldPulse{0%{box-shadow:0 0 0 0 rgba(34,197,94,.55)}70%{box-shadow:0 0 0 7px rgba(34,197,94,0)}100%{box-shadow:0 0 0 0 rgba(34,197,94,0)}} 3187.no-sold-toast__close{position:absolute;top:8px;right:8px;width:24px;height:24px;border:none;background:rgba(1,16,46,.05);border-radius:50%;color:#98a2b3;font-size:19px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;transition:background .15s,color .15s;} 3188.no-sold-toast__close:hover{background:rgba(1,16,46,.11);color:#101828;} 3189@media (max-width:480px){.no-sold-toast{left:12px;right:12px;width:auto;bottom:12px;}} 3190</style>
3191<script> 3192(function () { 3193 var el = document.getElementById('no-sold-toast'); 3194 if (!el) return; 3195 3196 var DATA_URL = '/recent-sales.json'; 3197 var DISMISS_URL = '/recent-sales/dismiss'; 3198 var SECOND_AFTER_MS = 5 * 60 * 1000; // second toast only if the visit passes 5 min 3199 var MAX_PER_SESSION = 2; 3200 3201 // Pop sound. Browsers block audio before any user gesture, so we prime it on 3202 // the first interaction; playSound() is a silent no-op if still blocked. 3203 var SOUND_URL = '/sounds/ui_click.wav'; 3204 var sound = null; 3205 try { sound = new Audio(SOUND_URL); sound.preload = 'auto'; sound.volume = 0.45; } catch (e) {} 3206 function primeSound(){ 3207 if (!sound) return; 3208 try { 3209 sound.muted = true; 3210 var p = sound.play(); 3211 if (p && p.then) { p.then(function(){ sound.pause(); sound.currentTime = 0; sound.muted = false; }).catch(function(){ sound.muted = false; }); } 3212 } catch (e) { sound.muted = false; } 3213 } 3214 ['pointerdown','keydown','touchstart'].forEach(function(ev){ document.addEventListener(ev, primeSound, { once: true, passive: true }); }); 3215 function playSound(){ if (!sound) return; try { sound.currentTime = 0; var p = sound.play(); if (p && p.catch) p.catch(function(){}); } catch (e) {} } 3216 3217 var SEEN_KEY = 'no_sold_seen'; // localStorage: names already shown (across visits) 3218 var SS_START = 'no_sold_start'; // sessionStorage: visit start timestamp 3219 var SS_SHOWN = 'no_sold_shown'; // sessionStorage: how many shown this visit 3220 var SS_FIRST = 'no_sold_first_at'; // sessionStorage: scheduled time for the first toast 3221 var SS_DONE = 'no_sold_done'; // sessionStorage: nothing left to show this visit 3222 3223 function lsArr(k){ try { return JSON.parse(localStorage.getItem(k)) || []; } catch (e) { return []; } } 3224 function lsSet(k,v){ try { localStorage.setItem(k, JSON.stringify(v)); } catch (e) {} } 3225 function ss(k){ try { return sessionStorage.getItem(k); } catch (e) { return null; } } 3226 function ssSet(k,v){ try { sessionStorage.setItem(k, v); } catch (e) {} } 3227 function looksInvalidSale(s){ 3228 if (!s || !s.name || !s.logo) return true; 3229 var name = String(s.name).toLowerCase(); 3230 var formatted = String(s.formattedName || '').toLowerCase(); 3231 return name.indexOf('.old') !== -1 || formatted.indexOf('.old') !== -1; 3232 } 3233 function markSeen(name){ 3234 if (!name) return; 3235 var seen = lsArr(SEEN_KEY); 3236 if (seen.indexOf(name) !== -1) return; 3237 seen.push(name); 3238 if (seen.length > 100) seen = seen.slice(seen.length - 100); 3239 lsSet(SEEN_KEY, seen); 3240 } 3241 3242 var now = Date.now(); 3243 var start = parseInt(ss(SS_START) || '0', 10); 3244 if (!start) { start = now; ssSet(SS_START, String(start)); } 3245 var firstAt = parseInt(ss(SS_FIRST) || '0', 10); 3246 if (!firstAt) { firstAt = start + 4000 + Math.floor(Math.random() * 5000); ssSet(SS_FIRST, String(firstAt)); } // 4-9s 3247 3248 var logoEl = el.querySelector('[data-toast-logo]'); 3249 var msgEl = el.querySelector('[data-toast-msg]'); 3250 var closeBtn = el.querySelector('[data-toast-close]'); 3251 var sales = null; // fetched once per page 3252 var timer = null; 3253 var currentName = null; 3254 3255 function shownCount(){ return parseInt(ss(SS_SHOWN) || '0', 10); } 3256 3257 function mono(sale){ return String(sale.formattedName || sale.name || '?').charAt(0).toUpperCase(); } 3258 3259 function fetchSales(cb){ 3260 if (sales){ cb(sales); return; } 3261 fetch(DATA_URL, { credentials: 'same-origin' }) 3262 .then(function(r){ return r.json(); }) 3263 .then(function(d){ 3264 sales = ((d && d.sales) || []).filter(function(s){ return !looksInvalidSale(s); }); 3265 cb(sales); 3266 }) 3267 .catch(function(){ cb([]); }); 3268 } 3269 3270 // Newest first, not random. The server already returns these ordered by 3271 // sold_at DESC, so taking the first unseen one means a visitor's first 3272 // toast is the most recent sale â picking at random surfaced a sale from 3273 // weeks ago while a fresh one sat unused, which reads as stale. 3274 function pick(list){ 3275 var seen = lsArr(SEEN_KEY); 3276 var avail = list.filter(function(s){ 3277 return !looksInvalidSale(s) && seen.indexOf(s.name) === -1; 3278 }); 3279 return avail.length ? avail[0] : null; 3280 } 3281 3282 function show(sale){ 3283 if (looksInvalidSale(sale)) return; 3284 currentName = sale.name; 3285 logoEl.innerHTML = ''; 3286 logoEl.classList.remove('is-mono'); 3287 var img = document.createElement('img'); 3288 img.src = sale.logo; img.alt = ''; 3289 img.onerror = function(){ 3290 markSeen(sale.name); 3291 currentName = null; 3292 attempt(); 3293 }; 3294 logoEl.appendChild(img); 3295 msgEl.innerHTML = '<strong></strong> just sold!'; 3296 msgEl.querySelector('strong').textContent = sale.formattedName || sale.name; 3297 el.classList.add('is-visible'); 3298 playSound(); 3299 3300 markSeen(sale.name); 3301 ssSet(SS_SHOWN, String(shownCount() + 1)); 3302 schedule(); 3303 } 3304 3305 function attempt(){ 3306 fetchSales(function(list){ 3307 var sale = pick(list); 3308 if (sale) { show(sale); } 3309 else { ssSet(SS_DONE, '1'); } // seen them all â stop for this visit 3310 }); 3311 } 3312 3313 function schedule(){ 3314 if (timer){ clearTimeout(timer); timer = null; } 3315 if (ss(SS_DONE) === '1') return; 3316 var count = shownCount(); 3317 if (count >
3317= MAX_PER_SESSION) return; 3318 var target = count === 0 ? firstAt : (start + SECOND_AFTER_MS); 3319 timer = setTimeout(attempt, Math.max(500, target - Date.now())); 3320 } 3321 3322 closeBtn.addEventListener('click', function(){ 3323 el.classList.remove('is-visible'); 3324 if (currentName){ 3325 try { 3326 var body = 'name=' + encodeURIComponent(currentName); 3327 if (navigator.sendBeacon){ 3328 navigator.sendBeacon(DISMISS_URL, new Blob([body], { type: 'application/x-www-form-urlencoded' })); 3329 } else { 3330 fetch(DISMISS_URL, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: body, keepalive: true }); 3331 } 3332 } catch (e) {} 3333 } 3334 currentName = null; 3335 }); 3336 3337 schedule(); 3338})(); 3339</script>
3339 3340</body> 3341</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.