Найден корректный robots.txt
Файл управления сканированием сайта роботами
?
Robots.txt указывает поисковым роботам, какие страницы сканировать, а какие — нет. Ошибки в файле могут случайно закрыть важные разделы от индексации.
Robots.txt настроен корректно. Размер файла: 33460 байт. Загружен за: 1сек.
Проверяемая страница не запрещена в robots.txt.
Показать содержимое robots.txt
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BirdingPal — Find a Birding Companion Anywhere</title>
<meta name="description" content="Connect with birders in any country. Find a local birding companion, discover hotspots, and plan your next birding adventure.">
<!-- Leaflet CSS -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" crossorigin="">
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.css" crossorigin="">
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.5.3/dist/MarkerCluster.Default.css" crossorigin="">
<!-- Tailwind (precompiled — see core_birdingpal.com/package.json's
build:css script to regenerate after adding a new utility class) -->
<link rel="stylesheet" href="/css/tailwind.build.css?v=1">
<!-- Corporate fonts: Lora (wordmark + headings) · Nunito (UI body) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,700;1,400&family=Nunito:wght@400;600;700;800&display=swap">
<!-- App Stylesheet (layout, components, brand) -->
<link rel="stylesheet" href="/css/main.css?v=2.7">
</head>
<body class="bg-stone-50 text-gray-800 antialiased">
<!-- =====================================================================
SITE HEADER (T-030: single sticky header)
One bar, sticky from page top. Whole inner area is the menu trigger
except for a 5px transparent strip along the bottom edge — the
"safety strip" that lets reaches for the dropdown below fall through
to the search widget instead of accidentally opening the menu.
===================================================================== -->
<header class="site-header" id="site-header">
<!-- The clickable trigger. Opens the site-nav modal on tap.
Logo (100px head-tilt animation) + "Birding Pal" wordmark
on the left, hamburger far right. -->
<button id="site-header-trigger" class="site-header-trigger" type="button"
aria-label="Open BirdingPal menu" aria-haspopup="true">
<img src="/i/logo.gif"
alt="BirdingPal bird logo"
class="site-header-logo"
onerror="this.style.display='none'; document.querySelector('.site-header-logo-fallback').style.display='inline-block'">
<span class="site-header-logo-fallback" aria-hidden="true" style="display:none">🐦</span>
<span class="site-header-wordmark">Birding Pal</span>
<span class="site-header-hamburger" aria-hidden="true">☰</span>
</button>
<!-- The 5px padding-bottom on .site-header (see main.css) IS the
safety strip. No element needed here — the strip is just the
trigger button stopping short of the bottom edge. -->
</header>
<!-- =====================================================================
SITE NAV MODAL (T-030: opens from header tap)
Backdrop overlay with a centered modal box. Each menu item carries
a one-line description — senior-friendly explanation of what it
does. Privacy reassurance line at the bottom.
Three exit paths: tap the X, tap the header again, tap the backdrop.
===================================================================== -->
<nav id="main-menu" class="site-nav" role="dialog" aria-modal="true" aria-label="BirdingPal menu">
<div class="site-nav-inner">
<button class="site-nav-close" type="button" aria-label="Close menu">×</button>
<ul class="site-nav-list">
<li>
<a href="/" class="site-nav-link" data-action="reset-map">
<span class="site-nav-icon">🏠</span>
<span class="site-nav-text">
<span class="site-nav-label">New Search</span>
<span class="site-nav-help">Start fresh — clear the map and pick a new place.</span>
</span>
</a>
</li>
<li>
<a href="javascript:void(0)" class="site-nav-link"
data-action="tab_changed" data-tab-name="Pals">
<span class="site-nav-icon">🐦</span>
<span class="site-nav-text">
<span class="site-nav-label">Find a Pal</span>
<span class="site-nav-help">Browse local birders for the place you're looking at.</span>
</span>
</a>
</li>
<li>
<a href="javascript:void(0)" class="site-nav-link"
data-action="tab_changed" data-tab-name="hotspots">
<span class="site-nav-icon">📍</span>
<span class="site-nav-text">
<span class="site-nav-label">Hotspots</span>
<span class="site-nav-help">The best birding spots in this area.</span>
</span>
</a>
</li>
<li>
<a href="javascript:void(0)" class="site-nav-link"
data-action="tab_changed" data-tab-name="events">
<span class="site-nav-icon">🗓</span>
<span class="site-nav-text">
<span class="site-nav-label">Events</span>
<span class="site-nav-help">Bird walks, festivals, and club meets nearby.</span>
</span>
</a>
</li>
<li class="site-nav-divider" aria-hidden="true"></li>
<li>
<a href="javascript:void(0)" class="site-nav-link"
data-action="tab_changed" data-tab-name="messages">
<span class="site-nav-icon">📬</span>
<span class="site-nav-text">
<span class="site-nav-label">My Messages</span>
<span class="site-nav-help">Your conversations with other Pals.</span>
</span>
</a>
</li>
<li>
<a href="javascript:void(0)" class="site-nav-link"
data-action="tab_changed" data-tab-name="meetings">
<span class="site-nav-icon">📅</span>
<span class="site-nav-text">
<span class="site-nav-label">My Meetings</span>
<span class="site-nav-help">Confirmed birding meetups on your calendar.</span>
</span>
</a>
</li>
<li>
<a href="javascript:void(0)" class="site-nav-link"
data-action="view_my_profile">
<span class="site-nav-icon">👤</span>
<span class="site-nav-text">
<span class="site-nav-label">Edit Profile</span>
<span class="site-nav-help">Change your name, photo, interests, or area.</span>
</span>
</a>
</li>
<li class="site-nav-divider" aria-hidden="true"></li>
<li>
<a href="javascript:void(0)" class="site-nav-link"
data-action="contact_admin">
<span class="site-nav-icon">✉️</span>
<span class="site-nav-text">
<span class="site-nav-label">Contact Us</span>
<span class="site-nav-help">Get help, send feedback, or report a problem.</span>
</span>
</a>
</li>
<li>
<a href="/about" class="site-nav-link">
<span class="site-nav-icon">📖</span>
<span class="site-nav-text">
<span class="site-nav-label">About BirdingPal</span>
<span class="site-nav-help">What this place is and why it exists.</span>
</span>
</a>
</li>
<li>
<a href="/privacy" class="site-nav-link">
<span class="site-nav-icon">🔒</span>
<span class="site-nav-text">
<span class="site-nav-label">Privacy</span>
<span class="site-nav-help">How we protect your data and your messages.</span>
</span>
</a>
</li>
<!-- Log Out — only visible when a Pal is signed in. The
.nav-item-auth-only / body.is-logged-in pair handles
visibility (set in main.css and ui/auth.js). -->
<li class="nav-item-auth-only">
<a href="javascript:void(0)" class="site-nav-link site-nav-link-logout"
data-action="logout">
<span class="site-nav-icon">🚪</span>
<span class="site-nav-text">
<span class="site-nav-label">Log Out</span>
<span class="site-nav-help">Sign out — your messages and meetings stay safe.</span>
</span>
</a>
</li>
</ul>
<p class="site-nav-privacy">
🛡️ Your real email is never shared. Messages stay inside BirdingPal.
</p>
</div>
</nav>
<!-- =====================================================================
EVENTS TICKER
Populated by populateTicker() in card_renderer.js.
Hidden by default (display:none); shown only when active events exist.
===================================================================== -->
<div id="ticker-wrapper" class="ticker-wrapper" style="display:none">
<div id="tickerContent" class="ticker-content"></div>
</div>
<!-- =====================================================================
APP LAYOUT: Two-column (search/map | results/corner)
===================================================================== -->
<div class="app-layout">
<!-- ==========================================
LEFT COLUMN: Search + Corner + Trivia (sticky on desktop)
========================================== -->
<aside class="left-col">
<!-- ─── SEARCH WIDGET ────────────────────────────────── -->
<!-- The whole header is clickable to toggle the body — the inner
button keeps its own data-action so accessibility tools that
discover buttons still work. closest('[data-action]') resolves
to the innermost match, so a tap on the button still fires
with the same intent. -->
<div class="widget" id="widget-search">
<div class="widget-header" data-action="toggle_widget" data-widget="search">
<span class="widget-label">🔍 Find Your Next Birding Spot</span>
<button class="widget-toggle-btn"
data-action="toggle_widget" data-widget="search"
aria-label="Toggle search panel">
<span id="toggle-icon-search">−</span>
</button>
</div>
<div class="widget-body" id="widget-body-search">
<!-- COUNTRY AUTOCOMPLETE — first thing a Pal sees; gets focus on load -->
<div class="autocomplete-wrap">
<input type="text"
id="country-search-input"
class="search-input"
placeholder="Type a country name..."
autocomplete="off"
spellcheck="false"
autofocus
aria-label="Search for a country"
aria-autocomplete="list"
aria-controls="autocomplete-results">
<div id="autocomplete-results"
class="autocomplete-dropdown hidden"
role="listbox"
aria-label="Country suggestions"></div>
</div>
<!-- COSMETIC PLACEHOLDER: grey "Country region..." hint that signals
to the Pal that sub-location dropdowns will appear here.
Disappears the moment a real country is selected (JS below). -->
<div id="cosmetic-region-placeholder" class="custom-select-wrapper">
<div class="custom-select-trigger cosmetic-select-trigger">
<span>Country region...</span>
<span class="cosmetic-select-arrow">▼</span>
</div>
</div>
<!-- LOCATION DRILLDOWN DROPDOWNS
continent-select-wrapper: populated by createContinentSelector()
country/region/district/city: shown/hidden by dropdowns.js
District + City appear as siblings under a region — a Pal
can pick either depending on how they think about a place. -->
<div class="drilldown-row">
<div id="continent-select-wrapper" class="custom-select-wrapper"></div>
<div id="country-select-wrapper" class="custom-select-wrapper hidden"></div>
<div id="region-select-wrapper" class="custom-select-wrapper hidden"></div>
<div id="district-select-wrapper" class="custom-select-wrapper hidden"></div>
<div id="city-select-wrapper" class="custom-select-wrapper hidden"></div>
</div>
<!-- CONVERSATIONAL SEARCH CARD — lives below the location dropdowns -->
<div id="agent-chat-card" class="pip-card">
<p class="pip-section-label">Try our assistant Pip.</p>
<div class="pip-card-header">
<div class="pip-avatar">🦜</div>
<div class="pip-intro">
<p class="pip-tagline">Your birding guide — ask me anything.</p>
</div>
</div>
<!-- Chat history — bubbles injected by appendChatBubble() -->
<div id="pip-chat-history" class="pip-history" aria-live="polite" aria-relevant="additions"></div>
<!-- Input row -->
<form id="pip-chat-form" class="pip-form" autocomplete="off">
<textarea id="main-query-box"
class="pip-input"
placeholder="Where do you want to go birding?"
rows="2"
aria-label="Search or ask a question"
style="width:100%;"></textarea>
</form>
<!-- Mode toggle: Search vs Write to a Pal -->
<div class="pip-mode-tabs" id="pip-mode-tabs">
<button type="button" class="pip-mode-tab active" id="pip-tab-search" data-mode="search">
🔍 Search
</button>
<button type="button" class="pip-mode-tab" id="pip-tab-write" data-mode="write">
✉️ Write to a Pal
</button>
</div>
</div>
</div><!-- /widget-body-search -->
</div><!-- /widget-search -->
<!-- ─── INTEREST MATCH WIDGET ────────────────────────── -->
<!-- HIDDEN PENDING T-024: filter UI is moving into the Pals tab
header alongside the location drill-down. Keeping the markup
commented (not deleted) so we can yank it out cleanly once
the new Pals-tab-header replacement ships. The init call
`initInterestFilter()` in app.ui.v1.js still runs harmlessly —
it no-ops when the container element isn't in the DOM.
<div class="widget" id="widget-interests">
<div class="widget-header">
<span class="widget-label">🎯 Interest Match</span>
<button class="widget-toggle-btn"
data-action="toggle_widget" data-widget="interests"
aria-label="Toggle Interest Match">
<span id="toggle-icon-interests">+</span>
</button>
</div>
<div class="widget-body collapsed" id="widget-body-interests">
<p class="interest-loading">Loading interests...</p>
</div>
</div>
-->
<!-- ─── YOUR CORNER WIDGET ───────────────────────────── -->
<!-- Starts collapsed everywhere. A guest taps the header to open the
login box (tap 1), submits credentials (tap 2). Once logged in
the widget gets the `.logged-in` class — corp-green header acts
as the at-a-glance "you are signed in" cue, even when collapsed. -->
<div class="widget" id="widget-corner">
<div class="widget-header" data-action="toggle_widget" data-widget="corner">
<span class="widget-label">👤 Your Corner</span>
<button class="widget-toggle-btn"
data-action="toggle_widget" data-widget="corner"
aria-label="Toggle Your Corner">
<span id="toggle-icon-corner">+</span>
</button>
</div>
<div class="widget-body collapsed" id="widget-body-corner">
<!-- updateYourCorner() in ui/auth.js populates this -->
<div id="quick-login-box" class="quick-login-box"></div>
</div>
</div><!-- /widget-corner -->
<!-- ─── COMMUNITY PHOTO WIDGET ─────────────────────────── -->
<div class="widget" id="widget-community-photo">
<div class="widget-header" data-action="toggle_widget" data-widget="community-photo">
<span class="widget-label">📸 Thanks for being a Pal</span>
<button class="widget-toggle-btn"
data-action="toggle_widget" data-widget="community-photo"
aria-label="Toggle photo">
<span id="toggle-icon-community-photo">−</span>
</button>
</div>
<div class="widget-body" id="widget-body-community-photo" style="padding:0;overflow:hidden;">
<img src="https://imagespeeder.com/bp3/NatureExplorers/SmilingBirdersFull.jpg"
alt="Smiling birders out in nature"
style="width:100%;display:block;border-radius:0 0 8px 8px;"
loading="lazy">
</div>
</div><!-- /widget-community-photo -->
<!-- ─── LEFT COLUMN MINI FOOTER ──────────────────────── -->
<!-- Sticky left col means the main footer is always far below on desktop.
This mini footer keeps About/Privacy/Terms/Contact in reach. -->
<div class="col-mini-footer">
<nav class="col-mini-footer-links">
<a href="#" data-action="open_main_menu">Menu</a>
<a href="/about">About</a>
<a href="/privacy">Privacy</a>
<a href="/terms">Terms</a>
<a href="#" data-action="contact_admin">Contact</a>
</nav>
<p class="col-mini-footer-copy">© <span data-current-year>2025</span> BirdingPal</p>
</div>
</aside><!-- /left-col -->
<!-- ==========================================
RIGHT COLUMN: Map + Content Tabs
========================================== -->
<main class="right-col">
<!-- ─── MAP WIDGET ────────────────────────────────────── -->
<!-- Map sits at the top of right-col so the tab bar and results
are immediately visible below on first load — no scrolling needed. -->
<div class="widget" id="widget-map">
<div class="widget-header" data-action="toggle_widget" data-widget="map">
<span class="widget-label">🗺️ World Map of Local Language Names</span>
<button class="widget-toggle-btn"
data-action="toggle_widget" data-widget="map"
aria-label="Toggle map">
<span id="toggle-icon-map">−</span>
</button>
</div>
<!-- #map-container is the collapsible body for the map widget.
The toggle_widget intent handles it as a special case. -->
<div id="map-container" class="map-container">
<!-- Static JPG placeholder: shown until Leaflet fully boots.
Clicking a continent label fires continent_clicked intent. -->
<div id="map-placeholder" class="map-placeholder">
<img src="/i/birdingpalmap.jpg"
alt="World map — click a continent to explore birding hotspots"
class="world-map-img"
draggable="false">
<!-- Continent hotspot buttons — coordinates tuned for the JPG layout.
These fire window.updateStage via the inline script below. -->
<button class="continent-spot" data-continent="North America" style="left:18%;top:30%">N. America</button>
<button class="continent-spot" data-continent="South America" style="left:22%;top:60%">S. America</button>
<button class="continent-spot" data-continent="Europe" style="left:47%;top:22%">Europe</button>
<button class="continent-spot" data-continent="Africa" style="left:49%;top:54%">Africa</button>
<button class="continent-spot" data-continent="Asia" style="left:65%;top:28%">Asia</button>
<button class="continent-spot" data-continent="Oceania" style="left:76%;top:65%">Oceania</button>
</div>
<!-- Reset map button — shown by loadContinentView(), hidden by resetMap().
Starts hidden (display:none). Positioned over the map top-left. -->
<button id="reset-map-btn"
class="reset-map-btn"
data-action="reset-map"
style="display:none"
title="Reset to world view"
aria-label="Reset map to world view">
← World
</button>
<!-- Island / territory pills (T-052). Populated by showIslandPills()
in app.map.v1.js when a country with off-map birding territories
is selected. Hidden by default; hidden again on resetMap(). -->
<div id="island-pills-bar" class="island-pills-bar hidden" aria-label="Notable territories"></div>
<!-- Leaflet renders here. initializePrimaryMap() in app.map.v1.js
calls L.map('map', ...) — this element must exist in the DOM. -->
<div id="map"></div>
</div><!-- /map-container -->
</div><!-- /widget-map -->
<!-- ─── MAIN CONTENT CANVAS ──────────────────────────── -->
<!-- Hidden on landing. Revealed by handleLocationSelection() once
the user picks a country, continent, or autocomplete result. -->
<div id="main-content-tabs" class="main-canvas hidden">
<!-- TAB BAR
The breadcrumb strip below this nav (#tab-breadcrumb)
carries the "where am I" cue for all three person-types
(map / type / dropdown) — replaces the older inline
#tab-location-label that lived inside this nav. -->
<nav class="tabs" role="tablist" aria-label="Content tabs">
<button class="tab tab-primary"
data-tab-name="hotspots"
role="tab"
aria-selected="false">Hotspots</button>
<button class="tab tab-primary tab-hero active"
data-tab-name="Pals"
role="tab"
aria-selected="true">Pals</button>
<button class="tab tab-primary"
data-tab-name="events"
role="tab"
aria-selected="false">Events</button>
</nav>
<!-- BREADCRUMB STRIP (T-032)
Shared across all tabs. Sits between the tab bar and
the result list. Populated by app.tabs.v1.js's
renderTabBreadcrumb() — pills for each level + a
back-arrow that walks state.locationHistory.
Hidden when no location is selected. -->
<div id="tab-breadcrumb" class="tab-breadcrumb hidden" aria-label="Current location"></div>
<!-- CARD LIST AREA
renderer.js renders Pal/Hotspot cards here.
renderer.js also places #loading-spinner-{type} here. -->
<div id="listContent" class="list-content">
<div id="loading-spinner-pals" class="spinner-wrap hidden">
<div class="spinner" aria-label="Loading pals..."></div>
</div>
<div id="loading-spinner-hotspots" class="spinner-wrap hidden">
<div class="spinner" aria-label="Loading hotspots..."></div>
</div>
</div>
</div><!-- /main-content-tabs -->
<!-- ─── BIRD TRIVIA WIDGET (Easter egg at the end of tabs) ──
Lives at the very bottom of right-col so a Pal who scrolls
past the results discovers the "Name This Bird" mini-game.
On mobile this lands just above the site footer naturally.
TriviaGame in ui/trivia_game.js renders into #bird-trivia-widget. -->
<div class="widget" id="widget-trivia">
<div class="widget-header" data-action="toggle_widget" data-widget="trivia">
<span class="widget-label">🐦 Name This Bird</span>
<button class="widget-toggle-btn"
data-action="toggle_widget" data-widget="trivia"
aria-label="Toggle bird trivia">
<span id="toggle-icon-trivia">−</span>
</button>
</div>
<div class="widget-body" id="widget-body-trivia">
<div id="bird-trivia-widget"></div>
</div>
</div><!-- /widget-trivia -->
<!-- ─── RIGHT COLUMN MINI FOOTER ────────────────────── -->
<!-- Symmetric to left-col's mini-footer (T-040, 2026-05-04).
On desktop both columns scroll independently and the
page-level <footer> is hidden, so each column carries its
own legal links — always within reach without scrolling
past every Pal card to the bottom of the world. Hidden on
mobile (.col-mini-footer { display: none } in section 25)
because the site-footer is right there in the linear flow. -->
<div class="col-mini-footer">
<nav class="col-mini-footer-links">
<a href="#" data-action="open_main_menu">Menu</a>
<a href="/about">About</a>
<a href="/privacy">Privacy</a>
<a href="/terms">Terms</a>
<a href="#" data-action="contact_admin">Contact</a>
</nav>
<p class="col-mini-footer-copy">© <span data-current-year>2025</span> BirdingPal</p>
</div>
</main><!-- /right-col -->
</div><!-- /app-layout -->
<!-- =====================================================================
SITE FOOTER
Spans full width outside the two-column grid.
===================================================================== -->
<footer class="site-footer">
<div class="site-footer-inner">
<a href="/" class="site-footer-logo">
<img src="/i/birdingpallogos/BirdingPal_Logo_001_nhb.jpg" alt="BirdingPal" style="height:28px;width:auto;">
</a>
<nav class="site-footer-links">
<a href="/about">About</a>
<a href="/privacy">Privacy</a>
<a href="/terms">Terms</a>
<a href="#" data-action="contact_admin">Contact</a>
</nav>
<p class="site-footer-copy">© <span data-current-year>2025</span> BirdingPal — Find a birding companion, anywhere.</p>
</div>
</footer>
<!-- =====================================================================
EXPANDED MAP MODAL
Leaflet map element is moved here by expandMap() in app.map.v1.js
===================================================================== -->
<div id="map-modal" class="map-modal hidden" aria-hidden="true" role="dialog" aria-label="Full-screen map">
<div id="modal-map-container" class="map-modal-inner"></div>
</div>
<!-- =====================================================================
GLOBAL MODAL
showModal() / hideModal() in ui/modal.js toggle this element.
Initial state is display:none (set inline so it's reliable before CSS loads).
===================================================================== -->
<div id="app-modal" class="app-modal" style="display:none" role="dialog" aria-modal="true" aria-labelledby="modal-title">
<div class="app-modal-box">
<button id="modal-close-btn" class="modal-close-btn" aria-label="Close dialog">×</button>
<div class="app-modal-header">
<h2 id="modal-title" class="modal-title-text"></h2>
</div>
<div id="modal-body" class="modal-body-content"></div>
</div>
</div>
<!-- =====================================================================
SCRIPTS
Order matters:
1. Leaflet must load before any module that references L.*
2. MarkerCluster depends on Leaflet
3. Continent spot wiring (non-module inline, runs at DOMContentLoaded)
4. App entry point (type="module", deferred by browser — runs last)
===================================================================== -->
<!-- Leaflet core -->
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" crossorigin=""></script>
<!-- MarkerCluster plugin -->
<script src="https://unpkg.com/leaflet.markercluster@1.5.3/dist/leaflet.markercluster.js" crossorigin=""></script>
<!-- Wire the JPG map continent hotspot buttons.
window.updateStage is set by app.stage_manager.v1.js via the global bridge
(window.updateStage = updateStage) — it is available by the time the user
can click anything, since modules execute before DOMContentLoaded fires. -->
<script>
document.addEventListener('DOMContentLoaded', function () {
// Continent hotspots → updateStage intent.
document.querySelectorAll('.continent-spot').forEach(function (btn) {
btn.addEventListener('click', function () {
var continentName = btn.dataset.continent;
if (typeof window.updateStage === 'function') {
window.updateStage('continent_clicked', { name: continentName });
}
});
});
// Stamp the current year into every footer's copyright line.
// Each <span data-current-year>YYYY</span> in the markup keeps a
// hard-coded fallback so a JS-disabled visitor still sees a date.
var yearText = String(new Date().getFullYear());
document.querySelectorAll('[data-current-year]').forEach(function (el) {
el.textContent = yearText;
});
// Cosmetic region placeholder: vanishes the moment the real country
// dropdown appears (country-select-wrapper loses its 'hidden' class),
// reappears if the selection is reset back to continent-only.
var cosmeticPlaceholder = document.getElementById('cosmetic-region-placeholder');
var countrySelectWrapper = document.getElementById('country-select-wrapper');
if (cosmeticPlaceholder && countrySelectWrapper) {
new MutationObserver(function () {
if (countrySelectWrapper.classList.contains('hidden')) {
cosmeticPlaceholder.classList.remove('hidden');
} else {
cosmeticPlaceholder.classList.add('hidden');
}
}).observe(countrySelectWrapper, { attributes: true, attributeFilter: ['class'] });
}
// Belt-and-suspenders focus: autofocus attr handles most cases;
// this fires after modules have settled to catch any edge cases.
setTimeout(function () {
var countryInput = document.getElementById('country-search-input');
if (countryInput) countryInput.focus();
}, 100);
});
</script>
<!-- App Entry Point — ES Module (browser defers automatically) -->
<script type="module" src="/js/app.ui.v1.js?v=13"></script>
</body>
</html>