{"id":2275,"date":"2026-08-25T11:29:58","date_gmt":"2026-08-25T11:29:58","guid":{"rendered":"https:\/\/a2jobs.com\/Vanship_website_preview\/?page_id=2275"},"modified":"2026-09-07T17:37:29","modified_gmt":"2026-09-07T17:37:29","slug":"distance-calculator","status":"publish","type":"page","link":"https:\/\/vanchile.com\/distance-calculator\/","title":{"rendered":"Distance Calculator"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"2275\" class=\"elementor elementor-2275\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-9baaee3 e-con-full e-flex e-con e-parent\" data-id=\"9baaee3\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-34cd812 elementor-widget elementor-widget-html\" data-id=\"34cd812\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div id=\"fdt-calculator\">\r\n\r\n<style>\r\n#fdt-calculator{\r\n  --primary:#F96C0A;\r\n  --primary-dark:#c95f1d;\r\n  --text:#1f2937;\r\n  --muted:#6b7280;\r\n  --border:#e5e7eb;\r\n  --bg:#f7f8fa;\r\n  --white:#fff;\r\n\r\n  width:100%;\r\n  font-family:Arial,Helvetica,sans-serif;\r\n  color:var(--text);\r\n}\r\n\r\n#fdt-calculator *,\r\n#fdt-calculator *::before,\r\n#fdt-calculator *::after{\r\n  box-sizing:border-box;\r\n}\r\n\r\n#fdt-calculator .fdt-wrap{\r\n  width:100%;\r\n  max-width:1400px;\r\n  margin:auto;\r\n  background:#fff;\r\n  border:1px solid var(--border);\r\n  border-radius:14px;\r\n  overflow:hidden;\r\n  box-shadow:0 15px 45px rgba(0,0,0,.07);\r\n}\r\n\r\n#fdt-calculator .fdt-header{\r\n  padding:30px;\r\n  background:linear-gradient(135deg,#F96C0A,#ff914d);\r\n}\r\n\r\n#fdt-calculator .fdt-header h2{\r\n  margin:0 0 7px;\r\n  color:#fff;\r\n  font-size:28px;\r\n  line-height:1.2;\r\n}\r\n\r\n#fdt-calculator .fdt-header p{\r\n  margin:0;\r\n  color:#fff;\r\n  font-size:14px;\r\n  opacity:.95;\r\n  line-height:1.6;\r\n}\r\n\r\n#fdt-calculator .fdt-content{\r\n  display:grid;\r\n  grid-template-columns:390px minmax(0,1fr);\r\n  min-height:650px;\r\n}\r\n\r\n#fdt-calculator .fdt-sidebar{\r\n  padding:24px;\r\n  background:#fff;\r\n  border-right:1px solid var(--border);\r\n  position:relative;\r\n  z-index:20;\r\n}\r\n\r\n#fdt-calculator .fdt-field{\r\n  margin-bottom:17px;\r\n  position:relative;\r\n}\r\n\r\n#fdt-calculator label{\r\n  display:block;\r\n  margin-bottom:7px;\r\n  font-size:12px;\r\n  font-weight:700;\r\n  color:var(--text);\r\n}\r\n\r\n#fdt-calculator input,\r\n#fdt-calculator select{\r\n  width:100%;\r\n  height:48px;\r\n  border:1px solid var(--border);\r\n  border-radius:9px;\r\n  background:#fff;\r\n  color:var(--text);\r\n  padding:0 14px;\r\n  font-family:Arial,Helvetica,sans-serif;\r\n  font-size:14px;\r\n  outline:none;\r\n}\r\n\r\n#fdt-calculator input:focus,\r\n#fdt-calculator select:focus{\r\n  border-color:var(--primary);\r\n  box-shadow:0 0 0 3px rgba(249,108,10,.1);\r\n}\r\n\r\n#fdt-calculator .fdt-autocomplete{\r\n  position:relative;\r\n  width:100%;\r\n}\r\n\r\n#fdt-calculator .fdt-suggestions{\r\n  position:absolute;\r\n  left:0;\r\n  right:0;\r\n  top:53px;\r\n  z-index:9999;\r\n  background:#fff;\r\n  border:1px solid var(--border);\r\n  border-radius:10px;\r\n  box-shadow:0 12px 35px rgba(0,0,0,.15);\r\n  max-height:310px;\r\n  overflow-y:auto;\r\n  display:none;\r\n}\r\n\r\n#fdt-calculator .fdt-suggestions.show{\r\n  display:block;\r\n}\r\n\r\n#fdt-calculator .fdt-suggestion{\r\n  display:flex;\r\n  align-items:center;\r\n  gap:10px;\r\n  width:100%;\r\n  padding:12px 13px;\r\n  background:#fff;\r\n  border:0;\r\n  border-bottom:1px solid #f1f1f1;\r\n  text-align:left;\r\n  cursor:pointer;\r\n  transition:.15s;\r\n}\r\n\r\n#fdt-calculator .fdt-suggestion:hover,\r\n#fdt-calculator .fdt-suggestion.active{\r\n  background:#fff7f1;\r\n}\r\n\r\n#fdt-calculator .fdt-suggestion-icon{\r\n  width:34px;\r\n  height:34px;\r\n  flex:0 0 34px;\r\n  display:flex;\r\n  align-items:center;\r\n  justify-content:center;\r\n  border-radius:8px;\r\n  background:#fff1e8;\r\n  font-size:17px;\r\n}\r\n\r\n#fdt-calculator .fdt-suggestion-content{\r\n  min-width:0;\r\n  flex:1;\r\n}\r\n\r\n#fdt-calculator .fdt-suggestion-name{\r\n  display:block;\r\n  font-size:12px;\r\n  font-weight:700;\r\n  color:var(--text);\r\n  white-space:nowrap;\r\n  overflow:hidden;\r\n  text-overflow:ellipsis;\r\n}\r\n\r\n#fdt-calculator .fdt-suggestion-meta{\r\n  display:block;\r\n  margin-top:3px;\r\n  color:var(--muted);\r\n  font-size:10px;\r\n  white-space:nowrap;\r\n  overflow:hidden;\r\n  text-overflow:ellipsis;\r\n}\r\n\r\n#fdt-calculator .fdt-suggestion-type{\r\n  flex:0 0 auto;\r\n  padding:4px 6px;\r\n  border-radius:5px;\r\n  background:#f3f4f6;\r\n  color:#6b7280;\r\n  font-size:9px;\r\n  font-weight:700;\r\n}\r\n\r\n#fdt-calculator .fdt-suggestion-loading,\r\n#fdt-calculator .fdt-suggestion-empty{\r\n  padding:15px;\r\n  text-align:center;\r\n  color:var(--muted);\r\n  font-size:11px;\r\n}\r\n\r\n#fdt-calculator .fdt-origin{\r\n  position:relative;\r\n}\r\n\r\n#fdt-calculator .fdt-swap{\r\n  position:absolute;\r\n  right:12px;\r\n  top:30px;\r\n  width:34px!important;\r\n  height:34px!important;\r\n  padding:0!important;\r\n  margin:0!important;\r\n  border:1px solid var(--border)!important;\r\n  border-radius:50%!important;\r\n  background:#fff!important;\r\n  color:var(--primary)!important;\r\n  font-size:17px!important;\r\n  line-height:30px!important;\r\n  cursor:pointer;\r\n  z-index:20;\r\n}\r\n\r\n#fdt-calculator .fdt-swap:hover{\r\n  background:var(--primary)!important;\r\n  color:#fff!important;\r\n}\r\n\r\n#fdt-calculator .fdt-status{\r\n  min-height:16px;\r\n  margin-top:5px;\r\n  font-size:10px;\r\n  line-height:1.4;\r\n  color:var(--muted);\r\n}\r\n\r\n#fdt-calculator .fdt-transport{\r\n  margin-top:20px;\r\n}\r\n\r\n#fdt-calculator .fdt-transport select{\r\n  cursor:pointer;\r\n}\r\n\r\n#fdt-calculator .fdt-actions{\r\n  margin-top:20px;\r\n  display:grid;\r\n  gap:9px;\r\n}\r\n\r\n#fdt-calculator .fdt-calculate{\r\n  width:100%!important;\r\n  height:50px!important;\r\n  padding:0 18px!important;\r\n  margin:0!important;\r\n  border:0!important;\r\n  border-radius:9px!important;\r\n  background:#F96C0A!important;\r\n  color:#fff!important;\r\n  font-family:Arial,Helvetica,sans-serif!important;\r\n  font-size:14px!important;\r\n  font-weight:700!important;\r\n  cursor:pointer!important;\r\n}\r\n\r\n#fdt-calculator .fdt-calculate:hover{\r\n  background:#c95f1d!important;\r\n}\r\n\r\n#fdt-calculator .fdt-current{\r\n  width:100%!important;\r\n  height:44px!important;\r\n  padding:0 15px!important;\r\n  margin:0!important;\r\n  border:1px solid var(--border)!important;\r\n  border-radius:9px!important;\r\n  background:#fff!important;\r\n  color:var(--text)!important;\r\n  font-size:12px!important;\r\n  font-weight:600!important;\r\n  cursor:pointer!important;\r\n}\r\n\r\n#fdt-calculator .fdt-current:hover{\r\n  border-color:var(--primary)!important;\r\n  color:var(--primary)!important;\r\n}\r\n\r\n#fdt-calculator .fdt-error{\r\n  display:none;\r\n  margin-top:12px;\r\n  padding:10px 12px;\r\n  background:#fff1ed;\r\n  color:#b42318;\r\n  border-radius:8px;\r\n  font-size:11px;\r\n  line-height:1.5;\r\n}\r\n\r\n#fdt-calculator .fdt-error.show{\r\n  display:block;\r\n}\r\n\r\n#fdt-calculator .fdt-map-wrapper{\r\n  position:relative;\r\n  min-width:0;\r\n  min-height:650px;\r\n  background:#eef1f4;\r\n}\r\n\r\n#fdt-calculator .fdt-map{\r\n  width:100%;\r\n  height:650px;\r\n  min-height:650px;\r\n  background:#eef1f4;\r\n}\r\n\r\n#fdt-calculator .leaflet-container{\r\n  width:100%;\r\n  height:100%;\r\n  font-family:Arial,Helvetica,sans-serif;\r\n}\r\n\r\n#fdt-calculator .leaflet-control{\r\n  z-index:400;\r\n}\r\n\r\n#fdt-calculator .fdt-results{\r\n  position:absolute;\r\n  left:18px;\r\n  right:18px;\r\n  bottom:18px;\r\n  z-index:500;\r\n  background:rgba(255,255,255,.97);\r\n  border:1px solid var(--border);\r\n  border-radius:13px;\r\n  overflow:hidden;\r\n  box-shadow:0 10px 30px rgba(0,0,0,.13);\r\n}\r\n\r\n#fdt-calculator .fdt-result-grid{\r\n  display:grid;\r\n  grid-template-columns:repeat(2,1fr);\r\n}\r\n\r\n#fdt-calculator .fdt-result{\r\n  padding:17px 10px;\r\n  text-align:center;\r\n  border-right:1px solid var(--border);\r\n}\r\n\r\n#fdt-calculator .fdt-result:last-child{\r\n  border-right:0;\r\n}\r\n\r\n#fdt-calculator .fdt-result small{\r\n  display:block;\r\n  margin-bottom:5px;\r\n  color:var(--muted);\r\n  font-size:9px;\r\n  font-weight:700;\r\n  text-transform:uppercase;\r\n  letter-spacing:.5px;\r\n}\r\n\r\n#fdt-calculator .fdt-result strong{\r\n  display:block;\r\n  color:var(--primary);\r\n  font-size:21px;\r\n}\r\n\r\n#fdt-calculator .fdt-result span{\r\n  display:block;\r\n  margin-top:3px;\r\n  color:var(--muted);\r\n  font-size:10px;\r\n}\r\n\r\n#fdt-calculator .fdt-route-info{\r\n  padding:11px 14px;\r\n  border-top:1px solid var(--border);\r\n  color:var(--muted);\r\n  font-size:10px;\r\n  text-align:center;\r\n  line-height:1.5;\r\n}\r\n\r\n#fdt-calculator .fdt-loading{\r\n  position:absolute;\r\n  inset:0;\r\n  z-index:1000;\r\n  display:none;\r\n  align-items:center;\r\n  justify-content:center;\r\n  background:rgba(255,255,255,.7);\r\n}\r\n\r\n#fdt-calculator .fdt-loading.show{\r\n  display:flex;\r\n}\r\n\r\n#fdt-calculator .fdt-loading-box{\r\n  padding:16px 23px;\r\n  background:#fff;\r\n  border-radius:9px;\r\n  box-shadow:0 10px 30px rgba(0,0,0,.15);\r\n  font-size:13px;\r\n  font-weight:700;\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   RESPONSIVE\r\n========================================================= *\/\r\n\r\n@media(max-width:900px){\r\n\r\n  #fdt-calculator .fdt-content{\r\n    grid-template-columns:1fr;\r\n  }\r\n\r\n  #fdt-calculator .fdt-sidebar{\r\n    border-right:0;\r\n    border-bottom:1px solid var(--border);\r\n  }\r\n\r\n  #fdt-calculator .fdt-map-wrapper{\r\n    min-height:500px;\r\n  }\r\n\r\n  #fdt-calculator .fdt-map{\r\n    height:500px;\r\n    min-height:500px;\r\n  }\r\n}\r\n\r\n@media(max-width:600px){\r\n\r\n  #fdt-calculator .fdt-header{\r\n    padding:22px 17px;\r\n  }\r\n\r\n  #fdt-calculator .fdt-header h2{\r\n    font-size:21px;\r\n  }\r\n\r\n  #fdt-calculator .fdt-header p{\r\n    font-size:12px;\r\n  }\r\n\r\n  #fdt-calculator .fdt-sidebar{\r\n    padding:16px;\r\n  }\r\n\r\n  #fdt-calculator .fdt-map-wrapper{\r\n    min-height:470px;\r\n  }\r\n\r\n  #fdt-calculator .fdt-map{\r\n    height:470px;\r\n    min-height:470px;\r\n  }\r\n\r\n  #fdt-calculator .fdt-results{\r\n    left:9px;\r\n    right:9px;\r\n    bottom:9px;\r\n  }\r\n\r\n  #fdt-calculator .fdt-result{\r\n    padding:13px 4px;\r\n  }\r\n\r\n  #fdt-calculator .fdt-result strong{\r\n    font-size:16px;\r\n  }\r\n\r\n  #fdt-calculator .fdt-result small{\r\n    font-size:8px;\r\n  }\r\n}\r\n\r\n@media(max-width:360px){\r\n\r\n  #fdt-calculator .fdt-result-grid{\r\n    grid-template-columns:1fr;\r\n  }\r\n\r\n  #fdt-calculator .fdt-result{\r\n    border-right:0;\r\n    border-bottom:1px solid var(--border);\r\n  }\r\n\r\n  #fdt-calculator .fdt-result:last-child{\r\n    border-bottom:0;\r\n  }\r\n}\r\n<\/style>\r\n\r\n\r\n<link\r\n  rel=\"stylesheet\"\r\n  href=\"https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.css\"\r\n>\r\n\r\n\r\n<div class=\"fdt-wrap\">\r\n\r\n  <div class=\"fdt-header\">\r\n    <h2>Freight Distance &amp; Transit Calculator<\/h2>\r\n    <p>\r\n      Search the Excel port database by port, UN\/LOCODE,\r\n      country or region, then calculate the estimated route.\r\n    <\/p>\r\n  <\/div>\r\n\r\n\r\n  <div class=\"fdt-content\">\r\n\r\n    <!-- =====================================================\r\n         SIDEBAR\r\n    ====================================================== -->\r\n\r\n    <div class=\"fdt-sidebar\">\r\n\r\n      <!-- ORIGIN -->\r\n\r\n      <div class=\"fdt-field fdt-origin\">\r\n\r\n        <label>\r\n          Origin \/ Pickup Location\r\n        <\/label>\r\n\r\n        <div class=\"fdt-autocomplete\">\r\n\r\n          <input\r\n            id=\"fdt-origin\"\r\n            type=\"text\"\r\n            placeholder=\"Search port, UN\/LOCODE, city or country...\"\r\n            autocomplete=\"off\"\r\n          >\r\n\r\n          <div\r\n            id=\"fdt-origin-suggestions\"\r\n            class=\"fdt-suggestions\">\r\n          <\/div>\r\n\r\n        <\/div>\r\n\r\n        <div\r\n          id=\"fdt-origin-status\"\r\n          class=\"fdt-status\">\r\n          Loading Excel port database...\r\n        <\/div>\r\n\r\n        <button\r\n          type=\"button\"\r\n          id=\"fdt-swap\"\r\n          class=\"fdt-swap\"\r\n          title=\"Swap locations\">\r\n          \u21c5\r\n        <\/button>\r\n\r\n      <\/div>\r\n\r\n\r\n      <!-- DESTINATION -->\r\n\r\n      <div class=\"fdt-field\">\r\n\r\n        <label>\r\n          Destination \/ Delivery Location\r\n        <\/label>\r\n\r\n        <div class=\"fdt-autocomplete\">\r\n\r\n          <input\r\n            id=\"fdt-destination\"\r\n            type=\"text\"\r\n            placeholder=\"Search port, UN\/LOCODE, city or country...\"\r\n            autocomplete=\"off\"\r\n          >\r\n\r\n          <div\r\n            id=\"fdt-destination-suggestions\"\r\n            class=\"fdt-suggestions\">\r\n          <\/div>\r\n\r\n        <\/div>\r\n\r\n        <div\r\n          id=\"fdt-destination-status\"\r\n          class=\"fdt-status\">\r\n          Use a result from the Excel database for exact port coordinates.\r\n        <\/div>\r\n\r\n      <\/div>\r\n\r\n\r\n      <!-- TRANSPORT -->\r\n\r\n      <div class=\"fdt-transport\">\r\n\r\n        <label>\r\n          Mode of Transport\r\n        <\/label>\r\n\r\n        <select id=\"fdt-transport-mode\">\r\n\r\n          <option value=\"road\">\r\n            \ud83d\ude9b Road Freight\r\n          <\/option>\r\n\r\n          <option value=\"rail\">\r\n            \ud83d\ude86 Rail Freight\r\n          <\/option>\r\n\r\n          <option value=\"air\">\r\n            \u2708\ufe0f Air Freight\r\n          <\/option>\r\n\r\n          <option value=\"sea\">\r\n            \ud83d\udea2 Sea Freight\r\n          <\/option>\r\n\r\n        <\/select>\r\n\r\n      <\/div>\r\n\r\n\r\n      <div id=\"fdt-error\" class=\"fdt-error\"><\/div>\r\n\r\n\r\n      <!-- ACTIONS -->\r\n\r\n      <div class=\"fdt-actions\">\r\n\r\n        <button\r\n          type=\"button\"\r\n          id=\"fdt-calculate\"\r\n          class=\"fdt-calculate\">\r\n          Calculate Distance &amp; Time\r\n        <\/button>\r\n\r\n        <button\r\n          type=\"button\"\r\n          id=\"fdt-current\"\r\n          class=\"fdt-current\">\r\n          \u25ce Use My Current Location\r\n        <\/button>\r\n\r\n      <\/div>\r\n\r\n    <\/div>\r\n\r\n\r\n    <!-- =====================================================\r\n         MAP\r\n    ====================================================== -->\r\n\r\n    <div class=\"fdt-map-wrapper\">\r\n\r\n      <div\r\n        id=\"fdt-map\"\r\n        class=\"fdt-map\">\r\n      <\/div>\r\n\r\n\r\n      <div\r\n        id=\"fdt-loading\"\r\n        class=\"fdt-loading\">\r\n\r\n        <div class=\"fdt-loading-box\">\r\n          Calculating route...\r\n        <\/div>\r\n\r\n      <\/div>\r\n\r\n\r\n      <!-- RESULTS -->\r\n\r\n      <div class=\"fdt-results\">\r\n\r\n        <div class=\"fdt-result-grid\">\r\n\r\n          <div class=\"fdt-result\">\r\n\r\n            <small>\r\n              Distance\r\n            <\/small>\r\n\r\n            <strong id=\"fdt-distance\">\r\n              \u2014\r\n            <\/strong>\r\n\r\n            <span>\r\n              km\r\n            <\/span>\r\n\r\n          <\/div>\r\n\r\n\r\n          <div class=\"fdt-result\">\r\n\r\n            <small>\r\n              Transit Time\r\n            <\/small>\r\n\r\n            <strong id=\"fdt-time\">\r\n              \u2014\r\n            <\/strong>\r\n\r\n            <span>\r\n              estimated\r\n            <\/span>\r\n\r\n          <\/div>\r\n\r\n        <\/div>\r\n\r\n\r\n        <div\r\n          id=\"fdt-route-info\"\r\n          class=\"fdt-route-info\">\r\n          Select an origin and destination from the Excel port database.\r\n        <\/div>\r\n\r\n      <\/div>\r\n\r\n    <\/div>\r\n\r\n  <\/div>\r\n\r\n<\/div>\r\n\r\n\r\n<script>\r\n\r\n(function(){\r\n\r\n\"use strict\";\r\n\r\n\r\n\/* =========================================================\r\n   LOAD LEAFLET\r\n========================================================= *\/\r\n\r\nfunction loadLeaflet(callback){\r\n\r\n  if(typeof window.L !== \"undefined\"){\r\n    callback();\r\n    return;\r\n  }\r\n\r\n  var script=document.createElement(\"script\");\r\n\r\n  script.src=\r\n    \"https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.js\";\r\n\r\n  script.onload=callback;\r\n\r\n  script.onerror=function(){\r\n    console.error(\"Leaflet failed to load.\");\r\n  };\r\n\r\n  document.head.appendChild(script);\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   START CALCULATOR\r\n========================================================= *\/\r\n\r\nfunction startCalculator(){\r\n\r\n  var root=\r\n    document.getElementById(\"fdt-calculator\");\r\n\r\n  if(!root)return;\r\n\r\n\r\n  var mapElement=\r\n    root.querySelector(\"#fdt-map\");\r\n\r\n  if(!mapElement)return;\r\n\r\n\r\n  if(mapElement._leaflet_id)return;\r\n\r\n\r\n  \/* =======================================================\r\n     ELEMENTS\r\n  ======================================================= *\/\r\n\r\n  var origin=\r\n    root.querySelector(\"#fdt-origin\");\r\n\r\n  var destination=\r\n    root.querySelector(\"#fdt-destination\");\r\n\r\n  var originSuggestions=\r\n    root.querySelector(\"#fdt-origin-suggestions\");\r\n\r\n  var destinationSuggestions=\r\n    root.querySelector(\"#fdt-destination-suggestions\");\r\n\r\n  var originStatus=\r\n    root.querySelector(\"#fdt-origin-status\");\r\n\r\n  var destinationStatus=\r\n    root.querySelector(\"#fdt-destination-status\");\r\n\r\n  var transport=\r\n    root.querySelector(\"#fdt-transport-mode\");\r\n\r\n  var calculateButton=\r\n    root.querySelector(\"#fdt-calculate\");\r\n\r\n  var swapButton=\r\n    root.querySelector(\"#fdt-swap\");\r\n\r\n  var currentButton=\r\n    root.querySelector(\"#fdt-current\");\r\n\r\n  var loading=\r\n    root.querySelector(\"#fdt-loading\");\r\n\r\n  var error=\r\n    root.querySelector(\"#fdt-error\");\r\n\r\n\r\n  \/* =======================================================\r\n     MAP\r\n  ======================================================= *\/\r\n\r\n  var map=\r\n    L.map(mapElement,{\r\n      center:[20,0],\r\n      zoom:2,\r\n      scrollWheelZoom:true\r\n    });\r\n\r\n\r\n  L.tileLayer(\r\n    \"https:\/\/{s}.tile.openstreetmap.org\/{z}\/{x}\/{y}.png\",\r\n    {\r\n      maxZoom:19,\r\n      attribution:\r\n        \"&copy; OpenStreetMap contributors\"\r\n    }\r\n  ).addTo(map);\r\n\r\n\r\n  \/* =======================================================\r\n     VARIABLES\r\n  ======================================================= *\/\r\n\r\n  var originPoint=null;\r\n  var destinationPoint=null;\r\n\r\n  var originMarker=null;\r\n  var destinationMarker=null;\r\n\r\n  var routeLayer=null;\r\n\r\n  var ports=[];\r\n\r\n  var portReady=false;\r\n\r\n  var speeds={\r\n    road:35,\r\n    rail:45,\r\n    air:800,\r\n    sea:13\r\n  };\r\n\r\n\r\n  \/* =======================================================\r\n     ERROR\r\n  ======================================================= *\/\r\n\r\n  function showError(message){\r\n\r\n    error.textContent=message;\r\n\r\n    error.classList.add(\"show\");\r\n\r\n  }\r\n\r\n\r\n  function clearError(){\r\n\r\n    error.textContent=\"\";\r\n\r\n    error.classList.remove(\"show\");\r\n\r\n  }\r\n\r\n\r\n  \/* =======================================================\r\n     ESCAPE HTML\r\n  ======================================================= *\/\r\n\r\n  function escapeHTML(value){\r\n\r\n    return String(value||\"\")\r\n      .replace(\/&\/g,\"&amp;\")\r\n      .replace(\/<\/g,\"&lt;\")\r\n      .replace(\/>\/g,\"&gt;\")\r\n      .replace(\/\"\/g,\"&quot;\")\r\n      .replace(\/'\/g,\"&#039;\");\r\n\r\n  }\r\n\r\n\r\n  \/* =======================================================\r\n     LOAD PORT DATABASE\r\n  ======================================================= *\/\r\n\r\n  fetch(\r\n    \"https:\/\/a2jobs.com\/Vanship_website_preview\/wp-content\/uploads\/2026\/09\/World_Ports_Batch2.json\",\r\n    {\r\n      cache:\"no-store\"\r\n    }\r\n  )\r\n\r\n  .then(function(response){\r\n\r\n    if(!response.ok){\r\n\r\n      throw new Error(\r\n        \"Port database file could not be loaded.\"\r\n      );\r\n\r\n    }\r\n\r\n    return response.json();\r\n\r\n  })\r\n\r\n  .then(function(data){\r\n\r\n    ports=\r\n      Array.isArray(data)\r\n        ? data\r\n        : [];\r\n\r\n    portReady=true;\r\n\r\n\r\n    originStatus.textContent=\r\n      \"\u2693 Excel database loaded \u2022 \"+\r\n      ports.length.toLocaleString(\"en-IN\")+\r\n      \" locations\";\r\n\r\n\r\n    destinationStatus.textContent=\r\n      \"Search port name, UN\/LOCODE, country or region.\";\r\n\r\n  })\r\n\r\n  .catch(function(err){\r\n\r\n    originStatus.textContent=\r\n      \"Port database failed to load.\";\r\n\r\n    showError(\r\n      \"Unable to load the Excel port database.\"\r\n    );\r\n\r\n    console.error(err);\r\n\r\n  });\r\n\r\n\r\n  \/* =======================================================\r\n     SEARCHABLE TEXT\r\n  ======================================================= *\/\r\n\r\n  function searchableText(p){\r\n\r\n    return [\r\n\r\n      p[\"Port \/ location\"],\r\n      p[\"Local name\"],\r\n      p[\"UN\/LOCODE\"],\r\n      p.Country,\r\n      p.ISO2,\r\n      p.Region,\r\n      p.Subdivision,\r\n      p[\"Geo focus\"],\r\n      p[\"Vanchile tier\"],\r\n      p.Type\r\n\r\n    ]\r\n\r\n    .filter(Boolean)\r\n    .join(\" \")\r\n    .toLowerCase();\r\n\r\n  }\r\n\r\n\r\n  \/* =======================================================\r\n     SEARCH PORTS\r\n  ======================================================= *\/\r\n\r\n  function searchPorts(query){\r\n\r\n    query=\r\n      query.trim().toLowerCase();\r\n\r\n    if(!query)return[];\r\n\r\n    var words=\r\n      query\r\n        .split(\/\\s+\/)\r\n        .filter(Boolean);\r\n\r\n\r\n    return ports\r\n\r\n      .map(function(p){\r\n\r\n        var text=\r\n          searchableText(p);\r\n\r\n        var name=\r\n          String(\r\n            p[\"Port \/ location\"]||\"\"\r\n          ).toLowerCase();\r\n\r\n        var code=\r\n          String(\r\n            p[\"UN\/LOCODE\"]||\"\"\r\n          ).toLowerCase();\r\n\r\n        var country=\r\n          String(\r\n            p.Country||\"\"\r\n          ).toLowerCase();\r\n\r\n        var score=0;\r\n\r\n\r\n        if(code===query)\r\n          score+=1000;\r\n\r\n        if(name===query)\r\n          score+=900;\r\n\r\n        if(country===query)\r\n          score+=800;\r\n\r\n        if(name.indexOf(query)===0)\r\n          score+=500;\r\n\r\n        if(code.indexOf(query)===0)\r\n          score+=450;\r\n\r\n        if(country.indexOf(query)===0)\r\n          score+=350;\r\n\r\n\r\n        words.forEach(function(word){\r\n\r\n          if(text.indexOf(word)>=0)\r\n            score+=20;\r\n\r\n        });\r\n\r\n\r\n        if(score===0)\r\n          return null;\r\n\r\n\r\n        if(\r\n          p.Type===\r\n          \"Seaport (UN\/LOCODE)\"\r\n        )\r\n          score+=10;\r\n\r\n\r\n        if(p.Maritime===\"Yes\")\r\n          score+=5;\r\n\r\n\r\n        return{\r\n          p:p,\r\n          score:score\r\n        };\r\n\r\n      })\r\n\r\n      .filter(Boolean)\r\n\r\n      .sort(function(a,b){\r\n\r\n        return b.score-a.score;\r\n\r\n      })\r\n\r\n      .slice(0,8)\r\n\r\n      .map(function(x){\r\n\r\n        return x.p;\r\n\r\n      });\r\n\r\n  }\r\n\r\n\r\n  \/* =======================================================\r\n     SELECT DATABASE PLACE\r\n  ======================================================= *\/\r\n\r\n  function selectPlace(\r\n    input,\r\n    status,\r\n    place\r\n  ){\r\n\r\n    var isPort=\r\n      place.Type===\r\n      \"Seaport (UN\/LOCODE)\"\r\n      ||\r\n      place.Maritime===\"Yes\";\r\n\r\n\r\n    var displayName=\r\n      place[\"Port \/ location\"]\r\n      ||\r\n      place[\"Local name\"]\r\n      ||\r\n      place[\"UN\/LOCODE\"]\r\n      ||\r\n      \"Unknown location\";\r\n\r\n\r\n    var local=\r\n      place[\"Local name\"]\r\n      &&\r\n      place[\"Local name\"]!==displayName\r\n        ? place[\"Local name\"]\r\n        : \"\";\r\n\r\n\r\n    var meta=[\r\n\r\n      local,\r\n      place.Region,\r\n      place.Country,\r\n      place[\"UN\/LOCODE\"]\r\n\r\n    ]\r\n\r\n    .filter(Boolean)\r\n    .join(\" \u2022 \");\r\n\r\n\r\n    input.value=\r\n      displayName;\r\n\r\n\r\n    input.dataset.lat=\r\n      place.Latitude==null\r\n        ? \"\"\r\n        : place.Latitude;\r\n\r\n\r\n    input.dataset.lng=\r\n      place.Longitude==null\r\n        ? \"\"\r\n        : place.Longitude;\r\n\r\n\r\n    input.dataset.display=\r\n      displayName+\r\n      \", \"+\r\n      [\r\n        place.Country,\r\n        place.Region\r\n      ]\r\n      .filter(Boolean)\r\n      .join(\", \");\r\n\r\n\r\n    input.dataset.type=\r\n      isPort\r\n        ? \"port\"\r\n        : \"location\";\r\n\r\n\r\n    input.dataset.code=\r\n      place[\"UN\/LOCODE\"]||\"\";\r\n\r\n\r\n    input.dataset.country=\r\n      place.Country||\"\";\r\n\r\n\r\n    input.dataset.region=\r\n      place.Region||\"\";\r\n\r\n\r\n    input.dataset.selected=\"1\";\r\n\r\n\r\n    if(status){\r\n\r\n      status.textContent=\r\n        (isPort\r\n          ? \"\u2693 Port selected\"\r\n          : \"\ud83d\udccd Location selected\"\r\n        )+\r\n        \" \u2022 \"+\r\n        meta;\r\n\r\n    }\r\n\r\n  }\r\n\r\n\r\n  \/* =======================================================\r\n     AUTOCOMPLETE\r\n  ======================================================= *\/\r\n\r\n  function setupAutocomplete(\r\n    input,\r\n    box,\r\n    status\r\n  ){\r\n\r\n    var selectedIndex=-1;\r\n    var timer=null;\r\n\r\n\r\n    function hide(){\r\n\r\n      box.classList.remove(\"show\");\r\n\r\n      selectedIndex=-1;\r\n\r\n    }\r\n\r\n\r\n    function renderSuggestions(items){\r\n\r\n      box.innerHTML=\"\";\r\n\r\n\r\n      if(!items.length){\r\n\r\n        box.innerHTML=\r\n          '<div class=\"fdt-suggestion-empty\">'+\r\n          'No matching Excel port\/location found.'+\r\n          '<\/div>';\r\n\r\n        box.classList.add(\"show\");\r\n\r\n        return;\r\n\r\n      }\r\n\r\n\r\n      items.forEach(function(place,index){\r\n\r\n        var button=\r\n          document.createElement(\"button\");\r\n\r\n\r\n        button.type=\"button\";\r\n\r\n        button.className=\r\n          \"fdt-suggestion\";\r\n\r\n\r\n        var isPort=\r\n          place.Type===\r\n          \"Seaport (UN\/LOCODE)\"\r\n          ||\r\n          place.Maritime===\"Yes\";\r\n\r\n\r\n        var displayName=\r\n          place[\"Port \/ location\"]\r\n          ||\r\n          place[\"Local name\"]\r\n          ||\r\n          place[\"UN\/LOCODE\"]\r\n          ||\r\n          \"Unknown location\";\r\n\r\n\r\n        var local=\r\n          place[\"Local name\"]\r\n          &&\r\n          place[\"Local name\"]!==displayName\r\n            ? place[\"Local name\"]\r\n            : \"\";\r\n\r\n\r\n        var meta=[\r\n\r\n          local,\r\n          place.Region,\r\n          place.Country,\r\n          place[\"UN\/LOCODE\"]\r\n\r\n        ]\r\n\r\n        .filter(Boolean)\r\n        .join(\" \u2022 \");\r\n\r\n\r\n        button.innerHTML=\r\n\r\n          '<span class=\"fdt-suggestion-icon\">'+\r\n            (isPort?\"\u2693\":\"\ud83d\udccd\")+\r\n          '<\/span>'+\r\n\r\n          '<span class=\"fdt-suggestion-content\">'+\r\n\r\n            '<span class=\"fdt-suggestion-name\">'+\r\n              escapeHTML(displayName)+\r\n            '<\/span>'+\r\n\r\n            '<span class=\"fdt-suggestion-meta\">'+\r\n              escapeHTML(meta)+\r\n            '<\/span>'+\r\n\r\n          '<\/span>'+\r\n\r\n          '<span class=\"fdt-suggestion-type\">'+\r\n            (isPort?\"PORT\":\"LOCATION\")+\r\n          '<\/span>';\r\n\r\n\r\n        button.addEventListener(\r\n          \"click\",\r\n          function(){\r\n\r\n            selectPlace(\r\n              input,\r\n              status,\r\n              place\r\n            );\r\n\r\n            hide();\r\n\r\n          }\r\n        );\r\n\r\n\r\n        box.appendChild(button);\r\n\r\n      });\r\n\r\n\r\n      box.classList.add(\"show\");\r\n\r\n    }\r\n\r\n\r\n    function search(){\r\n\r\n      var query=\r\n        input.value.trim();\r\n\r\n\r\n      if(query.length<2){\r\n\r\n        hide();\r\n\r\n        return;\r\n\r\n      }\r\n\r\n\r\n      delete input.dataset.lat;\r\n      delete input.dataset.lng;\r\n      delete input.dataset.display;\r\n      delete input.dataset.selected;\r\n\r\n\r\n      var results=\r\n        portReady\r\n          ? searchPorts(query)\r\n          : [];\r\n\r\n\r\n      renderSuggestions(results);\r\n\r\n    }\r\n\r\n\r\n    input.addEventListener(\r\n      \"input\",\r\n      function(){\r\n\r\n        clearTimeout(timer);\r\n\r\n        timer=\r\n          setTimeout(\r\n            search,\r\n            180\r\n          );\r\n\r\n      }\r\n    );\r\n\r\n\r\n    input.addEventListener(\r\n      \"keydown\",\r\n      function(e){\r\n\r\n        var items=\r\n          box.querySelectorAll(\r\n            \".fdt-suggestion\"\r\n          );\r\n\r\n\r\n        if(\r\n          e.key===\"Enter\"\r\n        ){\r\n\r\n          e.preventDefault();\r\n\r\n\r\n          \/*\r\n           * FIX:\r\n           * If the user presses Enter without\r\n           * manually clicking a suggestion,\r\n           * automatically select the first result.\r\n           *\/\r\n\r\n          if(\r\n            box.classList.contains(\"show\") &&\r\n            items.length\r\n          ){\r\n\r\n            if(\r\n              selectedIndex>=0 &&\r\n              items[selectedIndex]\r\n            ){\r\n\r\n              items[selectedIndex].click();\r\n\r\n            }else{\r\n\r\n              items[0].click();\r\n\r\n            }\r\n\r\n            return;\r\n\r\n          }\r\n\r\n\r\n          \/*\r\n           * If suggestions have not opened yet,\r\n           * resolve the typed location and calculate.\r\n           *\/\r\n\r\n          calculate();\r\n\r\n          return;\r\n\r\n        }\r\n\r\n\r\n        if(\r\n          !box.classList.contains(\"show\") ||\r\n          !items.length\r\n        ){\r\n\r\n          return;\r\n\r\n        }\r\n\r\n\r\n        if(e.key===\"ArrowDown\"){\r\n\r\n          e.preventDefault();\r\n\r\n          selectedIndex=\r\n            Math.min(\r\n              selectedIndex+1,\r\n              items.length-1\r\n            );\r\n\r\n        }\r\n\r\n\r\n        if(e.key===\"ArrowUp\"){\r\n\r\n          e.preventDefault();\r\n\r\n          selectedIndex=\r\n            Math.max(\r\n              selectedIndex-1,\r\n              0\r\n            );\r\n\r\n        }\r\n\r\n\r\n        items.forEach(\r\n          function(item,i){\r\n\r\n            item.classList.toggle(\r\n              \"active\",\r\n              i===selectedIndex\r\n            );\r\n\r\n          }\r\n        );\r\n\r\n      }\r\n    );\r\n\r\n\r\n    input.addEventListener(\r\n      \"focus\",\r\n      function(){\r\n\r\n        if(\r\n          input.value.trim().length>=2\r\n        ){\r\n\r\n          search();\r\n\r\n        }\r\n\r\n      }\r\n    );\r\n\r\n\r\n    document.addEventListener(\r\n      \"click\",\r\n      function(e){\r\n\r\n        if(\r\n          !input.contains(e.target) &&\r\n          !box.contains(e.target)\r\n        ){\r\n\r\n          hide();\r\n\r\n        }\r\n\r\n      }\r\n    );\r\n\r\n  }\r\n\r\n\r\n  setupAutocomplete(\r\n    origin,\r\n    originSuggestions,\r\n    originStatus\r\n  );\r\n\r\n\r\n  setupAutocomplete(\r\n    destination,\r\n    destinationSuggestions,\r\n    destinationStatus\r\n  );\r\n\r\n\r\n  \/* =======================================================\r\n     GET LOCATION\r\n     \r\n     FIXED:\r\n     Automatically use the best matching database\r\n     result if the user typed a location but did\r\n     not click the suggestion.\r\n  ======================================================= *\/\r\n\r\n  function getLocation(input,status){\r\n\r\n    if(\r\n      input.dataset.selected===\"1\" &&\r\n      input.dataset.lat!==undefined &&\r\n      input.dataset.lat!==\"\" &&\r\n      input.dataset.lng!==undefined &&\r\n      input.dataset.lng!==\"\"\r\n    ){\r\n\r\n      var lat=\r\n        Number(input.dataset.lat);\r\n\r\n      var lng=\r\n        Number(input.dataset.lng);\r\n\r\n\r\n      if(\r\n        Number.isFinite(lat) &&\r\n        Number.isFinite(lng)\r\n      ){\r\n\r\n        return Promise.resolve({\r\n\r\n          lat:lat,\r\n          lng:lng,\r\n          name:\r\n            input.dataset.display ||\r\n            input.value\r\n\r\n        });\r\n\r\n      }\r\n\r\n    }\r\n\r\n\r\n    \/*\r\n     * NEW FALLBACK:\r\n     * Resolve typed text against Excel database.\r\n     *\/\r\n\r\n    if(portReady){\r\n\r\n      var query=\r\n        input.value.trim();\r\n\r\n      var matches=\r\n        searchPorts(query);\r\n\r\n\r\n      if(matches.length){\r\n\r\n        var place=\r\n          matches[0];\r\n\r\n\r\n        selectPlace(\r\n          input,\r\n          status,\r\n          place\r\n        );\r\n\r\n\r\n        var lat=\r\n          Number(place.Latitude);\r\n\r\n        var lng=\r\n          Number(place.Longitude);\r\n\r\n\r\n        if(\r\n          Number.isFinite(lat) &&\r\n          Number.isFinite(lng)\r\n        ){\r\n\r\n          return Promise.resolve({\r\n\r\n            lat:lat,\r\n            lng:lng,\r\n            name:\r\n              input.dataset.display ||\r\n              input.value\r\n\r\n          });\r\n\r\n        }\r\n\r\n      }\r\n\r\n    }\r\n\r\n\r\n    return Promise.reject(\r\n      new Error(\r\n        \"Please select a valid location from the Excel database suggestions.\"\r\n      )\r\n    );\r\n\r\n  }\r\n\r\n\r\n  \/* =======================================================\r\n     DISTANCE\r\n  ======================================================= *\/\r\n\r\n  function distanceBetween(a,b){\r\n\r\n    var R=6371;\r\n\r\n    var lat1=\r\n      a.lat*Math.PI\/180;\r\n\r\n    var lat2=\r\n      b.lat*Math.PI\/180;\r\n\r\n\r\n    var dLat=\r\n      (b.lat-a.lat)*\r\n      Math.PI\/180;\r\n\r\n\r\n    var dLng=\r\n      (b.lng-a.lng)*\r\n      Math.PI\/180;\r\n\r\n\r\n    var x=\r\n      Math.sin(dLat\/2)**2+\r\n      Math.cos(lat1)*\r\n      Math.cos(lat2)*\r\n      Math.sin(dLng\/2)**2;\r\n\r\n\r\n    return R*\r\n      2*\r\n      Math.atan2(\r\n        Math.sqrt(x),\r\n        Math.sqrt(1-x)\r\n      );\r\n\r\n  }\r\n\r\n\r\n  \/* =======================================================\r\n     CLEAR MAP LAYERS\r\n  ======================================================= *\/\r\n\r\n  function clearLayers(){\r\n\r\n    if(routeLayer){\r\n\r\n      map.removeLayer(\r\n        routeLayer\r\n      );\r\n\r\n      routeLayer=null;\r\n\r\n    }\r\n\r\n\r\n    if(originMarker){\r\n\r\n      map.removeLayer(\r\n        originMarker\r\n      );\r\n\r\n      originMarker=null;\r\n\r\n    }\r\n\r\n\r\n    if(destinationMarker){\r\n\r\n      map.removeLayer(\r\n        destinationMarker\r\n      );\r\n\r\n      destinationMarker=null;\r\n\r\n    }\r\n\r\n  }\r\n\r\n\r\n  \/* =======================================================\r\n     MARKER\r\n  ======================================================= *\/\r\n\r\n  function createMarker(\r\n    point,\r\n    label\r\n  ){\r\n\r\n    return L.marker([\r\n      point.lat,\r\n      point.lng\r\n    ])\r\n\r\n    .bindPopup(\r\n      \"<strong>\"+\r\n      escapeHTML(label)+\r\n      \"<\/strong><br>\"+\r\n      escapeHTML(point.name)\r\n    )\r\n\r\n    .addTo(map);\r\n\r\n  }\r\n\r\n\r\n  \/* =======================================================\r\n     ROAD ROUTE\r\n  ======================================================= *\/\r\n\r\n  async function roadRoute(){\r\n\r\n    var coordinates=\r\n      originPoint.lng+\r\n      \",\"+\r\n      originPoint.lat+\r\n      \";\"+\r\n      destinationPoint.lng+\r\n      \",\"+\r\n      destinationPoint.lat;\r\n\r\n\r\n    var url=\r\n      \"https:\/\/router.project-osrm.org\/route\/v1\/driving\/\"+\r\n      coordinates+\r\n      \"?overview=full&geometries=geojson\";\r\n\r\n\r\n    var response=\r\n      await fetch(url);\r\n\r\n\r\n    if(!response.ok){\r\n\r\n      throw new Error(\r\n        \"Road routing service is unavailable.\"\r\n      );\r\n\r\n    }\r\n\r\n\r\n    var data=\r\n      await response.json();\r\n\r\n\r\n    if(\r\n      !data ||\r\n      data.code!==\"Ok\" ||\r\n      !data.routes ||\r\n      !data.routes.length\r\n    ){\r\n\r\n      throw new Error(\r\n        \"No road route was found.\"\r\n      );\r\n\r\n    }\r\n\r\n\r\n    return data.routes[0];\r\n\r\n  }\r\n\r\n\r\n  \/* =======================================================\r\n     FALLBACK ROUTE\r\n     \r\n     Used if OSRM cannot find a road route.\r\n  ======================================================= *\/\r\n\r\n  function createFallbackRoute(){\r\n\r\n    var fallbackDistance=\r\n      distanceBetween(\r\n        originPoint,\r\n        destinationPoint\r\n      );\r\n\r\n\r\n    \/*\r\n     * Approximate road distance.\r\n     * 1.25 factor gives a practical estimate\r\n     * when an actual road route cannot be obtained.\r\n     *\/\r\n\r\n    fallbackDistance*=1.25;\r\n\r\n\r\n    routeLayer=\r\n      L.polyline(\r\n\r\n        [\r\n          [\r\n            originPoint.lat,\r\n            originPoint.lng\r\n          ],\r\n          [\r\n            destinationPoint.lat,\r\n            destinationPoint.lng\r\n          ]\r\n        ],\r\n\r\n        {\r\n          color:\"#F96C0A\",\r\n          weight:5,\r\n          opacity:.9,\r\n          dashArray:\"9 8\"\r\n        }\r\n\r\n      )\r\n\r\n      .addTo(map);\r\n\r\n\r\n    return fallbackDistance;\r\n\r\n  }\r\n\r\n\r\n  \/* =======================================================\r\n     FORMAT TIME\r\n  ======================================================= *\/\r\n\r\n  function formatTime(hours){\r\n\r\n    var minutes=\r\n      Math.max(\r\n        0,\r\n        Math.round(hours*60)\r\n      );\r\n\r\n\r\n    if(minutes<60)\r\n      return minutes+\" min\";\r\n\r\n\r\n    var days=\r\n      Math.floor(\r\n        minutes\/1440\r\n      );\r\n\r\n\r\n    var remaining=\r\n      minutes%1440;\r\n\r\n\r\n    var h=\r\n      Math.floor(\r\n        remaining\/60\r\n      );\r\n\r\n\r\n    var m=\r\n      remaining%60;\r\n\r\n\r\n    var output=\"\";\r\n\r\n\r\n    if(days)\r\n      output+=days+\"d \";\r\n\r\n\r\n    if(h)\r\n      output+=h+\"h \";\r\n\r\n\r\n    if(m)\r\n      output+=m+\"m\";\r\n\r\n\r\n    return(\r\n      output.trim() ||\r\n      \"0 min\"\r\n    );\r\n\r\n  }\r\n\r\n\r\n  \/* =======================================================\r\n     CALCULATE\r\n  ======================================================= *\/\r\n\r\n  async function calculate(){\r\n\r\n    clearError();\r\n\r\n\r\n    if(\r\n      !origin.value.trim()\r\n    ){\r\n\r\n      showError(\r\n        \"Please enter an origin location.\"\r\n      );\r\n\r\n      origin.focus();\r\n\r\n      return;\r\n\r\n    }\r\n\r\n\r\n    if(\r\n      !destination.value.trim()\r\n    ){\r\n\r\n      showError(\r\n        \"Please enter a destination location.\"\r\n      );\r\n\r\n      destination.focus();\r\n\r\n      return;\r\n\r\n    }\r\n\r\n\r\n    if(!portReady){\r\n\r\n      showError(\r\n        \"The Excel port database is still loading. Please wait a moment.\"\r\n      );\r\n\r\n      return;\r\n\r\n    }\r\n\r\n\r\n    loading.classList.add(\"show\");\r\n\r\n\r\n    try{\r\n\r\n      \/*\r\n       * Automatically resolve typed locations.\r\n       *\/\r\n\r\n      originPoint=\r\n        await getLocation(\r\n          origin,\r\n          originStatus\r\n        );\r\n\r\n\r\n      destinationPoint=\r\n        await getLocation(\r\n          destination,\r\n          destinationStatus\r\n        );\r\n\r\n\r\n      clearLayers();\r\n\r\n\r\n      \/*\r\n       * Add markers.\r\n       *\/\r\n\r\n      originMarker=\r\n        createMarker(\r\n          originPoint,\r\n          \"Origin \/ Pickup\"\r\n        );\r\n\r\n\r\n      destinationMarker=\r\n        createMarker(\r\n          destinationPoint,\r\n          \"Destination \/ Delivery\"\r\n        );\r\n\r\n\r\n      var selectedMode=\r\n        transport.value;\r\n\r\n\r\n      var distanceKm=0;\r\n\r\n      var description=\"\";\r\n\r\n\r\n      \/* ===================================================\r\n         ROAD\r\n      =================================================== *\/\r\n\r\n      if(selectedMode===\"road\"){\r\n\r\n        try{\r\n\r\n          var route=\r\n            await roadRoute();\r\n\r\n\r\n          distanceKm=\r\n            route.distance\/1000;\r\n\r\n\r\n          if(\r\n            route.geometry &&\r\n            route.geometry.coordinates\r\n          ){\r\n\r\n            routeLayer=\r\n              L.geoJSON(\r\n                route.geometry,\r\n                {\r\n                  style:{\r\n                    color:\"#F96C0A\",\r\n                    weight:5,\r\n                    opacity:.9\r\n                  }\r\n                }\r\n              )\r\n\r\n              .addTo(map);\r\n\r\n          }\r\n\r\n\r\n          description=\r\n            \"Actual road routing\";\r\n\r\n\r\n        }catch(routeError){\r\n\r\n          \/*\r\n           * FIX:\r\n           * OSRM failure no longer leaves the map blank.\r\n           *\/\r\n\r\n          console.warn(\r\n            \"OSRM route unavailable. Using estimated route.\",\r\n            routeError\r\n          );\r\n\r\n\r\n          distanceKm=\r\n            createFallbackRoute();\r\n\r\n\r\n          description=\r\n            \"Estimated road distance\";\r\n\r\n        }\r\n\r\n      }\r\n\r\n\r\n      \/* ===================================================\r\n         OTHER MODES\r\n      =================================================== *\/\r\n\r\n      else{\r\n\r\n        distanceKm=\r\n          distanceBetween(\r\n            originPoint,\r\n            destinationPoint\r\n          );\r\n\r\n\r\n        routeLayer=\r\n          L.polyline(\r\n\r\n            [\r\n              [\r\n                originPoint.lat,\r\n                originPoint.lng\r\n              ],\r\n              [\r\n                destinationPoint.lat,\r\n                destinationPoint.lng\r\n              ]\r\n            ],\r\n\r\n            {\r\n              color:\"#F96C0A\",\r\n              weight:5,\r\n              opacity:.9,\r\n              dashArray:\"9 8\"\r\n            }\r\n\r\n          )\r\n\r\n          .addTo(map);\r\n\r\n\r\n        if(selectedMode===\"air\"){\r\n\r\n          description=\r\n            \"Great-circle air distance\";\r\n\r\n        }\r\n\r\n        else if(selectedMode===\"rail\"){\r\n\r\n          distanceKm*=1.25;\r\n\r\n          description=\r\n            \"Estimated rail distance\";\r\n\r\n        }\r\n\r\n        else{\r\n\r\n          distanceKm*=1.15;\r\n\r\n          description=\r\n            \"Estimated sea distance (not a nautical routing engine)\";\r\n\r\n        }\r\n\r\n      }\r\n\r\n\r\n      \/* ===================================================\r\n         TRANSIT TIME\r\n      =================================================== *\/\r\n\r\n      var selectedSpeed=\r\n        speeds[selectedMode];\r\n\r\n\r\n      var speedKmh=\r\n        selectedMode===\"sea\"\r\n          ? selectedSpeed*1.852\r\n          : selectedSpeed;\r\n\r\n\r\n      var timeHours=\r\n        distanceKm\/speedKmh;\r\n\r\n\r\n      \/* ===================================================\r\n         UPDATE RESULTS\r\n      =================================================== *\/\r\n\r\n      root.querySelector(\r\n        \"#fdt-distance\"\r\n      ).textContent=\r\n        distanceKm.toLocaleString(\r\n          \"en-IN\",\r\n          {\r\n            maximumFractionDigits:1\r\n          }\r\n        );\r\n\r\n\r\n      root.querySelector(\r\n        \"#fdt-time\"\r\n      ).textContent=\r\n        formatTime(\r\n          timeHours\r\n        );\r\n\r\n\r\n      root.querySelector(\r\n        \"#fdt-route-info\"\r\n      ).textContent=\r\n        selectedMode.toUpperCase()+\r\n        \" \u2022 \"+\r\n        description;\r\n\r\n\r\n      \/* ===================================================\r\n         FIT MAP\r\n      =================================================== *\/\r\n\r\n      setTimeout(\r\n        function(){\r\n\r\n          map.invalidateSize();\r\n\r\n\r\n          if(routeLayer){\r\n\r\n            try{\r\n\r\n              var bounds=\r\n                routeLayer.getBounds();\r\n\r\n\r\n              if(\r\n                bounds &&\r\n                bounds.isValid()\r\n              ){\r\n\r\n                map.fitBounds(\r\n                  bounds,\r\n                  {\r\n                    padding:[40,40],\r\n                    maxZoom:13\r\n                  }\r\n                );\r\n\r\n                return;\r\n\r\n              }\r\n\r\n            }catch(e){\r\n\r\n              console.warn(\r\n                \"Could not fit route bounds.\",\r\n                e\r\n              );\r\n\r\n            }\r\n\r\n          }\r\n\r\n\r\n          map.fitBounds(\r\n\r\n            [\r\n              [\r\n                originPoint.lat,\r\n                originPoint.lng\r\n              ],\r\n              [\r\n                destinationPoint.lat,\r\n                destinationPoint.lng\r\n              ]\r\n            ],\r\n\r\n            {\r\n              padding:[40,40]\r\n            }\r\n\r\n          );\r\n\r\n        },\r\n        200\r\n      );\r\n\r\n\r\n    }catch(err){\r\n\r\n      console.error(\r\n        \"FDT Calculator:\",\r\n        err\r\n      );\r\n\r\n\r\n      showError(\r\n        err.message ||\r\n        \"Unable to calculate the route.\"\r\n      );\r\n\r\n\r\n    }finally{\r\n\r\n      loading.classList.remove(\r\n        \"show\"\r\n      );\r\n\r\n    }\r\n\r\n  }\r\n\r\n\r\n  \/* =======================================================\r\n     SWAP\r\n  ======================================================= *\/\r\n\r\n  swapButton.addEventListener(\r\n    \"click\",\r\n    function(){\r\n\r\n      var originValue=\r\n        origin.value;\r\n\r\n\r\n      var originData={\r\n\r\n        lat:\r\n          origin.dataset.lat,\r\n\r\n        lng:\r\n          origin.dataset.lng,\r\n\r\n        display:\r\n          origin.dataset.display,\r\n\r\n        type:\r\n          origin.dataset.type,\r\n\r\n        code:\r\n          origin.dataset.code,\r\n\r\n        country:\r\n          origin.dataset.country,\r\n\r\n        region:\r\n          origin.dataset.region,\r\n\r\n        selected:\r\n          origin.dataset.selected\r\n\r\n      };\r\n\r\n\r\n      var destinationValue=\r\n        destination.value;\r\n\r\n\r\n      var destinationData={\r\n\r\n        lat:\r\n          destination.dataset.lat,\r\n\r\n        lng:\r\n          destination.dataset.lng,\r\n\r\n        display:\r\n          destination.dataset.display,\r\n\r\n        type:\r\n          destination.dataset.type,\r\n\r\n        code:\r\n          destination.dataset.code,\r\n\r\n        country:\r\n          destination.dataset.country,\r\n\r\n        region:\r\n          destination.dataset.region,\r\n\r\n        selected:\r\n          destination.dataset.selected\r\n\r\n      };\r\n\r\n\r\n      origin.value=\r\n        destinationValue;\r\n\r\n\r\n      destination.value=\r\n        originValue;\r\n\r\n\r\n      function copyData(\r\n        to,\r\n        from\r\n      ){\r\n\r\n        [\r\n          \"lat\",\r\n          \"lng\",\r\n          \"display\",\r\n          \"type\",\r\n          \"code\",\r\n          \"country\",\r\n          \"region\",\r\n          \"selected\"\r\n        ]\r\n\r\n        .forEach(\r\n          function(k){\r\n\r\n            if(\r\n              from[k]!==undefined &&\r\n              from[k]!==\"\"\r\n            ){\r\n\r\n              to.dataset[k]=\r\n                from[k];\r\n\r\n            }else{\r\n\r\n              delete to.dataset[k];\r\n\r\n            }\r\n\r\n          }\r\n        );\r\n\r\n      }\r\n\r\n\r\n      copyData(\r\n        origin,\r\n        destinationData\r\n      );\r\n\r\n\r\n      copyData(\r\n        destination,\r\n        originData\r\n      );\r\n\r\n\r\n      originStatus.textContent=\r\n        \"Locations swapped.\";\r\n\r\n\r\n      destinationStatus.textContent=\r\n        \"Locations swapped.\";\r\n\r\n    }\r\n  );\r\n\r\n\r\n  \/* =======================================================\r\n     CURRENT LOCATION\r\n  ======================================================= *\/\r\n\r\n  currentButton.addEventListener(\r\n    \"click\",\r\n    function(){\r\n\r\n      clearError();\r\n\r\n\r\n      if(\r\n        !navigator.geolocation\r\n      ){\r\n\r\n        showError(\r\n          \"Geolocation is not supported by your browser.\"\r\n        );\r\n\r\n        return;\r\n\r\n      }\r\n\r\n\r\n      currentButton.textContent=\r\n        \"Locating...\";\r\n\r\n\r\n      navigator.geolocation.getCurrentPosition(\r\n\r\n        function(position){\r\n\r\n          origin.value=\r\n            \"My Current Location\";\r\n\r\n\r\n          origin.dataset.lat=\r\n            position.coords.latitude;\r\n\r\n\r\n          origin.dataset.lng=\r\n            position.coords.longitude;\r\n\r\n\r\n          origin.dataset.display=\r\n            \"My Current Location\";\r\n\r\n\r\n          origin.dataset.type=\r\n            \"location\";\r\n\r\n\r\n          origin.dataset.selected=\r\n            \"1\";\r\n\r\n\r\n          originStatus.textContent=\r\n            \"\u25ce Current location selected.\";\r\n\r\n\r\n          currentButton.textContent=\r\n            \"\u25ce Use My Current Location\";\r\n\r\n\r\n          map.setView(\r\n\r\n            [\r\n              position.coords.latitude,\r\n              position.coords.longitude\r\n            ],\r\n\r\n            10\r\n\r\n          );\r\n\r\n        },\r\n\r\n\r\n        function(){\r\n\r\n          currentButton.textContent=\r\n            \"\u25ce Use My Current Location\";\r\n\r\n\r\n          showError(\r\n            \"Unable to access your current location. Please allow location permission.\"\r\n          );\r\n\r\n        },\r\n\r\n        {\r\n          enableHighAccuracy:true,\r\n          timeout:10000,\r\n          maximumAge:300000\r\n        }\r\n\r\n      );\r\n\r\n    }\r\n  );\r\n\r\n\r\n  \/* =======================================================\r\n     CALCULATE BUTTON\r\n  ======================================================= *\/\r\n\r\n  calculateButton.addEventListener(\r\n    \"click\",\r\n    calculate\r\n  );\r\n\r\n\r\n  \/* =======================================================\r\n     ENTER ON DESTINATION\r\n  ======================================================= *\/\r\n\r\n  destination.addEventListener(\r\n    \"keydown\",\r\n    function(e){\r\n\r\n      if(e.key===\"Enter\"){\r\n\r\n        e.preventDefault();\r\n\r\n        calculate();\r\n\r\n      }\r\n\r\n    }\r\n  );\r\n\r\n\r\n  \/* =======================================================\r\n     MAP SIZE\r\n  ======================================================= *\/\r\n\r\n  setTimeout(\r\n    function(){\r\n\r\n      map.invalidateSize();\r\n\r\n    },\r\n    500\r\n  );\r\n\r\n}\r\n\r\n\r\n\/* =========================================================\r\n   BOOT\r\n========================================================= *\/\r\n\r\nfunction boot(){\r\n\r\n  loadLeaflet(\r\n    startCalculator\r\n  );\r\n\r\n}\r\n\r\n\r\nif(\r\n  document.readyState===\"loading\"\r\n){\r\n\r\n  document.addEventListener(\r\n    \"DOMContentLoaded\",\r\n    boot\r\n  );\r\n\r\n}else{\r\n\r\n  boot();\r\n\r\n}\r\n\r\n})();\r\n<\/script>\r\n\r\n<\/div>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-50eb1d5 lqd-search-style-classic elementor-widget elementor-widget-lqd-search\" data-id=\"50eb1d5\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"lqd-search.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\n\t\t<button class=\"lqd-trigger lqd-togglable-trigger items-center bg-transparent border-none p-0 flex transition-all\">\n\t\t\t\t\t\t\t\t\t<span class=\"lqd-trigger-bars-shape flex items-center justify-center relative pointer-events-none transition-all\">\n\t\t\t<span class=\"flex flex-col relative\">\n\t\t\t\t<span class=\"lqd-trigger-bar lqd-trigger-bar-close lqd-trigger-bar-close-1 absolute top-1\/2 start-1\/2 bg-current transition-transform\"><\/span>\n\t\t\t\t<span class=\"lqd-trigger-bar lqd-trigger-bar-close lqd-trigger-bar-close-2 absolute top-1\/2 start-1\/2 bg-current transition-transform\"><\/span>\n\t\t\t\t<span class=\"lqd-trigger-bar lqd-trigger-bar-open transition-transform origin-right invisible\"><\/span>\n\t\t\t\t<span class=\"lqd-trigger-icon inline-flex items-center justify-center w-full h-full absolute top-0 start-0 transition-all\">\n\t\t\t\t\t<svg class=\"lqd-akar-icon w-1em h-auto lqd-menu-icon \" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"36\" height=\"36\" viewbox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 21l-4.486-4.494M19 10.5a8.5 8.5 0 1 1-17 0 8.5 8.5 0 0 1 17 0z\"><\/path><\/svg>\t\t\t\t<\/span>\n\t\t\t<\/span>\n\t\t<\/span>\n\t\t\t\t<\/button>\n\n\t\t\n\t\t<div class=\"lqd-dropdown lqd-search-dropdown lqd-togglable-element hidden w-full flex-col overflow-hidden backface-hidden [&amp;.lqd-is-active:flex] absolute end-0\">\n\t\t\t<div class=\"lqd-search-container flex flex-col justify-center h-full mx-auto backface-hidden\">\n\t\t\t\t<form class=\"lqd-search-form w-full relative leading-none\" role=\"search\" method=\"get\" action=\"https:\/\/vanchile.com\/\">\n\t\t\t\t\t<input class=\"lqd-search-input w-full transition-colors\" type=\"search\" placeholder=\"Start searching\" value=\"\" name=\"s\" \/>\n\t\t\t\t\t<span class=\"lqd-input-icon inline-block absolute top-1\/2 lqd-transform -translate-y-1\/2 start-0\">\n\t\t\t\t\t\t\t\t\t\t\t\t<svg class=\"w-1em h-1em\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"ai ai-Search\"><path d=\"M21 21l-4.486-4.494M19 10.5a8.5 8.5 0 1 1-17 0 8.5 8.5 0 0 1 17 0z\"\/><\/svg>\n\t\t\t\t\t\t\t\t\t\t\t<\/span>\n\t\t\t\t\t<input type=\"hidden\" name=\"post_type\" value=\"all\" \/>\n\t\t\t\t<\/form>\n\t\t\t<\/div>\n\t\t<\/div>\n\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<script>\n\t\twindow.liquid.behaviors.push( {\n\t\t\tdataId: \"50eb1d5\",\n\t\t\tbehaviors:[\n\t\t\t\t{behaviorName:'liquidToggle',options:{changePropPrefix:'lqdSearchToggle-50eb1d5',toggleAllTriggers:true,ignoreEnterOnFocus:true,toggleOffOnEscPress:true,toggleOffOnOutsideClick:true,triggerElements:['click @togglableTriggers']}},{behaviorName:'liquidEffectsSlideToggle',options:{changePropPrefix:'lqdSearchToggle-50eb1d5'}},\n\t\t\t]\n\t\t} );\n\t\t\n\t\t<\/script>\t\t<div class=\"elementor-element elementor-element-96c65c7 lqd-search-style-classic elementor-widget elementor-widget-lqd-search\" data-id=\"96c65c7\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"lqd-search.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\n\t\t<button class=\"lqd-trigger lqd-togglable-trigger items-center bg-transparent border-none p-0 flex transition-all\">\n\t\t\t\t\t\t\t\t\t<span class=\"lqd-trigger-bars-shape flex items-center justify-center relative pointer-events-none transition-all\">\n\t\t\t<span class=\"flex flex-col relative\">\n\t\t\t\t<span class=\"lqd-trigger-bar lqd-trigger-bar-close lqd-trigger-bar-close-1 absolute top-1\/2 start-1\/2 bg-current transition-transform\"><\/span>\n\t\t\t\t<span class=\"lqd-trigger-bar lqd-trigger-bar-close lqd-trigger-bar-close-2 absolute top-1\/2 start-1\/2 bg-current transition-transform\"><\/span>\n\t\t\t\t<span class=\"lqd-trigger-bar lqd-trigger-bar-open transition-transform origin-right invisible\"><\/span>\n\t\t\t\t<span class=\"lqd-trigger-icon inline-flex items-center justify-center w-full h-full absolute top-0 start-0 transition-all\">\n\t\t\t\t\t<svg class=\"lqd-akar-icon w-1em h-auto lqd-menu-icon \" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"36\" height=\"36\" viewbox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 21l-4.486-4.494M19 10.5a8.5 8.5 0 1 1-17 0 8.5 8.5 0 0 1 17 0z\"><\/path><\/svg>\t\t\t\t<\/span>\n\t\t\t<\/span>\n\t\t<\/span>\n\t\t\t\t<\/button>\n\n\t\t\n\t\t<div class=\"lqd-dropdown lqd-search-dropdown lqd-togglable-element hidden w-full flex-col overflow-hidden backface-hidden [&amp;.lqd-is-active:flex] absolute end-0\">\n\t\t\t<div class=\"lqd-search-container flex flex-col justify-center h-full mx-auto backface-hidden\">\n\t\t\t\t<form class=\"lqd-search-form w-full relative leading-none\" role=\"search\" method=\"get\" action=\"https:\/\/vanchile.com\/\">\n\t\t\t\t\t<input class=\"lqd-search-input w-full transition-colors\" type=\"search\" placeholder=\"Start searching\" value=\"\" name=\"s\" \/>\n\t\t\t\t\t<span class=\"lqd-input-icon inline-block absolute top-1\/2 lqd-transform -translate-y-1\/2 start-0\">\n\t\t\t\t\t\t\t\t\t\t\t\t<svg class=\"w-1em h-1em\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"ai ai-Search\"><path d=\"M21 21l-4.486-4.494M19 10.5a8.5 8.5 0 1 1-17 0 8.5 8.5 0 0 1 17 0z\"\/><\/svg>\n\t\t\t\t\t\t\t\t\t\t\t<\/span>\n\t\t\t\t\t<input type=\"hidden\" name=\"post_type\" value=\"all\" \/>\n\t\t\t\t<\/form>\n\t\t\t<\/div>\n\t\t<\/div>\n\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<script>\n\t\twindow.liquid.behaviors.push( {\n\t\t\tdataId: \"96c65c7\",\n\t\t\tbehaviors:[\n\t\t\t\t{behaviorName:'liquidToggle',options:{changePropPrefix:'lqdSearchToggle-96c65c7',toggleAllTriggers:true,ignoreEnterOnFocus:true,toggleOffOnEscPress:true,toggleOffOnOutsideClick:true,triggerElements:['click @togglableTriggers']}},{behaviorName:'liquidEffectsSlideToggle',options:{changePropPrefix:'lqdSearchToggle-96c65c7'}},\n\t\t\t]\n\t\t} );\n\t\t\n\t\t<\/script>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Freight Distance &amp; Transit Calculator Search the Excel port database by port, UN\/LOCODE, country or region, then calculate the estimated route. Origin \/ Pickup Location Loading Excel port database&#8230; \u21c5 Destination \/ Delivery Location Use a result from the Excel database for exact port coordinates. Mode of Transport \ud83d\ude9b Road Freight \ud83d\ude86 Rail Freight \u2708\ufe0f [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":{"_acf_changed":false,"_xspeed_no_cache":false,"_xspeed_expiry_hours":0,"footnotes":""},"class_list":["post-2275","page","type-page","status-publish","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/vanchile.com\/wp-json\/wp\/v2\/pages\/2275","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vanchile.com\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/vanchile.com\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/vanchile.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vanchile.com\/wp-json\/wp\/v2\/comments?post=2275"}],"version-history":[{"count":0,"href":"https:\/\/vanchile.com\/wp-json\/wp\/v2\/pages\/2275\/revisions"}],"wp:attachment":[{"href":"https:\/\/vanchile.com\/wp-json\/wp\/v2\/media?parent=2275"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}