<!DOCTYPE html>
<html lang="sr">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Праћење возила</title>

  <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
  <link rel="stylesheet" href="style.css?v=35">
  <link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css">

  <style>
    .tracking-hero {
      position: relative;
      min-height: 145px;
      border-radius: 28px;
      overflow: hidden;
      box-shadow: 0 18px 40px rgba(0,0,0,0.38);
      border: 1px solid rgba(255,255,255,0.08);
      background:
        linear-gradient(135deg, rgba(4, 10, 18, 0.84), rgba(11, 39, 73, 0.58)),
        linear-gradient(to top, rgba(0,0,0,0.32), rgba(0,0,0,0.08));
      margin-bottom: 18px;
    }

    .tracking-hero-content {
      position: relative;
      z-index: 2;
      min-height: 145px;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: flex-start;
      padding: 18px 22px;
      max-width: 720px;
    }

    .tracking-hero-content h1 {
      font-size: 26px;
      line-height: 1.05;
      margin-bottom: 6px;
      font-weight: 800;
      text-shadow: 0 2px 10px rgba(0,0,0,0.25);
    }

    .tracking-hero-content p {
      font-size: 14px;
      line-height: 1.5;
      color: #e1ebfa;
      max-width: 560px;
      margin-bottom: 0;
    }

    .tracking-section {
      margin-top: 0;
    }

    .map-panel {
      background: linear-gradient(180deg, rgba(10, 28, 52, 0.96), rgba(7, 20, 38, 0.92));
      border-radius: 24px;
      padding: 14px;
      border: 1px solid rgba(255,255,255,0.06);
      box-shadow: 0 8px 18px rgba(0,0,0,0.25);
    }

    .map-wrap {
      position: relative;
      border-radius: 18px;
      overflow: hidden;
    }

    #map {
      width: 100%;
      height: 66vh;
      min-height: 440px;
      max-height: 780px;
      border-radius: 18px;
      overflow: hidden;
      box-shadow: 0 10px 35px rgba(0,0,0,0.5);
      border: 1px solid rgba(255,255,255,0.08);
    }

    .map-btns {
      position: absolute;
      top: 14px;
      right: 14px;
      z-index: 900;
      display: flex;
      gap: 8px;
      flex-wrap: wrap;
    }

    .map-btns button {
      background: #2d7df6;
      color: #fff;
      border: none;
      border-radius: 12px;
      padding: 10px 14px;
      font-weight: 700;
      cursor: pointer;
      box-shadow: 0 6px 18px rgba(0,0,0,0.28);
      font-family: inherit;
      transition: 0.25s ease;
    }

    .map-btns button:hover {
      background: #4990ff;
      transform: translateY(-1px);
    }

    .tracking-grid {
      display: grid;
      grid-template-columns: repeat(3, minmax(0, 1fr));
      gap: 12px;
      margin-top: 16px;
    }

    .tracking-grid .wide {
      grid-column: span 3;
    }

    .tracking-grid .info-card {
      padding: 16px 14px;
      min-height: 84px;
    }

    .tracking-grid .label {
      margin-bottom: 5px;
    }

    .tracking-grid .value {
      font-size: 16px;
      line-height: 1.3;
      word-break: break-word;
    }

    .tracking-grid .small {
      font-size: 12px;
      color: #c7d7ef;
      line-height: 1.4;
    }

    .status-badge {
      display: inline-block;
      padding: 5px 10px;
      border-radius: 999px;
      font-size: 13px;
      font-weight: 800;
    }

    .status-voznja {
      color: #55d27a;
      background: rgba(85, 210, 122, 0.12);
    }

    .status-stoji {
      color: #ffb74d;
      background: rgba(255, 183, 77, 0.12);
    }

    .status-stajaliste {
      color: #7cc4ff;
      background: rgba(124, 196, 255, 0.14);
    }

    .status-terminus {
      color: #c5a3ff;
      background: rgba(197, 163, 255, 0.14);
    }

    .status-garaza {
      color: #ff8a80;
      background: rgba(255, 138, 128, 0.14);
    }

    .bus-tooltip {
      background: rgba(7, 20, 38, 0.96);
      color: #fff;
      border: 1px solid rgba(255,255,255,0.08);
      border-radius: 10px;
      padding: 8px 10px;
      box-shadow: 0 6px 18px rgba(0,0,0,.25);
      font-size: 13px;
      line-height: 1.35;
    }

    .leaflet-tooltip-top:before {
      border-top-color: rgba(7, 20, 38, 0.96);
    }

    @media (max-width: 1100px) {
      .tracking-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
      }

      .tracking-grid .wide {
        grid-column: span 2;
      }

      #map {
        height: 60vh;
        min-height: 390px;
      }
    }

    @media (max-width: 700px) {
      .tracking-hero {
        min-height: 120px;
        border-radius: 20px;
      }

      .tracking-hero-content {
        min-height: 120px;
        padding: 14px 14px;
      }

      .tracking-hero-content h1 {
        font-size: 21px;
      }

      .tracking-hero-content p {
        font-size: 13px;
      }

      .map-panel {
        padding: 10px;
        border-radius: 18px;
      }

      .map-btns {
        top: 10px;
        right: 10px;
        left: 10px;
        justify-content: flex-end;
      }

      .map-btns button {
        padding: 9px 12px;
        font-size: 13px;
      }

      #map {
        height: 54vh;
        min-height: 330px;
      }

      .tracking-grid {
        grid-template-columns: 1fr;
        gap: 10px;
      }

      .tracking-grid .wide {
        grid-column: span 1;
      }

      .tracking-grid .info-card {
        padding: 14px 12px;
        min-height: 74px;
      }

      .tracking-grid .value {
        font-size: 15px;
      }
    }
  </style>
</head>
<body>

<header class="site-header">
  <div class="topbar header-row">
    <a href="index.html" class="brand">
      <img src="images/logo.png" class="logo" alt="Лого">
      <div class="brand-subtitle">Сигуран и поуздан превоз путника</div>
    </a>

    <nav class="main-nav">
      <a href="index.html">Почетна</a>
      <a href="pracenje.html">Праћење</a>
      <a href="kontakt.html">Контакт</a>
    </nav>
  </div>
</header>

<main class="container">
  <section class="tracking-hero">
    <div class="tracking-hero-content">
      <div class="badge">ПРАЋЕЊЕ УЖИВО</div>
      <h1>Праћење возила</h1>
      <p>Мапа, статус и сљедеће стајалиште у реалном времену.</p>
    </div>
  </section>

  <section class="tracking-section">
    <div class="map-panel">
      <div class="map-wrap">
        <div id="map"></div>

        <div class="map-btns">
          <button id="followBusBtn">Врати на аутобус</button>
        </div>
      </div>

      <div class="tracking-grid">
        <div class="info-card wide">
          <div class="label">Активан полазак</div>
          <div class="value" id="activeDeparture">Учитавање...</div>
        </div>

        <div class="info-card">
          <div class="label">Сљедећи полазак</div>
          <div class="value" id="nextDeparture">-</div>
        </div>

        <div class="info-card">
          <div class="label">Статус</div>
          <div class="value"><span class="status-badge" id="status">-</span></div>
        </div>

        <div class="info-card">
          <div class="label">Брзина</div>
          <div class="value" id="speed">-</div>
        </div>

        <div class="info-card wide">
          <div class="label">Тренутна локација</div>
          <div class="value" id="currentLocation">-</div>
        </div>

        <div class="info-card">
          <div class="label">Сљедеће стајалиште</div>
          <div class="value" id="nextStop">-</div>
        </div>

        <div class="info-card">
          <div class="label">Вријеме до сљедећег стајалишта</div>
          <div class="value" id="timeToNext">-</div>
        </div>

        <div class="info-card">
          <div class="label">Ажурирано</div>
          <div class="value small" id="fixTime">-</div>
        </div>
      </div>
    </div>
  </section>
</main>

<footer class="footer">
  © Самарџић Превоз
</footer>

<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet-polylinedecorator@1.6.0/dist/leaflet.polylineDecorator.js"></script>
<script>
  const map = L.map('map').setView([44.82, 19.27], 12);

  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    maxZoom: 18,
    attribution: '&copy; OpenStreetMap'
  }).addTo(map);

  let followBus = true;
  let firstLoad = true;
  let currentLatLng = null;
  let lastLoggedStopKey = null;
  let lastLoggedAt = 0;
  let currentActiveDepartureId = null;
  let lastSnappedIndex = null;
  let busMarker = null;
  let activePolyline = null;
  let activeDecorator = null;
  let activeStopMarkers = [];
  let animFrame = null;

  document.getElementById('followBusBtn').addEventListener('click', () => {
    followBus = true;
    if (currentLatLng) {
      map.setView(currentLatLng, Math.max(map.getZoom(), 15), { animate: true });
    }
  });

  map.on('dragstart zoomstart', () => {
    followBus = false;
  });

  const STOPS = {
    BN1:  { code: 'BN1',  name: 'Бијељина (СУД)', lat: 44.7565971, lng: 19.2160662 },
    BN2:  { code: 'BN2',  name: 'Саборна црква', lat: 44.7602248, lng: 19.2252709 },
    BN3:  { code: 'BN3',  name: 'Рачанска (СШЦ)', lat: 44.7705217, lng: 19.2254258 },
    BN4:  { code: 'BN4',  name: 'Комитска (Равна Гора)', lat: 44.7749811, lng: 19.2264675 },
    BN5:  { code: 'BN5',  name: 'Гојсовац', lat: 44.7855326, lng: 19.2378370 },
    BN6:  { code: 'BN6',  name: 'Нови Дворови (CSP)', lat: 44.7963962, lng: 19.2489940 },
    BN7:  { code: 'BN7',  name: 'Дворови (Глобус)', lat: 44.8058541, lng: 19.2600025 },
    BN8:  { code: 'BN8',  name: 'Дворови (Стадион)', lat: 44.8096574, lng: 19.2636054 },
    BN9:  { code: 'BN9',  name: 'Даждарево', lat: 44.8192270, lng: 19.2762268 },
    BN10: { code: 'BN10', name: 'Трњаци (Црква)', lat: 44.8268156, lng: 19.2859532 },
    BN11: { code: 'BN11', name: 'Трњаци (Центар)', lat: 44.8323773, lng: 19.2938178 },
    BN12: { code: 'BN12', name: 'Трњаци (Раскршће)', lat: 44.8479098, lng: 19.3037621 },
    BN13: { code: 'BN13', name: 'Балатун (Чанићи)', lat: 44.8522993, lng: 19.3118536 },
    BN14: { code: 'BN14', name: 'Балатун (Метеризи)', lat: 44.8550436, lng: 19.3171436 },
    BN15: { code: 'BN15', name: 'Балатун (Црква)', lat: 44.8612387, lng: 19.3248338 },
    BN16: { code: 'BN16', name: 'Балатун (Липице)', lat: 44.8629296, lng: 19.3376038 },

    BL2:  { code: 'BL2',  name: 'Балатун (Црква)', lat: 44.8609947, lng: 19.3245441 },
    BL3:  { code: 'BL3',  name: 'Балатун (Метеризи)', lat: 44.8551187, lng: 19.3171101 },
    BL4:  { code: 'BL4',  name: 'Балатун (Чанићи)', lat: 44.8525651, lng: 19.3121912 },

    BN17: { code: 'BN17', name: 'Бродац (Школа)', lat: 44.8531636, lng: 19.2898116 },
    BN18: { code: 'BN18', name: 'Бродац (Раскршће)', lat: 44.8636404, lng: 19.2766154 },
    BN19: { code: 'BN19', name: 'Бродац (Сувара)', lat: 44.8612806, lng: 19.2686295 },

    BC1:  { code: 'BC1',  name: 'Бродац (Школа)', lat: 44.8530889, lng: 19.2896835 },
    BL5:  { code: 'BL5',  name: 'Трњаци (Раскршће)', lat: 44.8473548, lng: 19.3026885 },
    BL7:  { code: 'BL7',  name: 'Трњаци (Златни Клас)', lat: 44.8282364, lng: 19.2879441 },
    BL8:  { code: 'BL8',  name: 'Даждарево', lat: 44.8186521, lng: 19.2754074 },
    G:    { code: 'G',    name: 'Гаража', lat: 44.8191159, lng: 19.2734263 },
    BL9:  { code: 'BL9',  name: 'Дворови (Стадион)', lat: 44.8099497, lng: 19.2636949 },
    BL10: { code: 'BL10', name: 'Дворови (Школа)', lat: 44.8045655, lng: 19.2585303 },
    BL11: { code: 'BL11', name: 'Нови Дворови (CSP)', lat: 44.7968399, lng: 19.2493813 },
    BL12: { code: 'BL12', name: 'Гојсовац', lat: 44.7858631, lng: 19.2379430 },
    BL13: { code: 'BL13', name: 'Равна Гора (Медицинска школа)', lat: 44.7740574, lng: 19.2253772 },
    BL14: { code: 'BL14', name: 'Рачанска (СШЦ)', lat: 44.7694418, lng: 19.2252810 },
    BL15: { code: 'BL15', name: 'СДГ (Апотека)', lat: 44.7609238, lng: 19.2240686 },
    BL16: { code: 'BL16', name: 'Бијељина (Трг)', lat: 44.7570432, lng: 19.2166409 }
  };

  const DEPARTURE_STOP_SEQUENCES = {
    d0600: ['G','BN9','BN10','BN11','BN12','BN13','BN14','BN15','BN16','BL2','BL3','BL4','BN17','BN18','BN19','BC1','BL5','BL7','BL8','BL9','BL10','BL11','BL12','BL13','BL14','BL15','BL16'],
    d0745: ['BN1','BN2','BN3','BN4','BN5','BN6','BN7','BN8','BN9','BN10','BN11','BN12','BN13','BN14','BN15','BN16','BL2','BL3','BL4','BN17','BN18','BN19','BC1','BL5','BL7','BL8','BL9','BL10','BL11','BL12','BL13','BL14','BL15','BL16'],
    d1000: ['BN1','BN2','BN3','BN4','BN5','BN6','BN7','BN8','BN9','BN10','BN11','BN17','BN18','BN19','BC1','BN12','BN13','BN14','BN15','BN16','BL2','BL3','BL4','BL5','BL7','BL8','BL9','BL10','BL11','BL12','BL13','BL14','BL15','BL16'],
    d1155: ['BN1','BN2','BN3','BN4','BN5','BN6','BN7','BN8','BN9','BN10','BN11','BN17','BN18','BN19','BC1','BN12','BN13','BN14','BN15','BN16','BL2','BL3','BL4','BL5','BL7','BL8','BL9','BL10','BL11','BL12','BL13','BL14','BL15','BL16'],
    d1340: ['BN1','BN2','BN3','BN4','BN5','BN6','BN7','BN8','BN9','BN10','BN11','BN12','BN13','BN14','BN15','BN16','BL2','BL3','BL4','BN17','BN18','BN19','BC1','BL5','BL7','BL8','G'],
    d1800: ['G','BL9','BL10','BL11','BL12','BL13','BL14','BL15','BL16'],
    d1830: ['BN1','BN2','BN3','BN4','BN5','BN6','BN7','BN8','BN9','BN10','BN11','BN12','BN13','BN14','BN15','BN16','BL2','BL3','BL4','BN17','BN18','BN19','BC1','BL5','BL7','BL8','BL9','BL10','BL11','BL12','BL13','BL14','BL15','BL16'],
    d1935: ['BN1','BN2','BN3','BN4','BN5','BN6','BN7','BN8','BN9','BN10','BN11','BN12','BN13','BN14','BN15','BN16','BL2','BL3','BL4','BN17','BN18','BN19','BC1','BL5','BL7','BL8','G']
  };

  const WAYPOINTS = {
    BIJ_BAL: [
      [44.7565971,19.2160662],[44.7582998,19.2255049],[44.7602248,19.2252709],
      [44.7616075,19.2253353],[44.7664921,19.2254087],[44.7739977,19.2257916],
      [44.7752694,19.2266596],[44.7808203,19.2339747],[44.7824719,19.2364587],
      [44.7850645,19.2373794],[44.7898636,19.2421329],[44.8000981,19.2539813],
      [44.8046882,19.2587817],[44.8099518,19.2637596],[44.8175562,19.2741072],
      [44.8265308,19.2854808],[44.8341194,19.2949044],[44.8478328,19.3035612],
      [44.8612727,19.3248506],[44.8628113,19.3381708],[44.8630268,19.3379378],[44.8628664,19.3375374]
    ],
    BAL_BROD: [
      [44.8628664,19.3375374],[44.8611720,19.3247383],[44.8530932,19.2899970],[44.8636996,19.2763794],[44.8612827,19.2685369]
    ],
    BROD_BIJ: [
      [44.8612827,19.2685369],[44.8585252,19.2790009],[44.8481221,19.3028085],[44.8262226,19.2849618],
      [44.8174659,19.2739312],[44.8039151,19.2579469],[44.7967669,19.2493833],[44.7835263,19.2362894],
      [44.7822199,19.2363813],[44.7748750,19.2261969],[44.7735805,19.2249836],[44.7731813,19.2255270],
      [44.7614620,19.2251797],[44.7611347,19.2248377],[44.7597316,19.2218880],[44.7570518,19.2166694]
    ],
    BIJ_BROD: [
      [44.7565971,19.2160662],[44.7582998,19.2255049],[44.7602248,19.2252709],[44.7616075,19.2253353],
      [44.7664921,19.2254087],[44.7739977,19.2257916],[44.7752694,19.2266596],[44.7808203,19.2339747],
      [44.7824719,19.2364587],[44.7850645,19.2373794],[44.7898636,19.2421329],[44.8000981,19.2539813],
      [44.8046882,19.2587817],[44.8099518,19.2637596],[44.8175562,19.2741072],[44.8265308,19.2854808],
      [44.8341194,19.2949044],[44.8530932,19.2899970],[44.8636996,19.2763794],[44.8612827,19.2685369]
    ],
    BROD_BAL: [
      [44.8612827,19.2685369],[44.8585252,19.2790009],[44.8481221,19.3028085],[44.8612727,19.3248506],
      [44.8628113,19.3381708],[44.8630268,19.3379378],[44.8628664,19.3375374]
    ],
    BAL_GAR: [
      [44.8628664,19.3375374],[44.8611720,19.3247383],[44.8474491,19.3029721],[44.8262226,19.2849618],[44.8191119,19.2734212]
    ],
    GAR_BIJ: [
      [44.8191119,19.2734212],[44.8039151,19.2579469],[44.7967669,19.2493833],[44.7835263,19.2362894],
      [44.7822199,19.2363813],[44.7748750,19.2261969],[44.7735805,19.2249836],[44.7731813,19.2255270],
      [44.7614620,19.2251797],[44.7611347,19.2248377],[44.7597316,19.2218880],[44.7570518,19.2166694]
    ],
    GAR_BAL: [
      [44.8191119,19.2734212],[44.8265308,19.2854808],[44.8341194,19.2949044],[44.8478328,19.3035612],
      [44.8612727,19.3248506],[44.8628113,19.3381708],[44.8630268,19.3379378],[44.8628664,19.3375374]
    ]
  };

  function mergeRoutes(...routes) {
    const out = [];
    routes.forEach((route, idx) => {
      route.forEach((pt, i) => {
        if (idx > 0 && i === 0) return;
        out.push(pt);
      });
    });
    return out;
  }

  const DEPARTURES = [
    { id: 'd0600', time: '06:00', displayInNext: false, title: '06:00 — Гаража → Балатун → Бродац → Бијељина (Трг)', waypointRoute: mergeRoutes(WAYPOINTS.GAR_BAL, WAYPOINTS.BAL_BROD, WAYPOINTS.BROD_BIJ) },
    { id: 'd0745', time: '07:45', displayInNext: true,  title: '07:45 — Бијељина (СУД) → Балатун → Бродац → Бијељина (Трг)', waypointRoute: mergeRoutes(WAYPOINTS.BIJ_BAL, WAYPOINTS.BAL_BROD, WAYPOINTS.BROD_BIJ) },
    { id: 'd1000', time: '10:00', displayInNext: true,  title: '10:00 — Бијељина (СУД) → Бродац → Балатун → Бијељина (Трг)', waypointRoute: mergeRoutes(WAYPOINTS.BIJ_BROD, WAYPOINTS.BROD_BAL, WAYPOINTS.BROD_BIJ) },
    { id: 'd1155', time: '11:55', displayInNext: true,  title: '11:55 — Бијељина (СУД) → Бродац → Балатун → Бијељина (Трг)', waypointRoute: mergeRoutes(WAYPOINTS.BIJ_BROD, WAYPOINTS.BROD_BAL, WAYPOINTS.BROD_BIJ) },
    { id: 'd1340', time: '13:40', displayInNext: true,  title: '13:40 — Бијељина (СУД) → Балатун → Бродац → Гаража', waypointRoute: mergeRoutes(WAYPOINTS.BIJ_BAL, WAYPOINTS.BAL_BROD, WAYPOINTS.BAL_GAR) },
    { id: 'd1800', time: '18:00', displayInNext: false, title: '18:00 — Гаража → Бијељина (Трг)', waypointRoute: WAYPOINTS.GAR_BIJ },
    { id: 'd1830', time: '18:30', displayInNext: true,  title: '18:30 — Бијељина (СУД) → Балатун → Бродац → Бијељина (Трг)', waypointRoute: mergeRoutes(WAYPOINTS.BIJ_BAL, WAYPOINTS.BAL_BROD, WAYPOINTS.BROD_BIJ) },
    { id: 'd1935', time: '19:35', displayInNext: true,  title: '19:35 — Бијељина (СУД) → Балатун → Бродац → Гаража', waypointRoute: mergeRoutes(WAYPOINTS.BIJ_BAL, WAYPOINTS.BAL_BROD, WAYPOINTS.BAL_GAR) }
  ].map(dep => ({
    ...dep,
    stopCodes: DEPARTURE_STOP_SEQUENCES[dep.id]
  }));

  const routedCache = new Map();
  const activeLayer = L.layerGroup().addTo(map);
  const activeStopsLayer = L.layerGroup().addTo(map);

  async function routeChunkOSRM(points) {
    const coords = points.map(p => `${p[1]},${p[0]}`).join(';');
    const url = `https://router.project-osrm.org/route/v1/driving/${coords}?overview=full&geometries=geojson`;
    const res = await fetch(url);
    const data = await res.json();
    if (!data.routes || !data.routes.length) throw new Error('OSRM route failed');
    return data.routes[0].geometry.coordinates.map(c => [c[1], c[0]]);
  }

  async function routeWaypoints(points) {
    if (points.length <= 25) return routeChunkOSRM(points);
    const out = [];
    let start = 0;
    while (start < points.length - 1) {
      const end = Math.min(start + 24, points.length - 1);
      const chunk = points.slice(start, end + 1);
      const geom = await routeChunkOSRM(chunk);
      if (out.length === 0) out.push(...geom);
      else out.push(...geom.slice(1));
      start = end;
    }
    return out;
  }

  async function ensureRoutedDeparture(dep) {
    if (routedCache.has(dep.id)) return routedCache.get(dep.id);
    let roadRoute;
    try {
      roadRoute = await routeWaypoints(dep.waypointRoute);
    } catch (e) {
      roadRoute = dep.waypointRoute;
    }
    const routed = { ...dep, roadRoute };
    routedCache.set(dep.id, routed);
    return routed;
  }

  function parseMinutes(hhmm) {
    const [h, m] = hhmm.split(':').map(Number);
    return h * 60 + m;
  }

  function formatFixTime(t) {
    if (!t) return '-';
    const d = new Date(t);
    if (isNaN(d.getTime())) return t;
    return d.toLocaleString('sr-RS');
  }

  function timeAgo(t) {
    if (!t) return '-';
    const d = new Date(t);
    const now = new Date();
    const diff = Math.floor((now - d) / 1000);
    if (diff < 60) return `прије ${diff}s`;
    if (diff < 3600) return `прије ${Math.floor(diff / 60)} min`;
    return `прије ${Math.floor(diff / 3600)} h`;
  }

  function haversineMeters(lat1, lon1, lat2, lon2) {
    const R = 6371000;
    const toRad = d => d * Math.PI / 180;
    const dLat = toRad(lat2 - lat1);
    const dLon = toRad(lon2 - lon1);
    const a =
      Math.sin(dLat / 2) ** 2 +
      Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLon / 2) ** 2;
    return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  }

  function buildCumulative(route) {
    const arr = [0];
    let total = 0;
    for (let i = 1; i < route.length; i++) {
      total += haversineMeters(route[i - 1][0], route[i - 1][1], route[i][0], route[i][1]);
      arr.push(total);
    }
    return arr;
  }

  function latLngToXYMeters(lat, lng, refLat) {
    const R = 6378137;
    return {
      x: R * (lng * Math.PI / 180) * Math.cos(refLat * Math.PI / 180),
      y: R * (lat * Math.PI / 180)
    };
  }

  function projectToRoute(route, lat, lng) {
    let best = { index: 0, lat: route[0][0], lng: route[0][1], distance: Infinity };

    for (let i = 0; i < route.length - 1; i++) {
      const a = route[i];
      const b = route[i + 1];
      const refLat = (lat + a[0] + b[0]) / 3;

      const p = latLngToXYMeters(lat, lng, refLat);
      const aa = latLngToXYMeters(a[0], a[1], refLat);
      const bb = latLngToXYMeters(b[0], b[1], refLat);

      const abx = bb.x - aa.x;
      const aby = bb.y - aa.y;
      const apx = p.x - aa.x;
      const apy = p.y - aa.y;
      const ab2 = abx * abx + aby * aby;

      let t = ab2 === 0 ? 0 : (apx * abx + apy * aby) / ab2;
      t = Math.max(0, Math.min(1, t));

      const projX = aa.x + abx * t;
      const projY = aa.y + aby * t;
      const dx = p.x - projX;
      const dy = p.y - projY;
      const dist = Math.sqrt(dx * dx + dy * dy);

      if (dist < best.distance) {
        best = {
          index: i,
          lat: a[0] + (b[0] - a[0]) * t,
          lng: a[1] + (b[1] - a[1]) * t,
          distance: dist
        };
      }
    }

    return best;
  }

  function buildStopIndices(dep) {
    let cursor = 0;
    return dep.stopCodes.map(code => {
      const s = STOPS[code];
      let bestIdx = cursor;
      let bestDist = Infinity;

      for (let i = cursor; i < dep.roadRoute.length; i++) {
        const d = haversineMeters(s.lat, s.lng, dep.roadRoute[i][0], dep.roadRoute[i][1]);
        if (d < bestDist) {
          bestDist = d;
          bestIdx = i;
        }
      }

      cursor = bestIdx;
      return bestIdx;
    });
  }

  function formatArrivalClock(minsFromNow) {
    if (!isFinite(minsFromNow) || minsFromNow < 0) return '-';
    const now = new Date();
    const future = new Date(now.getTime() + minsFromNow * 60000);
    return future.toLocaleTimeString('sr-RS', { hour12: false });
  }

  function getNextDisplayDeparture(nowMins, excludeId = null) {
    const list = DEPARTURES.filter(d => d.displayInNext && d.id !== excludeId);
    for (const dep of list) {
      if (parseMinutes(dep.time) > nowMins) return dep;
    }
    return list[0] || null;
  }

  function isAtTerminal(rawLat, rawLng, speed) {
    if (speed >= 6) return null;

    const terminals = [
      { code: 'BN1', radius: 25 },
      { code: 'BN16', radius: 25 },
      { code: 'BL16', radius: 25 }
    ];

    for (const t of terminals) {
      const s = STOPS[t.code];
      if (s && haversineMeters(rawLat, rawLng, s.lat, s.lng) <= t.radius) return t.code;
    }

    return null;
  }

  async function chooseActiveDeparture(rawLat, rawLng, speed, nowMins) {
    const terminalHit = isAtTerminal(rawLat, rawLng, speed);
    if (terminalHit) {
      const dep = getNextDisplayDeparture(nowMins);
      return dep ? ensureRoutedDeparture(dep) : ensureRoutedDeparture(DEPARTURES[0]);
    }

    let best = null;

    for (const dep of DEPARTURES) {
      const routed = await ensureRoutedDeparture(dep);
      const projected = projectToRoute(routed.roadRoute, rawLat, rawLng);
      const depTime = parseMinutes(dep.time);
      const timePenalty = Math.min(Math.abs(nowMins - depTime), 240) * 3;

      let directionPenalty = 0;
      if (currentActiveDepartureId === routed.id && lastSnappedIndex !== null) {
        const idxDiff = projected.index - lastSnappedIndex;
        if (idxDiff < -8) directionPenalty = 3500;
        else if (idxDiff < -3) directionPenalty = 1500;
      }

      const score = projected.distance + timePenalty + directionPenalty;

      if (!best || score < best.score) {
        best = { dep: routed, score };
      }
    }

    return best.dep;
  }

  function getLocationAndNext(dep, rawLat, rawLng, speed) {
    const stopIndices = buildStopIndices(dep);
    const cum = buildCumulative(dep.roadRoute);
    const projected = projectToRoute(dep.roadRoute, rawLat, rawLng);
    const snappedIdx = projected.index;

    let prevStopIdx = 0;
    let nextStopIdx = dep.stopCodes.length - 1;
    let currentStopIdx = null;

    for (let i = 0; i < stopIndices.length - 1; i++) {
      if (snappedIdx >= stopIndices[i] && snappedIdx < stopIndices[i + 1]) {
        prevStopIdx = i;
        nextStopIdx = i + 1;
        break;
      }
    }

    const windowStart = Math.max(0, prevStopIdx);
    const windowEnd = Math.min(dep.stopCodes.length - 1, prevStopIdx + 3);

    for (let i = windowStart; i <= windowEnd; i++) {
      const s = STOPS[dep.stopCodes[i]];
      const d = haversineMeters(rawLat, rawLng, s.lat, s.lng);
      if (d <= 14 && speed < 7) {
        currentStopIdx = i;
        prevStopIdx = i;
        nextStopIdx = Math.min(i + 1, dep.stopCodes.length - 1);
        break;
      }
    }

    if (snappedIdx > stopIndices[nextStopIdx] && nextStopIdx < dep.stopCodes.length - 1) {
      prevStopIdx = nextStopIdx;
      nextStopIdx = Math.min(nextStopIdx + 1, dep.stopCodes.length - 1);
    }

    const locationText = currentStopIdx !== null
      ? `На стајалишту ${STOPS[dep.stopCodes[currentStopIdx]].name}`
      : `Између ${STOPS[dep.stopCodes[prevStopIdx]].name} и ${STOPS[dep.stopCodes[nextStopIdx]].name}`;

    const nextStopCode = dep.stopCodes[nextStopIdx];
    const metersToNext = Math.max(0, cum[stopIndices[nextStopIdx]] - cum[snappedIdx]);
    const effectiveSpeed = speed < 5 ? 28 : speed;
    const minsToNext = (metersToNext / 1000) / effectiveSpeed * 60;

    return {
      snappedIdx,
      snappedPoint: [projected.lat, projected.lng],
      nextStopCode,
      nextStopLabel: STOPS[nextStopCode].name,
      locationText,
      minsToNext,
      prevStopIdx,
      nextStopIdx,
      currentStopIdx
    };
  }

  const busIcon = L.divIcon({
    className: '',
    html: `
      <div style="
        width:34px;height:34px;display:flex;align-items:center;justify-content:center;
        background:#111827;border:3px solid #fff;border-radius:50%;
        box-shadow:0 4px 12px rgba(0,0,0,.35);font-size:18px;
      ">🚌</div>
    `,
    iconSize: [34, 34],
    iconAnchor: [17, 17]
  });

  function clearActiveRoute() {
    if (activePolyline) activeLayer.removeLayer(activePolyline);
    if (activeDecorator) activeLayer.removeLayer(activeDecorator);
    activePolyline = null;
    activeDecorator = null;
  }

  function clearActiveStops() {
    activeStopMarkers.forEach(m => activeStopsLayer.removeLayer(m));
    activeStopMarkers = [];
  }

  function drawActiveStops(dep, loc) {
    clearActiveStops();

    dep.stopCodes.forEach((code, idx) => {
      const s = STOPS[code];

      let fillColor = '#0ea5e9';
      if (idx < loc.prevStopIdx) fillColor = '#9ca3af';
      if (idx === loc.nextStopIdx) fillColor = '#f59e0b';
      if (loc.currentStopIdx !== null && idx === loc.currentStopIdx) fillColor = '#22c55e';

      const marker = L.circleMarker([s.lat, s.lng], {
        radius: 7,
        color: '#ffffff',
        fillColor,
        fillOpacity: 1,
        weight: 3
      }).bindPopup(`${code} — ${s.name}`).addTo(activeStopsLayer);

      activeStopMarkers.push(marker);
    });
  }

  function drawActiveRoute(dep, loc) {
    if (currentActiveDepartureId !== dep.id) {
      currentActiveDepartureId = dep.id;
      clearActiveRoute();

      activePolyline = L.polyline(dep.roadRoute, {
        color: '#1d4ed8',
        weight: 4,
        opacity: 0.95
      }).addTo(activeLayer);

      activeDecorator = L.polylineDecorator(activePolyline, {
        patterns: [{
          offset: '6%',
          repeat: '10%',
          symbol: L.Symbol.arrowHead({
            pixelSize: 8,
            polygon: false,
            pathOptions: {
              color: '#1d4ed8',
              weight: 2
            }
          })
        }]
      }).addTo(activeLayer);
    }

    drawActiveStops(dep, loc);
  }

  function animateBus(toLatLng) {
    if (!busMarker) return;

    if (!currentLatLng) {
      currentLatLng = toLatLng;
      busMarker.setLatLng(toLatLng);
      return;
    }

    if (animFrame) cancelAnimationFrame(animFrame);

    const from = { lat: currentLatLng[0], lng: currentLatLng[1] };
    const to = { lat: toLatLng[0], lng: toLatLng[1] };
    const start = performance.now();
    const duration = 2200;

    function step(now) {
      const p = Math.min((now - start) / duration, 1);
      const e = 1 - Math.pow(1 - p, 3);
      const lat = from.lat + (to.lat - from.lat) * e;
      const lng = from.lng + (to.lng - from.lng) * e;

      busMarker.setLatLng([lat, lng]);
      currentLatLng = [lat, lng];

      if (followBus) {
        map.panTo([lat, lng], { animate: false });
      }

      if (p < 1) animFrame = requestAnimationFrame(step);
    }

    animFrame = requestAnimationFrame(step);
  }

  function setStatus(statusText, statusClass) {
    const el = document.getElementById('status');
    el.textContent = statusText;
    el.className = `status-badge ${statusClass}`;
  }

  async function updateBus() {
    try {
      const res = await fetch('traccar_proxy.php?t=' + Date.now(), { cache: 'no-store' });
      const data = await res.json();
      if (!data.success || !data.latitude || !data.longitude) return;

      const rawLat = parseFloat(data.latitude);
      const rawLng = parseFloat(data.longitude);
      const speed = parseFloat(data.speed || 0);
      const now = new Date();
      const nowMins = now.getHours() * 60 + now.getMinutes();

      const activeDep = await chooseActiveDeparture(rawLat, rawLng, speed, nowMins);
      const nextDep = getNextDisplayDeparture(nowMins, activeDep.id);
      const nextDepRouted = nextDep ? await ensureRoutedDeparture(nextDep) : null;

      const loc = getLocationAndNext(activeDep, rawLat, rawLng, speed);
      drawActiveRoute(activeDep, loc);

      lastSnappedIndex = loc.snappedIdx;

      let statusText = 'У вожњи';
      let statusClass = 'status-voznja';

      if (speed < 5) {
        statusText = 'Стоји';
        statusClass = 'status-stoji';
      }

      if (loc.locationText.includes('На стајалишту')) {
        statusText = 'На стајалишту';
        statusClass = 'status-stajaliste';
      }

      if ((loc.nextStopCode === 'BN1' || loc.nextStopCode === 'BN16' || loc.nextStopCode === 'BL16') && speed < 5) {
        statusText = 'На терминусу';
        statusClass = 'status-terminus';
      }

      if ((loc.nextStopCode === 'G' || (loc.currentStopIdx !== null && activeDep.stopCodes[loc.currentStopIdx] === 'G')) && speed < 5) {
        statusText = 'У гаражи';
        statusClass = 'status-garaza';
      }

      setStatus(statusText, statusClass);

      const nextStopObj = STOPS[loc.nextStopCode];
      const distToNextStop = haversineMeters(rawLat, rawLng, nextStopObj.lat, nextStopObj.lng);
      const nowTs = Date.now();

      if (distToNextStop <= 14 && speed < 7) {
        const logKey = `${activeDep.id}_${loc.nextStopCode}`;
        if (lastLoggedStopKey !== logKey || (nowTs - lastLoggedAt) > 30000) {
          lastLoggedStopKey = logKey;
          lastLoggedAt = nowTs;

          fetch('log_stop.php', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
              departureId: activeDep.id,
              stopCode: loc.nextStopCode,
              stopName: loc.nextStopLabel,
              fixTime: data.fixTime || new Date().toISOString()
            })
          }).catch(() => {});
        }
      }

      document.getElementById('activeDeparture').innerText = activeDep.title;
      document.getElementById('nextDeparture').innerText = nextDepRouted ? nextDepRouted.title : '-';
      document.getElementById('currentLocation').innerText = loc.locationText;
      document.getElementById('nextStop').innerText = `${loc.nextStopCode} — ${loc.nextStopLabel}`;
      document.getElementById('timeToNext').innerText = formatArrivalClock(loc.minsToNext);
      document.getElementById('speed').innerText = `${speed.toFixed(1)} km/h`;
      document.getElementById('fixTime').innerText = `${formatFixTime(data.fixTime)} (${timeAgo(data.fixTime)})`;

      if (!busMarker) {
        busMarker = L.marker(loc.snappedPoint, { icon: busIcon }).addTo(map);
        currentLatLng = loc.snappedPoint;

        if (firstLoad) {
          map.setView(loc.snappedPoint, 15, { animate: true });
          firstLoad = false;
        }
      } else {
        animateBus(loc.snappedPoint);
      }

      const tooltipHtml = `
        <div class="bus-tooltip">
          <strong>Сљедеће стајалиште: ${loc.nextStopCode} — ${loc.nextStopLabel}</strong>
          Вријеме до сљедећег стајалишта: ${formatArrivalClock(loc.minsToNext)}
        </div>
      `;

      if (!busMarker.getTooltip()) {
        busMarker.bindTooltip(tooltipHtml, {
          permanent: false,
          direction: 'top',
          offset: [0, -18],
          className: ''
        });
      } else {
        busMarker.setTooltipContent(tooltipHtml);
      }

      busMarker.openTooltip();

    } catch (e) {
      console.log('Грешка updateBus', e);
    }
  }

  async function init() {
    for (const dep of DEPARTURES) {
      await ensureRoutedDeparture(dep);
    }

    const boundsPolys = [];
    for (const dep of DEPARTURES) {
      const r = await ensureRoutedDeparture(dep);
      boundsPolys.push(L.polyline(r.roadRoute));
    }

    map.fitBounds(L.featureGroup(boundsPolys).getBounds(), { padding: [20, 20] });

    await updateBus();
    setInterval(updateBus, 3000);
  }

  init();
</script>

</body>
</html>