Period Navigator
· 1 year ago
d50dec47971250a0439206d03da7a7ad545e9dfa
Parent:
492e5d091
1 file changed +264 −83
- human-evolution.html +264 −83
Diff
--- a/human-evolution.html +++ b/human-evolution.html @@ -3,7 +3,7 @@ <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> - <title>Human Evolution: Journey Through Time</title> + <title>Human Evolution: Journey Through Time (Concise) - AWESOME Edition v3</title> <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" /> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css" /> <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" /> @@ -62,9 +62,6 @@ margin: 0; overflow: hidden; background: var(--dark-bg); - /* Optional subtle noise texture for richness: - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cfilter id='n' x='0' y='0'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.75' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100' height='100' filter='url(%23n)' opacity='0.03'/%3E%3C/svg%3E"); - */ color: var(--text-color); display: flex; flex-direction: column; @@ -117,26 +114,10 @@ gap: 1rem; } - @media (max-width: 992px) { - #main-content { - flex-direction: column; - } - #controls-sidebar { - width: 100%; - max-height: 45vh; - border-right: none; - border-bottom: 1px solid var(--light-bg); - } - #map-container { - flex-grow: 1; - height: auto; - } - } - #map-container { flex: 1; position: relative; - background: #0a0a0a; /* Original, could be var(--dark-bg) too */ + background: #0a0a0a; } #map { @@ -186,7 +167,6 @@ line-height: 1.65; font-size: 0.85rem; } - /* Specific narrative text element to ensure it takes base body styling */ #narrative-panel .card-body p#narrative-text-element { line-height: 1.65; font-size: 0.85rem; @@ -264,7 +244,7 @@ filter var(--transition-speed) var(--transition-timing), box-shadow var(--transition-speed) var(--transition-timing); border: 1px solid var(--light-bg); - margin-top: 0.25rem; /* Retain original */ + margin-top: 0.25rem; } .timeline-buttons .btn i, #reset-button i { @@ -272,14 +252,13 @@ transition: transform var(--transition-speed) var(--transition-timing); vertical-align: middle; } - .timeline-buttons .btn:hover i, /* Only scale icon if button itself is hovered */ + .timeline-buttons .btn:hover i, #reset-button:hover i { transform: scale(1.1); } - .timeline-buttons .btn-group { margin-top: 0.25rem; } /* Retain original */ + .timeline-buttons .btn-group { margin-top: 0.25rem; } - /* Primary styled buttons (Play, active speed, active quick filters) */ #play-pause-button, .timeline-buttons .speed-btn.active, .quick-filter-buttons .btn.active { @@ -295,22 +274,20 @@ box-shadow: 0 4px 12px var(--focus-ring-color); } - /* Outline styled buttons (inactive speed, inactive quick filters) */ .btn-outline-light { color: var(--text-muted-color); border-color: var(--light-bg); background-color: transparent; } .btn-outline-light:hover, - .btn-outline-light.active { /* .active might be added by bootstrap too */ - color: var(--text-heading-color); /* Text color for active/hover state */ - background-color: var(--primary-accent-static); /* Use static accent for hover of outline */ + .btn-outline-light.active { + color: var(--text-heading-color); + background-color: var(--primary-accent-static); border-color: var(--primary-accent-static); } - /* Ensure active quick filter buttons specifically use the gradient */ .quick-filter-buttons .btn.active, .timeline-buttons .speed-btn.active { - background: var(--primary-accent-gradient); /* Override general .active if needed */ + background: var(--primary-accent-gradient); border-color: transparent; color: var(--text-heading-color); } @@ -331,6 +308,30 @@ filter: brightness(110%); box-shadow: 0 4px 12px rgba(var(--danger-accent-rgb), 0.5); } + + /* Period Navigator Select Styling */ + #period-filter-select { + background-color: var(--dark-bg); + color: var(--text-color); + border: 1px solid var(--light-bg); + border-radius: var(--border-radius-pill); + padding: 0.375rem 2.25rem 0.375rem 0.75rem; + font-size: 0.8rem; + font-weight: var(--font-weight-medium); + transition: border-color var(--transition-speed) var(--transition-timing), + box-shadow var(--transition-speed) var(--transition-timing); + } + #period-filter-select:focus { + border-color: var(--primary-accent-static); + box-shadow: 0 0 0 0.25rem var(--focus-ring-color); + background-color: var(--dark-bg); + color: var(--text-color); + } + #period-filter-select option { + background-color: var(--medium-bg); + color: var(--text-color); + } + #species-filter-list { max-height: 180px; @@ -356,7 +357,7 @@ border-bottom: none; } #species-filter-list .form-check-label { - font-size: 0.8rem; /* Overridden by general p styling, but kept for specificity if needed */ + font-size: 0.8rem; cursor: pointer; margin-left: 0.5rem; color: var(--text-color); @@ -402,8 +403,6 @@ } #narrative-panel .card-body { - /* font-size: 0.8rem; Original, now handled by p#narrative-text-element */ - /* line-height: 1.5; Original */ color: var(--text-color); overflow-y: auto; } @@ -411,19 +410,19 @@ color: var(--timeline-period-color); font-weight: var(--font-weight-medium); } - #narrative-text-element { /* Ensured by specific p rule above */ + #narrative-text-element { color: var(--text-color) !important; } .data-limitation-note { color: var(--warning-accent); font-size: 0.78rem; - border-top: 1px dashed var(--light-bg); /* Original had this, now it's a bg box */ padding-top: 0.6rem; margin-top: 0.75rem; background-color: rgba(var(--warning-accent-rgb), 0.05); padding: 0.5rem; border-radius: 4px; + border-top: none; /* Removed original dashed top border as it's a box now */ } .data-limitation-note i { margin-right: 0.35rem; @@ -467,7 +466,6 @@ display: flex; align-items: center; margin-bottom: 0.3rem; - /* font-size: 0.78rem; Handled by general p/span rule */ padding: 0.1rem 0; } .legend-color-box { @@ -490,8 +488,6 @@ display: flex; align-items: center; justify-content: center; - /* color: white; Handled by p */ - /* font-size: 1.2rem; Handled by p */ z-index: 9999; flex-direction: column; } @@ -525,18 +521,18 @@ right: 10px; background: var(--medium-bg); padding: 0.4rem 0.6rem; - border-radius: var(--border-radius-standard); /* Updated radius */ + border-radius: var(--border-radius-standard); z-index: 1000; - box-shadow: var(--modern-shadow); /* Updated shadow */ + box-shadow: var(--modern-shadow); border: 1px solid var(--light-bg); } .climate-toggle-container .form-check-label { - font-size: 0.75rem; /* Original */ + font-size: 0.75rem; color: var(--text-muted-color); } .climate-toggle-container .form-check-input { - border-color: var(--primary-accent-static); /* Use static for consistency */ - background-color: var(--medium-bg); /* Ensure it's not transparent */ + border-color: var(--primary-accent-static); + background-color: var(--medium-bg); } .climate-toggle-container .form-check-input:checked { background-color: var(--primary-accent-static); @@ -549,8 +545,8 @@ background-color: var(--medium-bg) !important; color: var(--text-color) !important; border: 1px solid var(--light-bg) !important; - border-radius: var(--border-radius-standard) !important; /* Softer radius */ - box-shadow: var(--card-element-shadow) !important; /* Subtle shadow */ + border-radius: var(--border-radius-standard) !important; + box-shadow: var(--card-element-shadow) !important; transition: background-color var(--transition-speed) var(--transition-timing) !important; } .leaflet-control-zoom-in:hover, @@ -566,12 +562,12 @@ border: 1px solid var(--light-bg); border-radius: var(--border-radius-standard); } - .leaflet-popup-content-wrapper { padding: 1px; } /* Leaflet default behavior */ - .leaflet-popup-content { /* Inner content area */ + .leaflet-popup-content-wrapper { padding: 1px; } + .leaflet-popup-content { padding: 0.8rem 1rem; font-size: 0.85rem; line-height: 1.6; - margin: 0 !important; /* Override Leaflet's p margin */ + margin: 0 !important; } .leaflet-popup-content h5 { margin-top: 0; @@ -590,17 +586,38 @@ .leaflet-container a.leaflet-popup-close-button:hover { color: var(--danger-accent); } + .popup-species-image { + max-height: 100px; + width: auto; + display: block; + margin-left: auto; + margin-right: auto; + border: 1px solid var(--light-bg); + border-radius: var(--border-radius-standard); /* Match other radii */ + } + .popup-learn-more { + color: var(--primary-accent-static); + text-decoration: none; + font-weight: var(--font-weight-medium); + } + .popup-learn-more:hover { + color: var(--primary-accent-end); + text-decoration: underline; + } + .popup-learn-more i { + font-size: 0.8em; + margin-left: 0.2em; + } /* Focus States */ *:focus-visible { outline: 2px solid var(--focus-ring-color); outline-offset: 2px; - /* box-shadow: 0 0 0 3px rgba(var(--focus-ring-color-rgb), 0.5); Requires focus-ring-color-rgb */ } - #species-filter-list .form-check-input:focus-visible { + #species-filter-list .form-check-input:focus-visible, + #period-filter-select:focus-visible { /* Add period filter to custom focus */ box-shadow: 0 0 0 2px var(--dark-bg), 0 0 0 4px var(--focus-ring-color); } - /* Ensure tooltips are styled for the new theme if they appear */ .tooltip-inner { background-color: var(--primary-accent-static); color: var(--text-heading-color); @@ -609,18 +626,126 @@ font-size: 0.75rem; } .tooltip .tooltip-arrow::before { - border-top-color: var(--primary-accent-static) !important; /* Example for top tooltip */ - border-bottom-color: var(--primary-accent-static) !important; /* Example for bottom tooltip */ + border-top-color: var(--primary-accent-static) !important; + border-bottom-color: var(--primary-accent-static) !important; border-left-color: var(--primary-accent-static) !important; border-right-color: var(--primary-accent-static) !important; } + /* Responsive Adjustments */ + @media (max-width: 992px) { + #main-content { + flex-direction: column; + } + #controls-sidebar { + width: 100%; + max-height: 45vh; /* Original */ + border-right: none; + border-bottom: 1px solid var(--light-bg); + } + #map-container { + flex-grow: 1; + height: auto; + } + } + + @media (max-width: 767.98px) { + :root { + /* --sidebar-width: 100%; Already handled by .controls-sidebar in this query */ + } + + header.app-header h1 { font-size: 1.3rem; } + header.app-header h1 i { margin-right: 0.5rem; } + + #controls-sidebar { + padding: 0.75rem; + gap: 0.75rem; + max-height: 50vh; + } + + .control-card .card-header { font-size: 0.8rem; padding: 0.5rem 0.75rem; } + .control-card .card-body { padding: 0.6rem 0.75rem; } + + #current-period-display { font-size: 1.1rem; } + #timeline-value-display { font-size: 0.7rem; margin-bottom: 0.5rem; } + #timeline-scrubber { margin: 0.5rem 0; } + #timeline-scrubber::-webkit-slider-thumb { width: 18px; height: 18px; } + #timeline-scrubber::-moz-range-thumb { width: 18px; height: 18px; } + + + .timeline-buttons .btn, + .quick-filter-buttons .btn, + #reset-button { padding: 0.3rem 0.75rem; font-size: 0.75rem; } + .timeline-buttons .btn i, #reset-button i { margin-right: 0.25rem; } + + #period-filter-select { font-size: 0.75rem; padding-top: 0.3rem; padding-bottom: 0.3rem; } + + #species-filter-list .form-check-label { font-size: 0.75rem; } + #species-filter-list .form-check-input { width: 1em; height: 1em; } + #species-filter-list .form-check-input:checked::before { font-size: 0.7em; } + .species-color-indicator { width: 10px; height: 10px; } + + + #narrative-panel .card-body, + #narrative-panel .card-body p#narrative-text-element, + .data-limitation-note { font-size: 0.75rem; } + + .legend { padding: 0.5rem 0.75rem; max-height: 150px; } + .legend h5 { font-size: 0.8rem; } + .legend-item { font-size: 0.7rem; } + .legend-color-box { width: 10px; height: 10px; } + + .leaflet-popup-content { padding: 0.6rem 0.8rem; font-size: 0.75rem; } + .leaflet-popup-content h5 { font-size: 1em; } + } + + @media (max-width: 575.98px) { + header.app-header { padding: 0 1rem; } + header.app-header h1 { font-size: 1.15rem; } + header.app-header h1 i { font-size: 0.9em; } + + + #controls-sidebar { padding: 0.5rem; gap: 0.5rem; } + + .control-card .card-header { font-size: 0.75rem; padding: 0.4rem 0.6rem; } + .control-card .card-header i { font-size: 0.85em; } + .control-card .card-body { padding: 0.5rem 0.6rem; } + .control-card .card-header + .card-body { padding-top: 0.75rem; } + + + #current-period-display { font-size: 1rem; } + + .timeline-buttons { flex-wrap: wrap; gap: 0.25rem; } + .timeline-buttons .btn, + .timeline-buttons .btn-group, /* Target group directly for basis */ + #reset-button { /* Play, Group, Reset */ + flex-grow: 1; /* Allow them to grow */ + flex-basis: auto; /* Reset basis or set to a meaningful min-content */ + } + /* Make all three items (Play button, Speed group, Reset button) share space */ + .d-flex.justify-content-between.align-items-center.mt-2.timeline-buttons > * { + flex-basis: calc(33.333% - 0.2rem); /* Approx 1/3 width, accounting for gap */ + min-width: 80px; /* Prevent them from becoming too small */ + } + .timeline-buttons .btn-group { display: flex; } + .timeline-buttons .btn-group .btn { flex-grow: 1; } + + + .quick-filter-buttons .btn { font-size: 0.7rem; padding: 0.25rem 0.5rem; } + + #species-filter-list { max-height: 120px; } + + #narrative-panel .card-body, + #narrative-panel .card-body p#narrative-text-element, + .data-limitation-note { font-size: 0.7rem; } + .data-limitation-note i { display: none; } + } + </style> </head> <body> <div id="loading-screen" class="loading-overlay"> - <!-- Spinner is now CSS-only via ::before on loading-overlay --> <p class="mt-2 mb-0">Loading Evolution Data...</p> </div> <header class="app-header"> @@ -636,7 +761,7 @@ <span id="timeline-value-display"></span> <input type="range" - class="form-range" /* Bootstrap class, custom styles will override */ + class="form-range" id="timeline-scrubber" min="0" value="0" @@ -647,9 +772,10 @@ <div class="d-flex justify-content-between align-items-center mt-2 timeline-buttons"> <button id="play-pause-button" - class="btn btn-sm" /* Removed btn-primary, style handled by ID */ + class="btn btn-sm" disabled data-bs-toggle="tooltip" + data-bs-placement="top" title="Play or Pause animation" > <i class="bi bi-play-fill"></i> <span>Play</span> @@ -666,7 +792,7 @@ </div> <button id="reset-button" - class="btn btn-sm" /* Removed btn-danger, style handled by ID */ + class="btn btn-sm" disabled data-bs-toggle="tooltip" title="Reset timeline to start" @@ -676,6 +802,19 @@ </div> </div> </div> + + <!-- NEW: Period Navigation Card --> + <div class="control-card card"> + <div class="card-header"><i class="bi bi-collection-play"></i> Period Navigator</div> + <div class="card-body"> + <label for="period-filter-select" class="form-label visually-hidden">Select a Time Period</label> + <select class="form-select" id="period-filter-select" data-bs-toggle="tooltip" title="Jump to a specific period"> + <option selected disabled value="">Select a period...</option> + <!-- Options will be populated by JavaScript --> + </select> + </div> + </div> + <div class="control-card card"> <div class="card-header"><i class="bi bi-people-fill"></i> Species Filter</div> <div class="card-body"> @@ -777,13 +916,15 @@ narrativeTextElement: null, loadingScreen: null, controlsContentDiv: null, - climateToggle: null + climateToggle: null, + periodFilterSelect: null // Added for Period Navigator }; function populateDomElements() { const idMap = { controlsContentDiv: 'controls-content', - climateToggle: 'climate-toggle-switch' + climateToggle: 'climate-toggle-switch', + periodFilterSelect: 'period-filter-select' // Added }; for (const key in domElements) { if (key === 'playPauseIcon' || key === 'playPauseText') continue; @@ -843,6 +984,29 @@ } } + function populatePeriodFilter() { + if (!domElements.periodFilterSelect || !timePeriodsData || timePeriodsData.length === 0) { + if (domElements.periodFilterSelect) domElements.periodFilterSelect.disabled = true; + return; + } + domElements.periodFilterSelect.disabled = false; + domElements.periodFilterSelect.innerHTML = ''; // Clear existing options + + const defaultOption = document.createElement('option'); + defaultOption.textContent = 'Select a period...'; + defaultOption.value = ""; + defaultOption.disabled = true; + // defaultOption.selected = true; // Will be handled by updateUIForCurrentPeriod setting the correct index + domElements.periodFilterSelect.appendChild(defaultOption); + + timePeriodsData.forEach((period, index) => { + const option = document.createElement('option'); + option.value = index; + option.textContent = period.periodName || `Period ${index + 1}`; + domElements.periodFilterSelect.appendChild(option); + }); + } + function initializeApplication() { if (!domElements.loadingScreen || !domElements.controlsContentDiv) { @@ -855,7 +1019,7 @@ domElements.controlsContentDiv.classList.remove('d-none'); Object.values(domElements).forEach(el => { - if (el && typeof el.disabled === 'boolean') { + if (el && el.id !== 'period-filter-select' && typeof el.disabled === 'boolean') { // Don't enable period filter select here el.disabled = false; } }); @@ -865,10 +1029,11 @@ populateAllSpeciesList(); populateSpeciesFilter(); + populatePeriodFilter(); // Populate period filter currentPeriodIndex = 0; - updateSpeedButtonsActiveState(); // Includes ensuring 1x is active - updateQuickFilterActiveState('filter-all'); // Set 'All' as active by default - updateUIForCurrentPeriod(); + updateSpeedButtonsActiveState(); + updateQuickFilterActiveState('filter-all'); + updateUIForCurrentPeriod(); // This will now also set periodFilterSelect const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); @@ -907,7 +1072,7 @@ input.addEventListener('change', (event) => { selectedSpecies[event.target.value] = event.target.checked; updateUIForCurrentPeriod(); - updateQuickFilterActiveState(null); // Clear All/None/Homo if individual changes + updateQuickFilterActiveState(null); }); const colorIndicator = document.createElement('span'); colorIndicator.className = 'species-color-indicator'; @@ -915,11 +1080,10 @@ const label = document.createElement('label'); label.className = 'form-check-label'; label.htmlFor = input.id; - // label.style.color = 'var(--text-color)'; // Set by global CSS label.textContent = s.name; itemDiv.appendChild(input); - itemDiv.appendChild(colorIndicator); // Swapped order with label for better alignment with custom checkbox + itemDiv.appendChild(colorIndicator); itemDiv.appendChild(label); domElements.speciesFilterList.appendChild(itemDiv); }); @@ -936,6 +1100,7 @@ domElements.narrativeTextElement.textContent = "End of timeline data."; } if(domElements.legendItemsContainer) domElements.legendItemsContainer.innerHTML = '<small class="text-muted">No active period.</small>'; + if(domElements.periodFilterSelect) domElements.periodFilterSelect.value = ""; // Select placeholder return; } @@ -943,6 +1108,9 @@ if(domElements.currentPeriodDisplay) domElements.currentPeriodDisplay.textContent = period.periodName || "Unknown Period"; if(domElements.timelineValueDisplay) domElements.timelineValueDisplay.textContent = period.timeRange || `Period ${currentPeriodIndex + 1}`; if (domElements.timelineScrubber) domElements.timelineScrubber.value = currentPeriodIndex; + if (domElements.periodFilterSelect) { // Set period filter select + domElements.periodFilterSelect.value = currentPeriodIndex; + } if(domElements.narrativeTextElement) { domElements.narrativeTextElement.style.color = 'var(--text-color)'; @@ -956,14 +1124,20 @@ if (s && s.name && selectedSpecies[s.name]) { visibleSpeciesInLegend.push(s); + // ENHANCED POPUP CONTENT + let learnMoreLink = s.learnMoreUrl ? `<p class="mb-1 mt-1 small"><a href="${s.learnMoreUrl}" target="_blank" rel="noopener noreferrer" class="popup-learn-more">Learn More <i class="bi bi-box-arrow-up-right"></i></a></p>` : ''; + let speciesImage = s.imageUrl ? `<img src="${s.imageUrl}" alt="${s.name}" class="img-fluid rounded mb-2 popup-species-image">` : ''; + let popupContent = `<h5>${s.name}</h5> + ${speciesImage} <p class="mb-1 small"><strong>Timeline:</strong> ${s.timeline || 'N/A'}</p> - <p class="mb-0 small">${s.notes || 'No specific notes.'}</p>`; + <p class="mb-0 small">${s.notes || 'No specific notes.'}</p> + ${learnMoreLink}`; if (s.estimatedRangeGeoJson && Object.keys(s.estimatedRangeGeoJson).length > 0) { try { L.geoJSON(s.estimatedRangeGeoJson, { - style: { // AWESOME EDITION STYLE + style: { fillColor: s.color || '#888888', weight: 1.5, opacity: 1, @@ -979,7 +1153,7 @@ if (path && Object.keys(path).length > 0) { try { L.geoJSON(path, { - style: { // AWESOME EDITION STYLE + style: { color: s.migrationColor || s.color || '#888888', weight: 3, opacity: 0.75, @@ -1009,7 +1183,6 @@ const colorBox = document.createElement('span'); colorBox.className = 'legend-color-box'; colorBox.style.backgroundColor = s.color || '#cccccc'; const nameSpan = document.createElement('span'); nameSpan.textContent = s.name; - // nameSpan.style.color = 'var(--text-color)'; // Set by global CSS itemDiv.appendChild(colorBox); itemDiv.appendChild(nameSpan); domElements.legendItemsContainer.appendChild(itemDiv); }); @@ -1055,7 +1228,7 @@ if (sheet && sheet.type && sheet.coordinates) { try { L.geoJSON(sheet, { - style: { // AWESOME EDITION STYLE + style: { color: 'rgba(173, 216, 230, 0.5)', fillColor: '#A2D1E6', fillOpacity: 0.35, @@ -1080,7 +1253,6 @@ } } - // --- Animation & Controls --- function animationLoop(timestamp) { if (!isPlaying) return; @@ -1130,6 +1302,7 @@ pauseAnimation(); currentPeriodIndex = 0; if (domElements.timelineScrubber) domElements.timelineScrubber.value = 0; + // periodFilterSelect will be updated by updateUIForCurrentPeriod playbackSpeed = 1; updateSpeedButtonsActiveState(); if (allSpeciesList && allSpeciesList.length > 0) { @@ -1140,37 +1313,35 @@ cb.checked = true; }); } - updateQuickFilterActiveState('filter-all'); // Reset to 'All' - updateUIForCurrentPeriod(); + updateQuickFilterActiveState('filter-all'); + updateUIForCurrentPeriod(); // This will sync periodFilterSelect } function updateSpeedButtonsActiveState() { document.querySelectorAll('.speed-btn').forEach(btn => { btn.classList.remove('active'); - btn.classList.add('btn-outline-light'); // Ensure outline is re-applied + btn.classList.add('btn-outline-light'); if (parseInt(btn.dataset.speed) === playbackSpeed) { btn.classList.add('active'); - btn.classList.remove('btn-outline-light'); // Remove outline for active state + btn.classList.remove('btn-outline-light'); } }); } - // Function to update active state for quick filter buttons function updateQuickFilterActiveState(activeButtonId) { document.querySelectorAll('.quick-filter-buttons .btn').forEach(btn => { btn.classList.remove('active'); - btn.classList.add('btn-outline-light'); // Ensure outline is re-applied + btn.classList.add('btn-outline-light'); }); if (activeButtonId) { const activeButton = document.getElementById(activeButtonId); if (activeButton) { activeButton.classList.add('active'); - activeButton.classList.remove('btn-outline-light'); // Remove outline for active state + activeButton.classList.remove('btn-outline-light'); } } } - // --- Event Listeners --- function addEventListeners() { if (!domElements.timelineScrubber || !domElements.playPauseButton || !domElements.resetButton) { console.error("Cannot add event listeners: Core DOM elements for controls not found."); @@ -1223,9 +1394,19 @@ } }); } + + if (domElements.periodFilterSelect) { // Event listener for Period Navigator + domElements.periodFilterSelect.addEventListener('change', (event) => { + const selectedIndex = parseInt(event.target.value); + if (!isNaN(selectedIndex) && selectedIndex >= 0 && selectedIndex < timePeriodsData.length) { + currentPeriodIndex = selectedIndex; + pauseAnimation(); + updateUIForCurrentPeriod(); + } + }); + } } - // --- Initial Load --- document.addEventListener('DOMContentLoaded', () => { populateDomElements(); addEventListeners();