<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
  <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
  <link rel="manifest" href="/site.webmanifest">
  <!-- 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>
  <link href="https://unpkg.com/iconoir@latest/css/iconoir.css" rel="stylesheet" />
  <title>Presidential Location</title>
  <style>
    /* Global resets */
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    }
    body {
      background: #f8f9fa;
    }
    /* Top bar styling */
    .top-bar {
      background-color: #264edc;
      padding: 10px;
      display: flex;
      align-items: center;
      justify-content: center; /* Centering text */
    }
    .top-bar-link {
      color: #ffffff;
      text-decoration: none;
      font-size: 14px;
      font-weight: 500;
    }
    .donate-button {
      color: #ffffff;
      background-color: #ff6600;
      padding: 8px 16px;
      border-radius: 4px;
      text-decoration: none;
      font-weight: bold;
      transition: opacity 0.1s ease;
    }
    .donate-button:hover {
      opacity: 0.9;
    }
    /* Container styling */
    .container {
      max-width: 1200px;
      margin: 0 auto;
      position: relative;
      padding: 20px;
    }
    .map-legislation-wrapper {
      display: flex;
      gap: 20px;
    }
    .map-column {
      display: flex;
      flex-direction: column;
      flex: 1;
    }
    #title {
      font-size: 28px;
      font-weight: 300;
      color: #1a1a1a;
      margin: 8px 0;
      letter-spacing: -0.5px;
      text-align: center;
    }
    #subtitle {
      font-size: 14px;
      font-weight: 200;
      color: #555555;
      margin-bottom: 20px;
      letter-spacing: -0.5px;
      text-align: center;
    }
    .map-container {
      position: relative;
      flex: 1;
      height: 600px;
    }
    #map {
      width: 100%;
      height: 600px;
      position: relative;
    }
    .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));
    }
    /* Legislation section styling */
    .legislation-section {
      display: flex;
      flex-direction: column;
      width: 360px;
      gap: 10px;
    }
    .legislation-title {
      font-size: 28px;
      font-weight: 300;
      color: #1a1a1a;
      margin: 25px 0;
      letter-spacing: -0.5px;
      text-align: center;
    }
    .legislation-container {
      margin-top: 75px;
      width: 100%;
      padding: 20px;
      background: #ffffff;
      border-radius: 8px;
      box-shadow: 0 1px 3px rgba(0,0,0,0.1);
      overflow-y: auto;
      height: 100%;
      display: flex;
      flex-direction: column;
      gap: 18px;
    }
    .legislation-item {
      padding: 16px;
      background: #f9f9f9;
      border-radius: 6px;
      display: flex;
      flex-direction: column;
      gap: 6px;
    }
    .legislation-header {
      display: flex;
      align-items: center;
      gap: 10px;
      font-size: 16px;
      font-weight: 600;
      color: #1a1a1a;
    }
    .legislation-meta {
      font-size: 14px;
      color: #444444;
      display: flex;
      flex-direction: column;
      gap: 4px;
    }
    .legislation-meta div {
      display: flex;
      align-items: center;
      gap: 6px;
    }
    .legislation-description {
      font-size: 14px;
      color: #555555;
      line-height: 1.3;
      margin-top: 6px;
    }
    .legislation-footer {
      margin-top: 8px;
      display: flex;
      justify-content: flex-end;
    }
    .read-more-btn {
      background-color: #264edc;
      color: #fff;
      border: none;
      padding: 6px 12px;
      font-size: 12px;
      border-radius: 4px;
      cursor: pointer;
      transition: opacity 0.1s ease;
    }
    .read-more-btn:hover {
      opacity: 0.9;
    }
    /* Map controls */
    .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);
    }
    /* Schedule styling */
    .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;
      flex-shrink: 0;
      background: #888888;
      border: 2px solid #ffffff;
      border-radius: 50%;
      margin-top: 4px;
      position: relative;
      box-shadow: 0 0 0 2px #888888;
    }
    .schedule-marker-latest {
      background: #264edc;
      box-shadow: 0 0 0 2px #264edc;
    }
    .schedule-marker-latest-ring {
      position: absolute;
      width: 12px;
      height: 12px;
      background: transparent;
      border: 2px solid #264edc;
      border-radius: 50%;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      animation: pulse-map 2s infinite;
    }
    .schedule-content {
      flex-grow: 1;
    }
    .schedule-time {
      font-size: 14px;
      color: #6b7280;
      margin-bottom: 4px;
      word-wrap: break-word;
    }
    .schedule-details {
      font-size: 14px;
      color: #1a1a1a;
      line-height: 1.4;
      word-wrap: break-word;
    }
    @keyframes pulse-map {
      0% {
        width: 12px;
        height: 12px;
        opacity: 1;
        border-width: 2px;
      }
      100% {
        width: 40px;
        height: 40px;
        opacity: 0;
        border-width: 1px;
      }
    }
    /* Map markers */
    .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%);
    }
    /* Leaflet attribution */
    .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: 0 !important;
    }
    /* Footer styling */
    .footer {
      margin-top: 40px;
      margin-bottom: 25px;
      text-align: center;
      font-size: 12px;
      color: #555555;
    }
  </style>
  <link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
</head>
<body>
  <!-- Top Bar -->
  <div class="top-bar">
    <a href="faq.html" class="top-bar-link">
      The POTUS Tracker is now in beta! Click here to read a brief FAQ on safety and features.
    </a>
  </div>
  
  <!-- Main Container -->
  <div class="container">
    <div class="map-legislation-wrapper">
      <div class="map-column">
        <div id="title"></div>
        <div id="subtitle"></div>
        <div class="map-container">
          <div id="map"></div>
          <div class="fade-overlay"></div>
        </div>
      </div>
      <div class="legislation-section" id="legislation-section">
        <div class="legislation-container" id="legislation-container"></div>
      </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 -->
  <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>

  <!-- Ko-fi Widget -->
  <script src='https://storage.ko-fi.com/cdn/scripts/overlay-widget.js'></script>
  <script>
    kofiWidgetOverlay.draw('lukewines', {
      'type': 'floating-chat',
      'floating-chat.donateButton.text': 'Support me',
      'floating-chat.donateButton.background-color': '#264ecd',
      'floating-chat.donateButton.text-color': '#fff'
    });
  </script>

  <!-- Leaflet JS and map logic -->
  <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) {
        return L.DivIcon.prototype.createIcon.call(this, oldIcon);
      }
    });
    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 phrases
    const onTheLocations = ['Grounds'];
    const inLocations = [', '];
    const inTheLocations = ['Room', 'Office', 'House', 'Building', 'Lobby'];
    const atTheLocations = ['Museum', 'Library', 'Hotel', 'Department', 'National'];
    const atLocations = [
      'Park','Beach','Entrance','Mall','Station','Central Park',
      'Base','Medical Center','Hospital','School','Airport'
    ];

    function determinePreposition(location) {
      const locLower = location.toLowerCase();
      const createRegex = (word) => new RegExp(`\\b${word.toLowerCase()}\\b`);
      for (let loc of onTheLocations) {
        if (createRegex(loc).test(locLower)) return 'on the';
      }
      for (let loc of inTheLocations) {
        if (createRegex(loc).test(locLower)) return 'in the';
      }
      for (let loc of atTheLocations) {
        if (createRegex(loc).test(locLower)) return 'at the';
      }
      for (let loc of inLocations) {
        if (createRegex(loc.trim()).test(locLower)) return 'in';
      }
      for (let loc of atLocations) {
        if (createRegex(loc).test(locLower)) return 'at';
      }
      return 'at';
    }

    function determineEnRoutePreposition(location) {
      const locLower = location.toLowerCase();
      const createRegex = (word) => new RegExp(`\\b${word.toLowerCase()}\\b`);
      for (let loc of onTheLocations) {
        if (createRegex(loc).test(locLower)) return 'the';
      }
      for (let loc of inTheLocations) {
        if (createRegex(loc).test(locLower)) return 'the';
      }
      for (let loc of atTheLocations) {
        if (createRegex(loc).test(locLower)) return 'the';
      }
      for (let loc of inLocations) {
        if (createRegex(loc.trim()).test(locLower)) return '';
      }
      for (let loc of atLocations) {
        if (createRegex(loc).test(locLower)) return '';
      }
      return '';
    }

    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);
        circle.setRadius(fraction * targetRadius);
        if (fraction < 1) requestAnimationFrame(growCircle);
      }
      requestAnimationFrame(growCircle);
    }

    function describeLocation(event) {
      if (event.fuzzed && event.in_flight != 1) {
        if (event.location_name.includes(',')) {
          return `near ${event.location_name}`;
        } else {
          return `near the ${event.location_name}`;
        }
      } else {
        if (event.in_flight == 1) {
          const flightPreposition = determineEnRoutePreposition(event.arrival_location_name || '');
          return `${flightPreposition} ${event.arrival_location_name || 'an unknown location'}`;
        } else {
          const preposition = determinePreposition(event.location_name || '');
          return `${preposition} ${event.location_name || 'an unknown location'}`;
        }
      }
    }

    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 parseLegislationLink(billName) {
      const match = billName.match(/(hr|s)\.?(\d+)/i);
      if (!match) return '';
      const chamber = match[1].toLowerCase() === 'hr' ? 'house-bill' : 'senate-bill';
      const number = match[2];
      return `https://www.congress.gov/bill/119th-congress/${chamber}/${number}`;
    }

    let leafletMap = null;

    function createSchedule(events) {
      const scheduleDiv = document.getElementById('schedule');
      let currentDate = null;
      const reversedEvents = events.slice().reverse();

      reversedEvents.forEach((event, index) => {
        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';

        if (index === 0) {
          marker.classList.add('schedule-marker-latest');
          const ring = document.createElement('div');
          ring.className = 'schedule-marker-latest-ring';
          marker.appendChild(ring);
        }

        const content = document.createElement('div');
        content.className = 'schedule-content';

        const timeAndLocation = document.createElement('div');
        timeAndLocation.className = 'schedule-time';
        const localTimeStr = eventDate.toLocaleTimeString('en-US', {
          hour: 'numeric',
          minute: 'numeric',
          hour12: true,
          timeZone: 'America/Los_Angeles'
        });
        const locationName = event.location_name || 'Unknown location';
        timeAndLocation.textContent = `${localTimeStr} — ${locationName}`;

        const details = document.createElement('div');
        details.className = 'schedule-details';
        details.textContent = event.event_details;

        content.appendChild(timeAndLocation);
        content.appendChild(details);
        item.appendChild(marker);
        item.appendChild(content);
        scheduleDiv.appendChild(item);
      });
    }

    function createLegislationList(legislationArrays) {
      const container = document.getElementById('legislation-container');
      const section = document.getElementById('legislation-section');
      container.innerHTML = '';
      const allBills = legislationArrays.flat();

      if (allBills.length === 0) {
        section.style.display = 'none';
        if (leafletMap) leafletMap.invalidateSize();
        return;
      }
      section.style.display = 'flex';

      allBills.forEach(bill => {
        const link = parseLegislationLink(bill.name);
        const item = document.createElement('div');
        item.className = 'legislation-item';

        const header = document.createElement('div');
        header.className = 'legislation-header';
        const titleSpan = document.createElement('span');
        titleSpan.textContent = `${bill.title} (${bill.name})`;
        header.appendChild(titleSpan);

        const meta = document.createElement('div');
        meta.className = 'legislation-meta';

        const presentedDiv = document.createElement('div');
        const presentedIcon = document.createElement('i');
        if (bill.presentedToPresident) {
          presentedIcon.className = 'iconoir-check-circle';
          presentedIcon.style.color = 'green';
        } else {
          presentedIcon.className = 'iconoir-circle';
          presentedIcon.style.color = 'gray';
        }
        const presentedText = document.createElement('span');
        presentedText.textContent = 'Presented to the President';
        presentedDiv.appendChild(presentedIcon);
        presentedDiv.appendChild(presentedText);
        meta.appendChild(presentedDiv);

        const signedDiv = document.createElement('div');
        const signedIcon = document.createElement('i');
        if (bill.presidentSigned) {
          signedIcon.className = 'iconoir-check-circle';
          signedIcon.style.color = 'green';
        } else {
          signedIcon.className = 'iconoir-circle';
          signedIcon.style.color = 'gray';
        }
        const signedText = document.createElement('span');
        signedText.textContent = 'Signed by the President';
        signedDiv.appendChild(signedIcon);
        signedDiv.appendChild(signedText);
        meta.appendChild(signedDiv);

        const desc = document.createElement('div');
        desc.className = 'legislation-description';
        const shortDescription = bill.summary.length > 240
          ? bill.summary.substring(0, 180) + '...'
          : bill.summary;
        desc.textContent = shortDescription;

        const footer = document.createElement('div');
        footer.className = 'legislation-footer';
        const readMore = document.createElement('button');
        readMore.className = 'read-more-btn';
        readMore.textContent = 'Read More';
        readMore.onclick = () => {
          if (link) window.open(link, '_blank');
        };
        footer.appendChild(readMore);

        item.appendChild(header);
        item.appendChild(meta);
        item.appendChild(desc);
        item.appendChild(footer);
        container.appendChild(item);
      });

      if (leafletMap) leafletMap.invalidateSize();
    }

    function getMapAndRender(events, data) {
      leafletMap = 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(leafletMap);

      L.tileLayer('https://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}{r}.png', {
        subdomains: 'abcd',
        maxZoom: 20,
        opacity: 0.7
      }).addTo(leafletMap);

      const polylineGroup = L.layerGroup().addTo(leafletMap);
      const flightPolylineGroup = L.layerGroup().addTo(leafletMap);
      let previousIndex = events.length - 1;
      const addedLines = [];
      const newestEvent = events[events.length - 1];

      // Latest marker handling
      if (newestEvent.in_flight === 1) {
        const marker = L.marker([newestEvent.latitude, newestEvent.longitude], { icon: departureIcon }).addTo(leafletMap);
        marker.bindPopup(newestEvent.event_details, { closeButton: false, autoClose: false });
      } else {
        if (newestEvent.fuzzed) {
          const fuzzMarker = L.marker([newestEvent.latitude, newestEvent.longitude], { icon: latestIcon }).addTo(leafletMap);
          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(leafletMap);
          animateCircleGrowth(fuzzCircle, 32187, 2000);
        } else {
          const newestMarker = L.marker([newestEvent.latitude, newestEvent.longitude], { icon: latestIcon }).addTo(leafletMap);
          newestMarker.bindPopup(newestEvent.event_details, { closeButton: false, autoClose: false });
        }
      }

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

      function updateEvent(index) {
        const event = events[index];
        const lat = event.latitude;
        const lng = event.longitude;
        const fetchedAt = event.event_datetime_utc;

        if (index === events.length - 1) {
          if (event.in_flight === 1) {
            document.getElementById('title').textContent =
              `The President is en route to ${describeLocation(event)}`;
          } else {
            document.getElementById('title').textContent =
              `The President is ${describeLocation(event)}`;
          }
          if (isToday(fetchedAt)) {
            document.getElementById('subtitle').textContent = `as of ${convertUTCToLocalTime(fetchedAt)}`;
          } else {
            document.getElementById('subtitle').textContent = `as of ${convertUTCToLocalDate(fetchedAt)} at ${convertUTCToLocalTime(fetchedAt)}`;
          }
        } else {
          if (event.in_flight === 1) {
            document.getElementById('title').textContent =
              `The President was en route to ${describeLocation(event)}`;
          } else {
            document.getElementById('title').textContent =
              `The President was ${describeLocation(event)}`;
          }
          if (isToday(fetchedAt)) {
            document.getElementById('subtitle').textContent = `at ${convertUTCToLocalTime(fetchedAt)}`;
          } else {
            document.getElementById('subtitle').textContent = `on ${convertUTCToLocalDate(fetchedAt)} at ${convertUTCToLocalTime(fetchedAt)}`;
          }
        }

        leafletMap.setView([lat, lng], leafletMap.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(leafletMap);
          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(leafletMap);
            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]) {
            leafletMap.removeLayer(pastMarkers[previousIndex]);
          }
          if (previousIndex < events.length - 1 && fuzzedCircles[previousIndex]) {
            leafletMap.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(events[idx].event_details, { closeButton: false, autoClose: false });
            } else {
              marker.setIcon(greyIcon);
              marker.closePopup();
            }
          }
        });
      }

      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(leafletMap);
        } 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);
            leafletMap.removeLayer(flightMovingMarker);
            return;
          }
          const newIndex = Math.floor(newProgress * (fullPath.length - 1));
          drawPartialPath(newIndex);
          flightMovingMarker.setLatLng(fullPath[newIndex]);
        }, 1000);
      }

      createSchedule(events);
      createLegislationList(data.legislation);
      updateEvent(events.length - 1);

      if (newestEvent.in_flight === 1) {
        startFlightAnimation(newestEvent);
      }

      const timeSlider = document.getElementById('time-slider');
      timeSlider.min = 0;
      timeSlider.max = events.length - 1;
      timeSlider.value = events.length - 1;
      timeSlider.addEventListener('input', (e) => {
        updateEvent(parseInt(e.target.value));
      });

      const zoomSlider = document.getElementById('zoom-slider');
      zoomSlider.min = leafletMap.getMinZoom();
      zoomSlider.max = leafletMap.getMaxZoom();
      zoomSlider.value = leafletMap.getZoom();
      zoomSlider.addEventListener('input', (e) => {
        leafletMap.setZoom(parseInt(e.target.value));
      });

      leafletMap.on('zoomend', () => {
        zoomSlider.value = leafletMap.getZoom();
      });

      leafletMap.invalidateSize();
    }

    // Data fetch
    fetch('http://localhost:8535/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));
        getMapAndRender(events, data);
      })
      .catch(err => console.error(err));
  </script>
</body>
</html>