Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"> | |
<meta http-equiv="Pragma" content="no-cache"> | |
<meta http-equiv="Expires" content="0"> | |
<title>TreeTrack Map - Interactive Field View</title> | |
<link rel="icon" type="image/png" href="/static/image/icons8-tree-96.png"> | |
<link rel="apple-touch-icon" href="/static/image/icons8-tree-96.png"> | |
<!-- Leaflet CSS --> | |
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" /> | |
<link rel="stylesheet" href="/static/css/design-system.css"> | |
<style> | |
:root { | |
/* Colors */ | |
--primary-50: #eff6ff; | |
--primary-100: #dbeafe; | |
--primary-500: #3b82f6; | |
--primary-600: #2563eb; | |
--primary-700: #1d4ed8; | |
--primary-900: #1e3a8a; | |
--gray-50: #f8fafc; | |
--gray-100: #f1f5f9; | |
--gray-200: #e2e8f0; | |
--gray-300: #cbd5e1; | |
--gray-400: #94a3b8; | |
--gray-500: #64748b; | |
--gray-600: #475569; | |
--gray-700: #334155; | |
--gray-800: #1e293b; | |
--gray-900: #0f172a; | |
--green-50: #f0fdf4; | |
--green-500: #22c55e; | |
--green-600: #16a34a; | |
--red-50: #fef2f2; | |
--red-500: #ef4444; | |
--red-600: #dc2626; | |
--orange-500: #f97316; | |
--orange-600: #ea580c; | |
/* Spacing */ | |
--space-1: 0.25rem; | |
--space-2: 0.5rem; | |
--space-3: 0.75rem; | |
--space-4: 1rem; | |
--space-5: 1.25rem; | |
--space-6: 1.5rem; | |
--space-8: 2rem; | |
/* Radius */ | |
--radius-sm: 0.375rem; | |
--radius-md: 0.5rem; | |
--radius-lg: 0.75rem; | |
--radius-xl: 1rem; | |
--radius-2xl: 1.5rem; | |
/* Shadows */ | |
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); | |
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); | |
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); | |
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); | |
} | |
* { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
} | |
body { | |
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; | |
font-feature-settings: 'cv11' 1, 'ss01' 1; | |
line-height: 1.6; | |
color: var(--gray-800); | |
background: var(--gray-50); | |
height: 100vh; | |
overflow: hidden; | |
-webkit-font-smoothing: antialiased; | |
-moz-osx-font-smoothing: grayscale; | |
} | |
.app-container { | |
height: 100vh; | |
display: flex; | |
flex-direction: column; | |
} | |
/* Map-specific header styling with Granim */ | |
.map-header { | |
position: relative; | |
overflow: hidden; | |
} | |
/* Ensure header content is above background */ | |
.tt-header-content { | |
position: relative; | |
z-index: 2; | |
} | |
.header-stats { | |
display: flex; | |
align-items: center; | |
gap: var(--space-4); | |
} | |
.stat-item { | |
display: flex; | |
align-items: center; | |
gap: var(--space-2); | |
padding: var(--space-3) var(--space-4); | |
background: var(--glass-bg-dark); | |
border-radius: var(--radius-xl); | |
border: var(--glass-border); | |
backdrop-filter: var(--glass-blur); | |
transition: all var(--transition-normal); | |
} | |
.stat-item:hover { | |
background: rgba(255, 255, 255, 0.2); | |
transform: translateY(-1px); | |
} | |
.stat-icon { | |
font-size: var(--text-lg); | |
width: 24px; | |
height: 24px; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
background: rgba(255, 255, 255, 0.2); | |
border-radius: var(--radius-md); | |
} | |
.stat-text { | |
font-size: var(--text-sm); | |
font-weight: var(--font-medium); | |
opacity: 0.9; | |
} | |
.stat-number { | |
font-size: var(--text-lg); | |
font-weight: var(--font-bold); | |
color: var(--primary-100); | |
} | |
.header-actions { | |
display: flex; | |
align-items: center; | |
gap: var(--space-3); | |
} | |
/* Map-specific adjustments for user info */ | |
.map-user-info { | |
padding: var(--space-3) var(--space-5); | |
} | |
/* Button styles handled by design system */ | |
/* Map Container */ | |
.map-container { | |
flex: 1; | |
position: relative; | |
overflow: hidden; | |
} | |
#map { | |
width: 100%; | |
height: 100%; | |
z-index: 1; | |
} | |
/* Controls Panel */ | |
.controls-panel { | |
position: absolute; | |
top: var(--space-4); | |
left: var(--space-4); | |
z-index: 1000; | |
background: rgba(255, 255, 255, 0.95); | |
backdrop-filter: blur(12px); | |
border-radius: var(--radius-xl); | |
border: 1px solid var(--gray-200); | |
box-shadow: var(--shadow-lg); | |
overflow: hidden; | |
} | |
.controls-header { | |
padding: var(--space-4); | |
border-bottom: 1px solid var(--gray-100); | |
} | |
.controls-title { | |
font-size: 0.875rem; | |
font-weight: 600; | |
color: var(--gray-900); | |
margin: 0; | |
} | |
.controls-content { | |
padding: var(--space-3); | |
} | |
.control-group { | |
display: flex; | |
flex-direction: column; | |
gap: var(--space-2); | |
} | |
.control-button { | |
background: var(--gray-50); | |
color: var(--gray-700); | |
border: 1px solid var(--gray-200); | |
padding: var(--space-3) var(--space-4); | |
border-radius: var(--radius-md); | |
font-size: 0.875rem; | |
font-weight: 500; | |
cursor: pointer; | |
transition: all 0.15s ease; | |
display: flex; | |
align-items: center; | |
gap: var(--space-2); | |
} | |
.control-button:hover { | |
background: var(--gray-100); | |
border-color: var(--gray-300); | |
transform: translateY(-1px); | |
box-shadow: var(--shadow-sm); | |
} | |
.control-button.active { | |
background: var(--primary-50); | |
color: var(--primary-700); | |
border-color: var(--primary-500); | |
} | |
/* Location Panel */ | |
.location-panel { | |
position: absolute; | |
bottom: var(--space-4); | |
left: var(--space-4); | |
right: var(--space-4); | |
z-index: 1000; | |
background: rgba(255, 255, 255, 0.95); | |
backdrop-filter: blur(12px); | |
border-radius: var(--radius-xl); | |
border: 1px solid var(--gray-200); | |
box-shadow: var(--shadow-lg); | |
transform: translateY(100%); | |
transition: transform 0.3s ease; | |
} | |
.location-panel.active { | |
transform: translateY(0); | |
} | |
.location-header { | |
padding: var(--space-4); | |
border-bottom: 1px solid var(--gray-100); | |
} | |
.location-title { | |
font-size: 1rem; | |
font-weight: 600; | |
color: var(--gray-900); | |
margin: 0; | |
} | |
.location-content { | |
padding: var(--space-4); | |
} | |
.coordinates-grid { | |
display: grid; | |
grid-template-columns: 1fr 1fr; | |
gap: var(--space-4); | |
margin-bottom: var(--space-4); | |
} | |
.coord-item { | |
text-align: center; | |
} | |
.coord-label { | |
font-size: 0.75rem; | |
font-weight: 500; | |
color: var(--gray-600); | |
margin-bottom: var(--space-1); | |
text-transform: uppercase; | |
letter-spacing: 0.05em; | |
} | |
.coord-value { | |
font-size: 1.125rem; | |
font-weight: 700; | |
color: var(--primary-600); | |
font-variant-numeric: tabular-nums; | |
} | |
.location-actions { | |
display: grid; | |
grid-template-columns: 1fr 1fr; | |
gap: var(--space-3); | |
} | |
/* Ensure buttons are visible in location panel */ | |
.location-actions .tt-btn { | |
width: 100%; | |
padding: var(--space-3) var(--space-4); | |
border-radius: var(--radius-md); | |
font-size: 0.875rem; | |
font-weight: 500; | |
cursor: pointer; | |
transition: all 0.15s ease; | |
border: 1px solid transparent; | |
text-align: center; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
min-height: 44px; | |
} | |
.location-actions .tt-btn-primary { | |
background: var(--primary-600); | |
color: white; | |
border-color: var(--primary-600); | |
} | |
.location-actions .tt-btn-primary:hover { | |
background: var(--primary-700); | |
border-color: var(--primary-700); | |
transform: translateY(-1px); | |
} | |
.location-actions .tt-btn-secondary { | |
background: var(--gray-100); | |
color: var(--gray-700); | |
border-color: var(--gray-300); | |
} | |
.location-actions .tt-btn-secondary:hover { | |
background: var(--gray-200); | |
border-color: var(--gray-400); | |
transform: translateY(-1px); | |
} | |
/* Messages */ | |
.message { | |
position: absolute; | |
top: var(--space-4); | |
left: 50%; | |
transform: translateX(-50%); | |
padding: var(--space-3) var(--space-6); | |
border-radius: var(--radius-lg); | |
font-size: 0.875rem; | |
font-weight: 500; | |
z-index: 1500; | |
opacity: 0; | |
transition: opacity 0.3s ease; | |
pointer-events: none; | |
} | |
.message.show { | |
opacity: 1; | |
} | |
.message.success { | |
background: var(--green-50); | |
color: var(--green-600); | |
border: 1px solid var(--green-500); | |
} | |
.message.error { | |
background: var(--red-50); | |
color: var(--red-600); | |
border: 1px solid var(--red-500); | |
} | |
.message.info { | |
background: var(--primary-50); | |
color: var(--primary-600); | |
border: 1px solid var(--primary-500); | |
} | |
/* Loading */ | |
.loading { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
background: rgba(255, 255, 255, 0.95); | |
backdrop-filter: blur(8px); | |
padding: var(--space-6) var(--space-8); | |
border-radius: var(--radius-xl); | |
z-index: 2000; | |
display: flex; | |
align-items: center; | |
gap: var(--space-3); | |
box-shadow: var(--shadow-lg); | |
border: 1px solid var(--gray-200); | |
} | |
.spinner { | |
border: 2px solid var(--gray-200); | |
border-top: 2px solid var(--primary-500); | |
border-radius: 50%; | |
width: 1.5rem; | |
height: 1.5rem; | |
animation: spin 1s linear infinite; | |
} | |
@keyframes spin { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
.loading-text { | |
color: var(--gray-700); | |
font-size: 0.875rem; | |
font-weight: 500; | |
} | |
/* Custom Leaflet Overrides */ | |
.leaflet-control-container { | |
display: none; | |
} | |
.leaflet-popup-content-wrapper { | |
background: white; | |
border-radius: var(--radius-lg); | |
box-shadow: var(--shadow-xl); | |
border: 1px solid var(--gray-200); | |
position: relative; | |
} | |
.leaflet-popup-tip { | |
background: white; | |
border: 1px solid var(--gray-200); | |
} | |
.leaflet-popup-content { | |
margin: 0; | |
line-height: 1.5; | |
overflow: hidden; | |
} | |
/* Fix popup close button positioning */ | |
.leaflet-popup-close-button { | |
position: absolute; | |
top: 8px; | |
right: 8px; | |
z-index: 10; | |
background: rgba(255, 255, 255, 0.9); | |
color: var(--gray-600); | |
border: 1px solid var(--gray-300); | |
border-radius: 50%; | |
width: 24px; | |
height: 24px; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
font-size: 14px; | |
line-height: 1; | |
cursor: pointer; | |
transition: all 0.15s ease; | |
text-decoration: none; | |
font-weight: 400; | |
box-shadow: var(--shadow-sm); | |
} | |
.leaflet-popup-close-button:hover { | |
background: white; | |
color: var(--gray-800); | |
border-color: var(--gray-400); | |
box-shadow: var(--shadow-md); | |
transform: scale(1.05); | |
} | |
.leaflet-popup-close-button:focus { | |
outline: 2px solid var(--primary-500); | |
outline-offset: 2px; | |
} | |
/* Tree Popup Content */ | |
.tree-popup { | |
padding: var(--space-4); | |
min-width: 280px; | |
} | |
.tree-popup-header { | |
display: flex; | |
align-items: center; | |
gap: var(--space-3); | |
margin-bottom: var(--space-3); | |
padding-bottom: var(--space-3); | |
border-bottom: 1px solid var(--gray-100); | |
} | |
.tree-icon { | |
font-size: 2rem; | |
color: var(--green-500); | |
} | |
.tree-popup-title { | |
flex: 1; | |
} | |
.tree-name { | |
font-size: 1rem; | |
font-weight: 600; | |
color: var(--gray-900); | |
margin: 0; | |
} | |
.tree-scientific { | |
font-size: 0.75rem; | |
color: var(--gray-600); | |
font-style: italic; | |
margin-top: var(--space-1); | |
} | |
.tree-popup-details { | |
display: grid; | |
gap: var(--space-2); | |
margin-bottom: var(--space-3); | |
} | |
.detail-row { | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
font-size: 0.8125rem; | |
} | |
.detail-label { | |
color: var(--gray-600); | |
font-weight: 500; | |
} | |
.detail-value { | |
color: var(--gray-900); | |
font-weight: 600; | |
} | |
.tree-popup-actions { | |
display: grid; | |
grid-template-columns: 1fr 1fr auto; | |
gap: var(--space-2); | |
} | |
.btn-icon { | |
background: transparent; | |
border: 1px solid var(--gray-300); | |
color: var(--gray-600); | |
padding: var(--space-2); | |
border-radius: var(--radius-md); | |
cursor: pointer; | |
transition: all 0.15s ease; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
} | |
.btn-icon:hover { | |
background: var(--gray-50); | |
border-color: var(--gray-400); | |
} | |
.btn-icon.edit:hover { | |
background: var(--primary-50); | |
border-color: var(--primary-500); | |
color: var(--primary-600); | |
} | |
.btn-icon.delete:hover { | |
background: var(--red-50); | |
border-color: var(--red-500); | |
color: var(--red-600); | |
} | |
/* Mobile Optimizations */ | |
@media (max-width: 768px) { | |
.header-content { | |
padding: var(--space-3) var(--space-4); | |
} | |
.header-stats { | |
gap: var(--space-3); | |
} | |
.stat-item { | |
padding: var(--space-1) var(--space-3); | |
} | |
.stat-text { | |
display: none; | |
} | |
.logo { | |
font-size: 1.25rem; | |
} | |
.controls-panel { | |
top: var(--space-3); | |
left: var(--space-3); | |
} | |
.location-panel { | |
bottom: var(--space-3); | |
left: var(--space-3); | |
right: var(--space-3); | |
} | |
.coordinates-grid { | |
grid-template-columns: 1fr; | |
gap: var(--space-3); | |
} | |
.location-actions { | |
grid-template-columns: 1fr; | |
} | |
.user-details { | |
display: none; | |
} | |
.tree-popup { | |
min-width: 260px; | |
padding: var(--space-3); | |
} | |
.tree-popup-actions { | |
grid-template-columns: 1fr; | |
gap: var(--space-2); | |
} | |
} | |
/* Gesture Hint */ | |
.gesture-hint { | |
position: absolute; | |
bottom: var(--space-16); | |
left: 50%; | |
transform: translateX(-50%); | |
background: rgba(0, 0, 0, 0.8); | |
color: white; | |
padding: var(--space-3) var(--space-6); | |
border-radius: var(--radius-xl); | |
font-size: 0.875rem; | |
font-weight: 500; | |
z-index: 1000; | |
animation: fadeInOut 4s ease-in-out; | |
pointer-events: none; | |
} | |
@keyframes fadeInOut { | |
0%, 100% { opacity: 0; } | |
25%, 75% { opacity: 1; } | |
} | |
/* Custom Map Markers */ | |
.map-marker { | |
position: relative; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
transition: transform 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55), filter 0.2s ease; | |
cursor: pointer; | |
} | |
/* Realistic Tree Markers */ | |
.realistic-tree:hover { | |
transform: scale(1.1) translateY(-2px); | |
filter: drop-shadow(3px 6px 12px rgba(93, 127, 73, 0.4)) brightness(1.1); | |
} | |
.realistic-tree svg { | |
transition: all 0.3s ease; | |
} | |
.realistic-tree:hover svg { | |
transform: rotate(-1deg); | |
} | |
/* Temp Marker Animation */ | |
.realistic-tree-temp { | |
animation: gentle-pulse 2.5s ease-in-out infinite; | |
} | |
.realistic-tree-temp:hover { | |
transform: scale(1.15) translateY(-3px); | |
filter: drop-shadow(3px 6px 12px rgba(185, 28, 28, 0.5)) brightness(1.1); | |
animation: none; | |
} | |
@keyframes gentle-pulse { | |
0%, 100% { | |
opacity: 1; | |
transform: scale(1) translateY(0px); | |
} | |
50% { | |
opacity: 0.85; | |
transform: scale(1.03) translateY(-1px); | |
} | |
} | |
/* User Location Marker */ | |
.user-marker { | |
filter: drop-shadow(0 3px 8px rgba(59, 130, 246, 0.4)); | |
} | |
.user-marker:hover { | |
transform: scale(1.1); | |
filter: drop-shadow(0 5px 15px rgba(59, 130, 246, 0.6)); | |
} | |
/* Enhanced marker clustering effect */ | |
.leaflet-marker-icon.realistic-tree { | |
z-index: 1000; | |
} | |
.leaflet-marker-icon.realistic-tree-temp { | |
z-index: 1100; | |
} | |
/* Smooth marker entrance animation */ | |
.leaflet-marker-icon { | |
animation: marker-appear 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55); | |
} | |
@keyframes marker-appear { | |
0% { | |
opacity: 0; | |
transform: scale(0) translateY(20px); | |
} | |
100% { | |
opacity: 1; | |
transform: scale(1) translateY(0px); | |
} | |
} | |
/* Tree Tooltip Improvements */ | |
.tree-tooltip { | |
background: rgba(255, 255, 255, 0.95) ; | |
border: 1px solid var(--gray-200) ; | |
border-radius: var(--radius-md) ; | |
box-shadow: var(--shadow-lg) ; | |
backdrop-filter: blur(8px); | |
font-size: 0.8125rem; | |
padding: var(--space-2) var(--space-3) ; | |
max-width: 200px; | |
} | |
.tree-tooltip-content { | |
text-align: center; | |
} | |
.tree-name { | |
font-weight: 600; | |
color: var(--gray-900); | |
margin-bottom: var(--space-1); | |
} | |
.tree-details { | |
font-size: 0.75rem; | |
color: var(--gray-600); | |
} | |
.tree-creator { | |
font-size: 0.7rem; | |
color: var(--gray-500); | |
font-style: italic; | |
} | |
/* Performance optimizations */ | |
.leaflet-tile-pane { | |
filter: none; | |
} | |
.leaflet-overlay-pane { | |
transform: translate3d(0, 0, 0); | |
} | |
/* Focus States */ | |
.btn:focus-visible, | |
.control-button:focus-visible, | |
.btn-icon:focus-visible { | |
outline: 2px solid var(--primary-500); | |
outline-offset: 2px; | |
} | |
</style> | |
<script> | |
// Force refresh if we detect cached version | |
(function() { | |
const currentVersion = '5.1.1'; | |
const timestamp = '1755116236'; // Current timestamp for cache busting | |
const lastVersion = sessionStorage.getItem('treetrack_version'); | |
const lastTimestamp = sessionStorage.getItem('treetrack_timestamp'); | |
if (!lastVersion || lastVersion !== currentVersion || !lastTimestamp || lastTimestamp !== timestamp) { | |
// Clear all storage to force fresh start | |
sessionStorage.clear(); | |
localStorage.removeItem('treetrack_cache'); | |
sessionStorage.setItem('treetrack_version', currentVersion); | |
sessionStorage.setItem('treetrack_timestamp', timestamp); | |
if (lastVersion && lastVersion !== currentVersion) { | |
// Force hard refresh | |
location.reload(true); | |
return; | |
} | |
} | |
})(); | |
</script> | |
</head> | |
<body> | |
<div class="app-container"> | |
<!-- Header --> | |
<div class="tt-header map-header" style="background-image: url('https://images.unsplash.com/photo-1441974231531-c6227db76b6e?q=80&w=1920&auto=format&fit=crop&ixlib=rb-4.0.3'); background-size: cover; background-position: center;"> | |
<div class="tt-header-content"> | |
<div class="tt-header-brand"> | |
<div class="tt-header-logo">TreeTrack Map</div> | |
<div class="tt-header-subtitle">Interactive Field Research View</div> | |
</div> | |
<div class="header-stats"> | |
<div class="stat-item"> | |
<span class="stat-icon">T</span> | |
<span class="stat-text">Trees:</span> | |
<span class="stat-number" id="treeCount">0</span> | |
</div> | |
</div> | |
<div class="tt-header-actions"> | |
<div class="tt-user-info map-user-info" id="userInfo"> | |
<div class="tt-user-avatar" id="userAvatar">U</div> | |
<div class="tt-user-details"> | |
<div class="tt-user-name" id="userName">Loading...</div> | |
<div class="tt-user-role" id="userRole">User</div> | |
</div> | |
</div> | |
<a href="/" class="tt-btn tt-btn-secondary">Add Tree</a> | |
<button id="logoutBtn" class="tt-btn tt-btn-secondary">Logout</button> | |
</div> | |
</div> | |
</div> | |
<!-- Map Container --> | |
<div class="map-container"> | |
<div id="map"></div> | |
<!-- Controls Panel --> | |
<div class="controls-panel"> | |
<div class="controls-header"> | |
<h3 class="controls-title">Map Controls</h3> | |
</div> | |
<div class="controls-content"> | |
<div class="control-group"> | |
<button id="myLocationBtn" class="control-button"> | |
<span>Loc</span> | |
<span>My Location</span> | |
</button> | |
<button id="clearPinsBtn" class="control-button"> | |
<span>Clr</span> | |
<span>Clear Pins</span> | |
</button> | |
<button id="centerMapBtn" class="control-button"> | |
<span>Ctr</span> | |
<span>Center View</span> | |
</button> | |
</div> | |
</div> | |
</div> | |
<!-- Location Panel --> | |
<div class="location-panel" id="locationPanel"> | |
<div class="location-header"> | |
<h3 class="location-title">Location Selected</h3> | |
</div> | |
<div class="location-content"> | |
<div class="coordinates-grid"> | |
<div class="coord-item"> | |
<div class="coord-label">Latitude</div> | |
<div class="coord-value" id="latValue">--</div> | |
</div> | |
<div class="coord-item"> | |
<div class="coord-label">Longitude</div> | |
<div class="coord-value" id="lngValue">--</div> | |
</div> | |
</div> | |
<div class="location-actions"> | |
<button id="useLocationBtn" class="tt-btn tt-btn-primary"> | |
Use This Location | |
</button> | |
<button id="cancelBtn" class="tt-btn tt-btn-secondary"> | |
Cancel | |
</button> | |
</div> | |
</div> | |
</div> | |
<!-- Loading --> | |
<div id="loading" class="loading" style="display: none;"> | |
<div class="spinner"></div> | |
<div class="loading-text">Loading map...</div> | |
</div> | |
<!-- Messages --> | |
<div id="message" class="message"></div> | |
<!-- Gesture Hint --> | |
<div class="gesture-hint"> | |
Tap anywhere on the map to drop a location pin | |
</div> | |
</div> | |
</div> | |
<!-- Leaflet JS --> | |
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> | |
<script src="/static/map.js?v=5.1.1&t=1755116236"> | |
"default-state": { | |
gradients: [ | |
['#0f172a', '#1e293b', '#16a085'], | |
['#1a202c', '#2d3748', '#27ae60'], | |
['#2d3748', '#4a5568', '#2ecc71'], | |
['#1a365d', '#2c5282', '#138d75'], | |
['#0f172a', '#2d3748', '#16a085'] | |
], | |
transitionSpeed: 12000 | |
} | |
} | |
}); | |
// Defer any heavy data fetch; ensure map shell is visible first | |
if (window.requestAnimationFrame) { | |
requestAnimationFrame(() => setTimeout(() => { | |
try { if (window.MapApp && window.MapApp.loadVisibleTrees) { window.MapApp.loadVisibleTrees(); } } catch(_) {} | |
}, 0)); | |
} | |
}); | |
</script> | |
</body> | |
</html> | |