<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
  <!-- Google tag (gtag.js) -->
  <script async src="https://www.googletagmanager.com/gtag/js?id=G-DS75SRCRQJ"></script>
  <script>
    window.dataLayer = window.dataLayer || [];
    function gtag(){dataLayer.push(arguments);}
    gtag('js', new Date());

    gtag('config', 'G-DS75SRCRQJ');
  </script>
  <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8260356444253845"
  crossorigin="anonymous"></script>
  <title>Presidential Location</title>
  <!-- Styles -->
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    }
    body {
      background: #f8f9fa;
      padding: 0;
      margin: 0;
    }
    .top-bar {
      background-color: #264edc;
      padding: 10px;
      text-align: center;
    }
    .top-bar-link {
      color: #ffffff;
      text-decoration: none;
      font-size: 14px;
      font-weight: 500;
    }
    .container {
      max-width: 1200px;
      margin: 0 auto;
      position: relative;
      padding: 20px;
    }
    #title {
      text-align: center;
      font-size: 28px;
      font-weight: 300;
      color: #1a1a1a;
      margin: 8px 0;
      letter-spacing: -0.5px;
    }
    #subtitle {
      text-align: center;
      font-size: 14px;
      font-weight: 200;
      color: #555555;
      margin: 0 0 40px;
      letter-spacing: -0.5px;
    }
    #map {
      height: 600px;
      width: 100%;
      position: relative;
    }
    .map-container {
      position: relative;
      width: 100%;
      height: 600px;
    }
    .fade-overlay {
      position: absolute;
      pointer-events: none;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      z-index: 1000;
      background:
        linear-gradient(to right, rgba(248, 249, 250, 1), transparent 15% 85%, rgba(248, 249, 250, 1)),
        linear-gradient(to bottom, rgba(248, 249, 250, 1), transparent 15% 85%, rgba(248, 249, 250, 1));
    }
    .control-group {
      width: 100%;
      padding: 20px;
      display: flex;
      align-items: center;
      gap: 12px;
      margin-top: 20px;
    }
    .slider-container {
      flex-grow: 1;
      display: flex;
      align-items: center;
      gap: 12px;
    }
    .slider-label {
      font-size: 14px;
      color: #6b7280;
      min-width: 50px;
      text-align: center;
    }
    .slider {
      flex-grow: 1;
      height: 4px;
      -webkit-appearance: none;
      appearance: none;
      background: #e5e7eb;
      outline: none;
      border-radius: 2px;
    }
    .slider::-webkit-slider-thumb {
      -webkit-appearance: none;
      appearance: none;
      width: 16px;
      height: 16px;
      background: #fff;
      border: 2px solid #264edc;
      border-radius: 50%;
      cursor: pointer;
      transition: transform 0.1s ease;
    }
    .slider::-moz-range-thumb {
      width: 16px;
      height: 16px;
      background: #fff;
      border: 2px solid #264edc;
      border-radius: 50%;
      cursor: pointer;
      transition: transform 0.1s ease;
    }
    .slider::-webkit-slider-thumb:hover {
      transform: scale(1.1);
    }
    .slider::-moz-range-thumb:hover {
      transform: scale(1.1);
    }
    .footer {
      margin-top: 40px;
      margin-bottom: 25px;
      text-align: center;
      font-size: 12px;
      color: #555555;
    }
    .schedule {
      margin-top: 40px;
      padding: 20px;
      background: #ffffff;
      border-radius: 8px;
      box-shadow: 0 1px 3px rgba(0,0,0,0.1);
    }
    .schedule-date {
      font-size: 18px;
      font-weight: 500;
      color: #1a1a1a;
      margin: 20px 0 10px;
      padding-bottom: 10px;
      border-bottom: 1px solid #e5e7eb;
    }
    .schedule-item {
      display: flex;
      align-items: flex-start;
      gap: 15px;
      padding: 10px 0;
    }
    .schedule-marker {
      width: 12px;
      height: 12px;
      background: #888888;
      border: 2px solid #ffffff;
      border-radius: 50%;
      margin-top: 4px;
      box-shadow: 0 0 0 2px #888888;
    }
    .schedule-content {
      flex-grow: 1;
    }
    .schedule-time {
      font-size: 14px;
      color: #6b7280;
      margin-bottom: 4px;
    }
    .schedule-details {
      font-size: 14px;
      color: #1a1a1a;
      line-height: 1.4;
    }
    .marker-latest {
      position: relative;
    }
    .marker-latest::before {
      content: '';
      position: absolute;
      width: 12px;
      height: 12px;
      background: #264edc;
      border: 2px solid #ffffff;
      border-radius: 50%;
      transform: translate(-50%, -50%);
    }
    .marker-latest-ring {
      position: absolute;
      width: 12px;
      height: 12px;
      background: transparent;
      border: 2px solid #264edc;
      border-radius: 50%;
      transform: translate(-50%, -50%);
      animation: pulse-map 2s infinite;
    }
    .marker-default {
      position: relative;
    }
    .marker-default::before {
      content: '';
      position: absolute;
      width: 12px;
      height: 12px;
      background: #888;
      border: 2px solid #fff;
      border-radius: 50%;
      transform: translate(-50%, -50%);
    }
    .marker-selected::before {
      width: 14px;
      height: 14px;
      background: #888;
    }
    .marker-selected-ring {
      position: absolute;
      width: 14px;
      height: 14px;
      background: transparent;
      border: 2px solid #888;
      border-radius: 50%;
      transform: translate(-50%, -50%);
      animation: pulse-map 2s infinite;
    }
    .marker-departure {
      position: relative;
    }
    .marker-departure::before {
      content: '';
      position: absolute;
      width: 6px;
      height: 6px;
      background: #264edc;
      border: 1px solid #ffffff;
      border-radius: 50%;
      transform: translate(-50%, -50%);
    }
    @keyframes pulse-map {
      0% {
        width: 12px;
        height: 12px;
        opacity: 1;
        border-width: 2px;
      }
      100% {
        width: 40px;
        height: 40px;
        opacity: 0;
        border-width: 1px;
      }
    }
    .leaflet-control-attribution {
      font-size: 10px;
      background: rgba(255, 255, 255, 0.8) !important;
      backdrop-filter: blur(4px);
      -webkit-backdrop-filter: blur(4px);
      border-radius: 4px;
    }
    .leaflet-marker-icon.leaflet-interactive {
      margin: 0px !important;
    }
  </style>
  <!-- Leaflet CSS -->
  <link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
</head>
<body>
  <div class="top-bar">
    <a href="faq.html" class="top-bar-link">The Presidential Tracker is now in beta! Click here to read the brief FAQ on safety and features.</a>
  </div>
  <div class="container">
    <div id="title"></div>
    <div id="subtitle"></div>
    <div class="map-container">
      <div id="map"></div>
      <div class="fade-overlay"></div>
    </div>
    <div class="control-group">
      <div class="slider-container">
        <span class="slider-label">-</span>
        <input type="range" min="2" max="18" value="4" class="slider" id="zoom-slider">
        <span class="slider-label">+</span>
      </div>
    </div>
    <div class="control-group">
      <div class="slider-container">
        <span class="slider-label">Oldest</span>
        <input type="range" min="0" max="0" value="0" class="slider" id="time-slider">
        <span class="slider-label">Newest</span>
      </div>
    </div>
    <div class="schedule" id="schedule"></div>
  </div>

  <footer class="footer">
    <p>President Location Tracker is owned and operated by <a href="https://lukewin.es" target="_blank" rel="noopener noreferrer">Luke Wines</a>.</p>
    <p>Portions of schedule data provided by <a href="https://rollcall.com/" target="_blank" rel="noopener noreferrer">Roll Call.</a></p>
    <p>Map data &copy; <a href="https://www.openstreetmap.org/copyright" target="_blank" rel="noopener noreferrer">OpenStreetMap</a></p>
  </footer>
  <!-- Leaflet JS -->
  <script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
  <script>
    // Icon classes
    const LatestIcon = L.DivIcon.extend({
      createIcon: function(oldIcon) {
        const div = L.DivIcon.prototype.createIcon.call(this, oldIcon);
        const ring = document.createElement('div');
        ring.className = 'marker-latest-ring';
        div.appendChild(ring);
        return div;
      }
    });
    const DepartureIcon = L.DivIcon.extend({
      createIcon: function(oldIcon) {
        const div = L.DivIcon.prototype.createIcon.call(this, oldIcon);
        return div;
      }
    });
    const GreyIcon = L.DivIcon.extend({});
    const SelectedGreyIcon = L.DivIcon.extend({
      createIcon: function(oldIcon) {
        const div = L.DivIcon.prototype.createIcon.call(this, oldIcon);
        const ring = document.createElement('div');
        ring.className = 'marker-selected-ring';
        div.appendChild(ring);
        return div;
      }
    });

    // Icon instances
    const latestIcon = new LatestIcon({
      className: 'marker-latest',
      iconSize: [12, 12],
      iconAnchor: [6, 6]
    });
    const departureIcon = new DepartureIcon({
      className: 'marker-departure',
      iconSize: [6, 6],
      iconAnchor: [3, 3]
    });
    const greyIcon = new GreyIcon({
      className: 'marker-default',
      iconSize: [12, 12],
      iconAnchor: [6, 6]
    });
    const selectedGreyIcon = new SelectedGreyIcon({
      className: 'marker-default marker-selected',
      iconSize: [14, 14],
      iconAnchor: [7, 7]
    });

    // Location logic
    const onTheLocations = ['Grounds'];
    const inLocations = [', '];
    const inTheLocations = ['Room', 'Office', 'House', 'Building', 'Apartment', 'Lobby'];
    const atTheLocations = ['Museum', 'Library', 'Hospital', 'Hotel'];
    const atLocations = [
      'Park','Beach','Entrance','Mall','Airport','Station','Central Park',
      'Base','Medical Center','Hospital','School'
    ];

    function determinePreposition(location) {
      const locLower = location.toLowerCase();
      for (let loc of onTheLocations) {
        if (locLower.includes(loc.toLowerCase())) {
          return 'on the';
        }
      }
      for (let loc of inTheLocations) {
        if (locLower.includes(loc.toLowerCase())) {
          return 'in the';
        }
      }
      for (let loc of atTheLocations) {
        if (locLower.includes(loc.toLowerCase())) {
          return 'at the';
        }
      }
      for (let loc of inLocations) {
        if (locLower.includes(loc.toLowerCase())) {
          return 'in';
        }
      }
      for (let loc of atLocations) {
        if (locLower.includes(loc.toLowerCase())) {
          return 'at';
        }
      }
      return 'at';
    }

    function getGreatCirclePath(latlng1, latlng2, steps = 100) {
      const lat1 = latlng1.lat * Math.PI / 180;
      const lon1 = latlng1.lng * Math.PI / 180;
      const lat2 = latlng2.lat * Math.PI / 180;
      const lon2 = latlng2.lng * Math.PI / 180;
      const d = 2 * Math.asin(Math.sqrt(
        Math.sin((lat2 - lat1)/2) ** 2 +
        Math.cos(lat1) * Math.cos(lat2) * Math.sin((lon2 - lon1)/2) ** 2
      ));
      const path = [];
      for (let i = 0; i <= steps; i++) {
        const f = i / steps;
        const A = Math.sin((1 - f) * d) / Math.sin(d);
        const B = Math.sin(f * d) / Math.sin(d);
        const x = A * Math.cos(lat1) * Math.cos(lon1) + B * Math.cos(lat2) * Math.cos(lon2);
        const y = A * Math.cos(lat1) * Math.sin(lon1) + B * Math.cos(lat2) * Math.sin(lon2);
        const z = A * Math.sin(lat1) + B * Math.sin(lat2);
        const lat = Math.atan2(z, Math.sqrt(x * x + y * y));
        const lon = Math.atan2(y, x);
        path.push([lat * 180 / Math.PI, lon * 180 / Math.PI]);
      }
      return path;
    }

    function animateCircleGrowth(circle, targetRadius, duration = 2000) {
      let startTime = null;
      function growCircle(timestamp) {
        if (!startTime) startTime = timestamp;
        const elapsed = timestamp - startTime;
        const fraction = Math.min(elapsed / duration, 1);
        const currentRadius = fraction * targetRadius;
        circle.setRadius(currentRadius);
        if (fraction < 1) {
          requestAnimationFrame(growCircle);
        }
      }
      requestAnimationFrame(growCircle);
    }
    function describeLocation(event) {
      if (event.fuzzed) {
        if (event.location_name.includes(",")){
          return `near ${event.location_name}`;
        } else {
          return `near the ${event.location_name}`;
        }
      } else {
        const preposition = determinePreposition(event.location_name || '');
        return `${preposition} ${event.location_name || 'an unknown location'}`;
      }
    }

    // Fetch schedule data
    fetch('https://potustracker.lukewin.es/api/schedule')
      .then(response => response.json())
      .then(data => {
        if (!data.events || data.events.length === 0) {
          throw new Error('No events available');
        }
        const events = data.events.slice().sort((a, b) => new Date(a.event_datetime_utc) - new Date(b.event_datetime_utc));
        const timeSlider = document.getElementById('time-slider');
        timeSlider.min = 0;
        timeSlider.max = events.length - 1;
        timeSlider.value = events.length - 1;
        const map = L.map('map', {
          center: [0, 0],
          zoom: 4,
          zoomControl: false,
          scrollWheelZoom: false,
          dragging: true,
          touchZoom: true
        });
        L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
          subdomains: 'abcd',
          maxZoom: 20
        }).addTo(map);
        L.tileLayer('https://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}{r}.png', {
          subdomains: 'abcd',
          maxZoom: 20,
          opacity: 0.7
        }).addTo(map);
        const polylineGroup = L.layerGroup().addTo(map);
        const flightPolylineGroup = L.layerGroup().addTo(map);
        let previousIndex = events.length - 1;
        const addedLines = [];
        const newestEvent = events[events.length - 1];

        // Latest marker
        if (newestEvent.in_flight === 1) {
          const marker = L.marker([newestEvent.latitude, newestEvent.longitude], {
            icon: departureIcon
          }).addTo(map);
          marker.bindPopup(newestEvent.event_details, { closeButton: false, autoClose: false });
        } else {
          if (newestEvent.fuzzed) {
            const fuzzMarker = L.marker([newestEvent.latitude, newestEvent.longitude], {
              icon: latestIcon
            }).addTo(map);
            fuzzMarker.bindPopup(newestEvent.event_details, { closeButton: false, autoClose: false });
            const fuzzCircle = L.circle([newestEvent.latitude, newestEvent.longitude], {
              radius: 0,
              color: '#264edc',
              fillColor: '#264edc',
              fillOpacity: 0.1,
              interactive: false
            }).addTo(map);
            animateCircleGrowth(fuzzCircle, 32187, 2000);
          } else {
            const newestMarker = L.marker([newestEvent.latitude, newestEvent.longitude], {
              icon: latestIcon
            }).addTo(map);
            newestMarker.bindPopup(newestEvent.event_details, { closeButton: false, autoClose: false });
          }
        }

        // Past markers
        const pastMarkers = [];
        const fuzzedCircles = [];
        for (let i = 0; i < events.length - 1; i++) {
          const evt = events[i];
          if (evt.fuzzed) {
            const marker = L.marker([evt.latitude, evt.longitude], {
              icon: greyIcon
            });
            pastMarkers.push(marker);
            fuzzedCircles.push(null);
          } else {
            const marker = L.marker([evt.latitude, evt.longitude], {
              icon: greyIcon
            });
            pastMarkers.push(marker);
            fuzzedCircles.push(null);
          }
        }

        // Update events
        function updateEvent(index) {
          const event = events[index];
          const lat = event.latitude;
          const lng = event.longitude;
          const eventDetails = event.event_details;
          const eventArrivalLocation = event.arrival_location_name || "an unknown location";
          const fetchedAt = event.event_datetime_utc;
          const localTime = convertUTCToLocalTime(fetchedAt);
          const localDate = convertUTCToLocalDate(fetchedAt);

          if (index === events.length - 1) {
            if (event.in_flight === 1) {
              document.getElementById('title').textContent =
                `The President is en route to ${eventArrivalLocation}`;
            } else {
              document.getElementById('title').textContent =
                `The President is ${describeLocation(event)}`;
            }
            if (isToday(fetchedAt)) {
              document.getElementById('subtitle').textContent = `as of ${localTime}`;
            } else {
              document.getElementById('subtitle').textContent = `as of ${localDate} at ${localTime}`;
            }
          } else {
            if (event.in_flight === 1) {
              document.getElementById('title').textContent =
                `The President was en route to ${eventArrivalLocation}`;
            } else {
              document.getElementById('title').textContent =
                `The President was ${describeLocation(event)}`;
            }
            if (isToday(fetchedAt)) {
              document.getElementById('subtitle').textContent = `at ${localTime}`;
            } else {
              document.getElementById('subtitle').textContent = `on ${localDate} at ${localTime}`;
            }
          }
          map.setView([lat, lng], map.getZoom());

          if (index < previousIndex) {
            if (event.in_flight === 1 && index + 1 < events.length) {
              const start = { lat: event.latitude, lng: event.longitude };
              const end = { lat: events[index + 1].latitude, lng: events[index + 1].longitude };
              const path = getGreatCirclePath(start, end, 100);
              const line = L.polyline(path, {
                color: '#8888',
                weight: 4,
                opacity: 0.7
              }).addTo(polylineGroup);
              addedLines.push(line);
            } else {
              const line = L.polyline([
                [event.latitude, event.longitude],
                [events[index + 1].latitude, events[index + 1].longitude]
              ], {
                color: '#8888',
                weight: 4,
                opacity: 0.7
              }).addTo(polylineGroup);
              addedLines.push(line);
            }
            if (pastMarkers[index]) {
              pastMarkers[index].addTo(map);
            }
            if (event.fuzzed && !fuzzedCircles[index]) {
              const circle = L.circle([event.latitude, event.longitude], {
                radius: 0,
                color: '#888',
                fillColor: '#888',
                fillOpacity: 0.1,
                interactive: false
              }).addTo(map);
              animateCircleGrowth(circle, 32187, 2000);
              fuzzedCircles[index] = circle;
            }
          } else if (index > previousIndex) {
            if (addedLines.length > 0) {
              const lineToRemove = addedLines.pop();
              polylineGroup.removeLayer(lineToRemove);
            }
            if (previousIndex < events.length - 1 && pastMarkers[previousIndex]) {
              map.removeLayer(pastMarkers[previousIndex]);
            }
            if (previousIndex < events.length - 1 && fuzzedCircles[previousIndex]) {
              map.removeLayer(fuzzedCircles[previousIndex]);
              fuzzedCircles[previousIndex] = null;
            }
          }
          previousIndex = index;
          pastMarkers.forEach((marker, idx) => {
            if (!marker) return;
            if (!events[idx].fuzzed) {
              if (idx === index) {
                marker.setIcon(selectedGreyIcon);
                marker.bindPopup(eventDetails, { closeButton: false, autoClose: false });
              } else {
                marker.setIcon(greyIcon);
                marker.closePopup();
              }
            }
          });
        }

        function isToday(utcTime) {
          const eventDate = new Date(utcTime);
          const now = new Date();
          return (
            eventDate.getFullYear() === now.getFullYear() &&
            eventDate.getMonth() === now.getMonth() &&
            eventDate.getDate() === now.getDate()
          );
        }
        function convertUTCToLocalTime(time) {
          const date = new Date(time);
          if (isNaN(date)) {
            return time;
          }
          const options = {
            hour: 'numeric',
            minute: 'numeric',
            hour12: true,
            timeZone: 'America/Los_Angeles',
            timeZoneName: 'short'
          };
          return date.toLocaleTimeString('en-US', options);
        }
        function convertUTCToLocalDate(time) {
          const date = new Date(time);
          if (isNaN(date)) {
            return '';
          }
          const options = {
            month: 'long',
            day: 'numeric',
            timeZone: 'America/Los_Angeles'
          };
          return date.toLocaleDateString('en-US', options);
        }
        function createSchedule(events) {
          const scheduleDiv = document.getElementById('schedule');
          scheduleDiv.innerHTML = '';
          let currentDate = null;
          const reversedEvents = events.slice().reverse();
          reversedEvents.forEach(event => {
            const eventDate = new Date(event.event_datetime_utc);
            const dateStr = eventDate.toLocaleDateString('en-US', {
              month: 'long',
              day: 'numeric',
              year: 'numeric',
              timeZone: 'America/Los_Angeles'
            });
            if (dateStr !== currentDate) {
              currentDate = dateStr;
              const dateHeader = document.createElement('div');
              dateHeader.className = 'schedule-date';
              dateHeader.textContent = dateStr;
              scheduleDiv.appendChild(dateHeader);
            }
            const item = document.createElement('div');
            item.className = 'schedule-item';
            const marker = document.createElement('div');
            marker.className = 'schedule-marker';
            const content = document.createElement('div');
            content.className = 'schedule-content';
            const time = document.createElement('div');
            time.className = 'schedule-time';
            time.textContent = eventDate.toLocaleTimeString('en-US', {
              hour: 'numeric',
              minute: 'numeric',
              hour12: true,
              timeZone: 'America/Los_Angeles'
            });
            const details = document.createElement('div');
            details.className = 'schedule-details';
            details.textContent = event.event_details;
            content.appendChild(time);
            content.appendChild(details);
            item.appendChild(marker);
            item.appendChild(content);
            scheduleDiv.appendChild(item);
          });
        }

        // Flight animations
        function startFlightAnimation(departureEvent) {
          const departureTime = new Date(departureEvent.event_datetime_utc).getTime();
          const arrivalTime = new Date(departureEvent.arrival_time).getTime();
          const currentTime = new Date().getTime();
          const totalFlightDuration = arrivalTime - departureTime;
          const elapsedTime = currentTime - departureTime;
          if (elapsedTime < 0 || elapsedTime > totalFlightDuration) {
            return;
          }
          const progress = elapsedTime / totalFlightDuration;
          const start = { lat: departureEvent.latitude, lng: departureEvent.longitude };
          const end = { lat: departureEvent.arrival_lat, lng: departureEvent.arrival_lng };
          const fullPath = getGreatCirclePath(start, end, 100);
          const currentIndex = Math.floor(progress * (fullPath.length - 1));
          const currentPosition = fullPath[currentIndex];
          let flightCurrentPolyline = null;
          let flightMovingMarker = null;
          function drawPartialPath(idx) {
            if (flightCurrentPolyline) {
              flightPolylineGroup.removeLayer(flightCurrentPolyline);
            }
            const partialPath = fullPath.slice(0, idx + 1);
            flightCurrentPolyline = L.polyline(partialPath, {
              color: '#264edc',
              weight: 4,
              opacity: 0.9
            }).addTo(flightPolylineGroup);
          }
          drawPartialPath(currentIndex);
          if (!flightMovingMarker) {
            flightMovingMarker = L.marker(currentPosition, {
              icon: latestIcon
            }).addTo(map);
          } else {
            flightMovingMarker.setLatLng(currentPosition);
          }
          let flightAnimationInterval = setInterval(() => {
            const now = new Date().getTime();
            const newElapsedTime = now - departureTime;
            const newProgress = newElapsedTime / totalFlightDuration;
            if (newProgress >= 1) {
              clearInterval(flightAnimationInterval);
              flightPolylineGroup.removeLayer(flightCurrentPolyline);
              map.removeLayer(flightMovingMarker);
              return;
            }
            const newIndex = Math.floor(newProgress * (fullPath.length - 1));
            drawPartialPath(newIndex);
            flightMovingMarker.setLatLng(fullPath[newIndex]);
          }, 1000);
        }

        createSchedule(events);
        updateEvent(events.length - 1);
        if (newestEvent.in_flight === 1) {
          startFlightAnimation(newestEvent);
        }
        timeSlider.addEventListener('input', (e) => {
          const index = parseInt(e.target.value);
          updateEvent(index);
        });
        const zoomSlider = document.getElementById('zoom-slider');
        zoomSlider.min = map.getMinZoom();
        zoomSlider.max = map.getMaxZoom();
        zoomSlider.value = map.getZoom();
        zoomSlider.addEventListener('input', (e) => {
          map.setZoom(parseInt(e.target.value));
        });
        map.on('zoomend', () => {
          zoomSlider.value = map.getZoom();
        });
      })
      .catch(err => console.error(err));
  </script>
</body>
</html>