Help
RSS
API
Feed
Maltego
Contact
Domain > africourier.com
×
More information on this domain is in
AlienVault OTX
Is this malicious?
Yes
No
DNS Resolutions
Date
IP Address
2024-09-25
212.1.212.150
(
ClassC
)
2026-02-05
74.208.236.41
(
ClassC
)
Port 80
HTTP/1.1 200 OKContent-Type: text/html; charsetUTF-8Transfer-Encoding: chunkedConnection: keep-aliveX-WS-RateLimit-Limit: 1000X-WS-RateLimit-Remaining: 999Date: Thu, 05 Feb 2026 00:05:07 GMTServer: Apache !DOCTYPE html>html langen>head> meta charsetUTF-8> meta nameviewport contentwidthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalableno> title>Home - AfriCourier/title> link relstylesheet href/styles.css> link relicon href/favicon.ico> link relstylesheet hrefhttps://cdn.jsdelivr.net/gh/lipis/flag-icons@7.2.3/css/flag-icons.min.css />/head>body> style> * { -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent; } header { margin-bottom: 2em; } .account { position: absolute; display: flex; align-items: center; justify-content: center; right: 12px; top: 0; height: 60px; z-index: 1; gap: 0px; } .account select, .account button { width: auto; max-width: 200px; min-width: 0; height: 36px; box-sizing: border-box; margin: 0px; display: flex; align-items: center; text-align: center; } .account button { margin-left: 8px; background: #7D382E; color: #fff; border: 1px solid #7D382E; border-radius: 6px; padding-left: 12px; padding-right: 12px; cursor: pointer; } .account button:hover { background-color: #652D25; border-color: #652D25; } /* Global Header Button */ #header-country-btn { display: flex; align-items: center; background: #f0f0f0; /* Light gray */ border: 1px solid #ddd; border-radius: 8px; padding: 4px 12px; cursor: pointer; transition: all 0.2s; height: 40px; margin-right: 12px; } #header-country-btn:hover { border-color: #7D382E; background: #e0e0e0; } .h-flag { font-size: 1.5rem; margin-right: 8px; } .h-info { display: flex; flex-direction: column; align-items: flex-start; margin-right: 12px; line-height: 1.1; } .h-country { font-weight: 600; font-size: 0.85rem; color: #333; } .h-meta { font-size: 0.7rem; color: #777; } .h-arrow { font-size: 0.7rem; color: #999; } /* Main Nav Styles */ #main-nav { position: relative; display: flex; align-items: center; justify-content: center; width: 100vw; height: 60px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); background: #fff; } .logo { display: none; } .menu-toggle { position: absolute; top: 50%; transform: translateY(-50%); left: 16px; display: block; cursor: pointer; color: #96332C; width: 30px; height: 30px; font-size: 30px; line-height: 1; padding: 0; margin: 0; display: flex; justify-content: center; align-items: center; z-index: 10001; } .menu-toggle svg { fill: #96332C; } .off-canvas { position: fixed; top: 0; left: -100%; width: 80dvw; max-width: 300px; height: 100%; background: #ffffffd4; box-shadow: 2px 0 5px rgba(0, 0, 0, 0.2); transition: left 0.3s ease; padding: 80px 2rem 2rem 2rem; z-index: 20000; display: flex; flex-direction: column; justify-content: flex-start; overflow-y: auto; backdrop-filter: blur(10px); } .off-canvas.active { left: 0; } .off-canvas a { color: inherit; text-decoration: none; margin-bottom: 2rem; display: block; } .nav-links { display: none; justify-content: center; align-items: center; } .nav-links a { color: inherit; font-size: 0.9rem; text-decoration: none; padding: 0.5rem 0.8rem; display: block; background: linear-gradient(to bottom, transparent 95%, #96332C 96%, #96332C 100%); background-position: -400px 0; background-repeat: no-repeat; transition: background-position 0.2s; user-select: none; } .nav-links a:hover { background-position: 0 0; } #countryDropdown, #currencyDropdown { border: 1px solid #ccc; border-right: none; padding: 5px 10px; font-size: 14px; appearance: none; background-color: white; height: 36px; } #countryDropdown { border-top-left-radius: 6px; border-bottom-left-radius: 6px; } #currencyDropdown { border-top-right-radius: 6px; border-bottom-right-radius: 6px; border-right: 1px solid #ccc; } @media (max-width: 1099px) { header { margin-bottom: 0; } .account .flag-toggle { display: block; background-color: #fff; border: 1px solid #ccc; border-radius: 50%; color: #000; cursor: pointer; width: 36px; height: 36px; padding: 0; margin-right: 8px; } #countryCurrencyContainer { display: none; flex-direction: column; position: absolute; top: 60px; right: 12px; background: white; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); padding: 0.5rem; z-index: 2; min-width: 200px; max-width: 90vw; gap: 0.5rem; align-items: stretch; } #countryCurrencyContainer.active { display: flex; } #countryCurrencyContainer select { display: block; width: 100%; padding: 8px 12px; border: 1px solid #ccc; border-radius: 4px; appearance: none; background-color: #fff; } #countryCurrencyContainer button { display: block; width: 100%; background-color: #fff; border: 1px solid #ccc; border-radius: 4px; color: #000; padding: 8px 0; cursor: pointer; margin: 0; max-width: none; } #countryCurrencyContainer button#applySelection { background-color: #28a745; border-color: #28a745; color: #fff; } #countryCurrencyContainer select, #countryCurrencyContainer button { box-sizing: border-box; } #header-country-btn { margin-right: 8px; padding: 4px 8px; } .h-info, .h-arrow { display: none; } .h-flag { margin-right: 0; font-size: 1.8rem; } } @media (min-width: 1100px) { .account .flag-toggle { display: none; } .logo { display: block; } .menu-toggle { display: none; } #main-nav { justify-content: start; } .nav-links { display: flex; justify-content: start; position: relative; margin-left: 190px; gap: 1.25rem; } .nav-links a { color: inherit; } .nav-links .login { position: absolute; right: 0; } .off-canvas { display: none; } #countryCurrencyContainer { display: none !important; } }/style>header> div classaccount> button idheader-country-btn onclickopenCountrySelector()> !-- Populated by JS --> span classh-flag>🌐/span> /button> button idaccountButton onclicklocation.href/account/> svg xmlnshttp://www.w3.org/2000/svg viewBox0 0 24 24 width24 height24 fillwhite stylemargin-right: 8px;> path dM12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z /> /svg> Account /button> /div> nav idmain-nav> div classmenu-toggle onclicktoggleMenu()> svg xmlnshttp://www.w3.org/2000/svg viewBox0 0 24 24> path dM12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M6,7H18V9H6V7M6,11H18V13H6V11M6,15H18V17H6V15Z /> /svg> /div> div classlogo onclicklocation.href/;> img src/africourier-logo.svg altAfriCourier Logo> /div> div classoff-canvas idoffCanvasMenu>/div> div classnav-links> a href/ship-and-track/>Ship & Track/a> a styledisplay: none; href/shopping/>Shopping/a> a href/about-us/>About us/a> a href/support/>Support/a> /div> /nav>/header>script> function toggleMenu() { const menu document.getElementById(offCanvasMenu); const navLinks document.querySelector(.nav-links); menu.innerHTML a href\/\>Home/a> + navLinks.innerHTML; menu.classList.toggle(active); loadCountriesAndCurrencies(); } document.addEventListener(click, function (event) { const menu document.getElementById(offCanvasMenu); const toggle document.querySelector(.menu-toggle); if (!menu.contains(event.target) && !toggle.contains(event.target)) { menu.classList.remove(active); } }); function loadCountriesAndCurrencies() { // This function is kept empty or minimal if needed by other parts, // but the new overlay handles its own logic. // We might need to ensure the button is updated if this is called. if (typeof updateHeaderDisplay function) { updateHeaderDisplay(); } } document.addEventListener(DOMContentLoaded, function () { // loadCountriesAndCurrencies(); // Handled by country-selector.php }); // Handle swipe left to close the menu let touchStartX 0; let touchEndX 0; document.addEventListener(touchstart, function (event) { touchStartX event.changedTouches0.screenX; }, false); document.addEventListener(touchend, function (event) { touchEndX event.changedTouches0.screenX; handleSwipeGesture(); }, false); function handleSwipeGesture() { const menu document.getElementById(offCanvasMenu); if (touchStartX > touchEndX + 50) { // Swipe left detected (50px threshold) menu.classList.remove(active); } }/script>script> // Removed toggleCountryCurrency as it is no longer used/script>!-- Country & Language Selector Overlay -->div idcountry-selector-overlay classcs-overlay styledisplay: none;> div classcs-modal> div classcs-header> h2>Select your country & language/h2> button classcs-close-btn onclickcloseCountrySelector()>×/button> /div> div classcs-content> p classcs-subtitle>Please select your country and preferred language to continue./p> div classcs-featured-options> !-- Zambia --> div classcs-option-card onclickselectCountry(Zambia, English, ZMW, zm)> div classcs-flag>span classfi fi-zm>/span>/div> div classcs-details> h3>Zambia/h3> span classcs-lang>English/span> /div> /div> !-- Mozambique --> div classcs-option-card-group> div classcs-flag-large>span classfi fi-mz>/span>/div> div classcs-details-group> h3>Mozambique/h3> div classcs-lang-buttons> button onclickselectCountry(Mozambique, English, MZN, mz)>English/button> button onclickselectCountry(Mozambique, Portuguese, MZN, mz)>Português/button> /div> /div> /div> /div> div classcs-divider> span>or/span> /div> button classcs-other-btn onclicktoggleOtherCountries()>Select other country/button> div idcs-country-search classcs-search styledisplay: none;> input idcs-country-search-input typetext placeholderSearch country or currency... oninputhandleCountrySearch(this.value) aria-labelSearch country /> /div> div idcs-other-countries-grid classcs-regions styledisplay: none;> !-- Populated by JS --> p>Loading countries.../p> /div> /div> /div>/div>style> .cs-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.7); backdrop-filter: blur(5px); z-index: 9999; display: flex; justify-content: center; align-items: center; opacity: 0; pointer-events: none; transition: opacity 0.3s ease; } .cs-overlay.active { opacity: 1; pointer-events: all; } .cs-modal { background: white; width: 90%; max-width: 600px; max-height: 90vh; border-radius: 16px; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); display: flex; flex-direction: column; overflow: hidden; transform: translateY(20px); transition: transform 0.3s ease; } .cs-overlay.active .cs-modal { transform: translateY(0); } .cs-header { padding: 1.5rem; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center; } .cs-header h2 { margin: 0; font-size: 1.25rem; color: #333; } .cs-close-btn { background: none; border: none; font-size: 2rem; line-height: 1; cursor: pointer; color: #999; padding: 0; } .cs-content { padding: 2rem; overflow-y: auto; } .cs-subtitle { text-align: center; color: #666; margin-bottom: 2rem; } .cs-featured-options { display: flex; flex-direction: column; gap: 1rem; margin-bottom: 2rem; } .cs-option-card { display: flex; align-items: center; padding: 1rem; border: 2px solid #eee; border-radius: 12px; cursor: pointer; transition: all 0.2s ease; } .cs-option-card:hover { border-color: #dc281d; background: #fff5f5; } .cs-flag { font-size: 2.5rem; margin-right: 1rem; } .cs-details h3 { margin: 0; font-size: 1.1rem; color: #333; } .cs-lang { color: #666; font-size: 0.9rem; } /* Mozambique Special Group */ .cs-option-card-group { display: flex; align-items: center; padding: 1rem; border: 2px solid #eee; border-radius: 12px; } .cs-flag-large { font-size: 2.5rem; margin-right: 1rem; } .cs-details-group { flex: 1; } .cs-details-group h3 { margin: 0 0 0.5rem 0; font-size: 1.1rem; color: #333; } .cs-lang-buttons { display: flex; gap: 0.5rem; } .cs-lang-buttons button { padding: 0.4rem 0.8rem; border: 1px solid #ddd; border-radius: 6px; background: white; cursor: pointer; font-size: 0.9rem; transition: all 0.2s; } .cs-lang-buttons button:hover { border-color: #dc281d; color: #dc281d; background: #fff5f5; } .cs-divider { display: flex; align-items: center; text-align: center; margin: 1rem 0; color: #999; } .cs-divider::before, .cs-divider::after { content: ; flex: 1; border-bottom: 1px solid #eee; } .cs-divider span { padding: 0 10px; font-size: 0.9rem; } .cs-other-btn { width: 100%; padding: 1rem; background: #f8f9fa; border: 1px solid #ddd; border-radius: 8px; color: #555; font-weight: 600; cursor: pointer; transition: all 0.2s; } .cs-other-btn:hover { background: #eee; color: #333; } .cs-regions { display: flex; flex-direction: column; gap: 1.25rem; margin-top: 1.5rem; padding-top: 1.5rem; border-top: 1px solid #eee; } .cs-region { display: flex; flex-direction: column; gap: 0.75rem; } .cs-region-title { margin: 0.5rem 0 0; font-size: 0.95rem; color: #444; font-weight: 700; } .cs-region-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 0.75rem; } .cs-grid-item { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 1rem; border: 1px solid #eee; border-radius: 8px; cursor: pointer; transition: all 0.2s; } .cs-grid-item:hover { border-color: #dc281d; background: #fff5f5; } .cs-grid-flag { font-size: 2rem; margin-bottom: 0.5rem; } .cs-grid-name { font-size: 0.9rem; color: #333; line-height: 1.2; } .cs-grid-currency { margin-top: 0.3rem; font-size: 0.8rem; color: #777; } .cs-search { margin-top: 1rem; } .cs-search input { width: 100%; padding: 0.75rem 0.9rem; border: 1px solid #ddd; border-radius: 8px; font-size: 0.95rem; outline: none; transition: border-color 0.2s, box-shadow 0.2s; } .cs-search input:focus { border-color: #dc281d; box-shadow: 0 0 0 3px rgba(220, 40, 29, 0.08); }/style>script> let worldCountryGroups ; function openCountrySelector() { const overlay document.getElementById(country-selector-overlay); overlay.style.display flex; // Force reflow overlay.offsetHeight; overlay.classList.add(active); } function closeCountrySelector() { const overlay document.getElementById(country-selector-overlay); overlay.classList.remove(active); setTimeout(() > { overlay.style.display none; }, 300); } function selectCountry(country, language, currency, flag) { localStorage.setItem(selected_country, country); localStorage.setItem(selected_language, language); localStorage.setItem(selected_currency, currency); localStorage.setItem(selected_flag, flag); // Set cookies for PHP document.cookie site_country + encodeURIComponent(country) + ; path/; max-age31536000; document.cookie site_lang + encodeURIComponent(language) + ; path/; max-age31536000; document.cookie site_currency + encodeURIComponent(currency) + ; path/; max-age31536000; updateHeaderDisplay(); closeCountrySelector(); // Reload page to apply language changes location.reload(); } function toggleOtherCountries() { const grid document.getElementById(cs-other-countries-grid); const btn document.querySelector(.cs-other-btn); const search document.getElementById(cs-country-search); if (grid.style.display none) { grid.style.display block; search.style.display block; btn.textContent Hide other countries; loadOtherCountries(); } else { grid.style.display none; search.style.display none; btn.textContent Select other country; } } function loadOtherCountries() { const grid document.getElementById(cs-other-countries-grid); if (grid.dataset.loaded) return; fetch(/json/world-countries.json?v + new Date().getTime()) .then(res > res.json()) .then(groups > { worldCountryGroups groups || ; grid.dataset.loaded true; renderOtherCountries(document.getElementById(cs-country-search-input)?.value || ); }) .catch(err > { grid.innerHTML p>Failed to load countries./p>; console.error(err); }); } function renderOtherCountries(filterTerm ) { const grid document.getElementById(cs-other-countries-grid); if (!worldCountryGroups.length) return; const term filterTerm.trim().toLowerCase(); grid.innerHTML ; worldCountryGroups.forEach(group > { const filteredCountries (group.countries || ).filter(c > { if (c.name Zambia || c.name Mozambique) return false; if (!term) return true; const currencyCode (c.currencyCode || ).toLowerCase(); const currencyName (c.currencyName || ).toLowerCase(); return (c.name || ).toLowerCase().includes(term) || currencyCode.includes(term) || currencyName.includes(term); }); if (!filteredCountries.length) return; const section document.createElement(div); section.className cs-region; const heading document.createElement(h4); heading.className cs-region-title; heading.textContent group.region || Other; section.appendChild(heading); const regionGrid document.createElement(div); regionGrid.className cs-region-grid; filteredCountries.forEach(c > { const item document.createElement(div); item.className cs-grid-item; const currencyCode c.currencyCode || USD; const currencyName c.currencyName || currencyCode; const iso c.iso ? c.iso.toLowerCase() : xx; item.onclick () > selectCountry(c.name, English, currencyCode, iso); const currencyLabel currencyName && currencyName ! currencyCode ? `${currencyCode} · ${currencyName}` : currencyCode; item.innerHTML ` div classcs-grid-flag>span classfi fi-${iso}>/span>/div> div classcs-grid-name>${c.name}/div> div classcs-grid-currency>${currencyLabel}/div> `; regionGrid.appendChild(item); }); section.appendChild(regionGrid); grid.appendChild(section); }); if (!grid.children.length) { grid.innerHTML p>No countries match your search./p>; } } function handleCountrySearch(value) { renderOtherCountries(value); } function updateHeaderDisplay() { const country localStorage.getItem(selected_country) || Zambia; const language localStorage.getItem(selected_language) || English; const currency localStorage.getItem(selected_currency) || ZMW; // Default to zm if not set, or handle legacy emoji if present (simple check: length > 2) let flag localStorage.getItem(selected_flag); if (!flag || flag.length > 2) { flag (country Zambia ? zm : (country Mozambique ? mz : xx)); } // Find the button in header and update it const btn document.getElementById(header-country-btn); if (btn) { btn.innerHTML ` span classh-flag>span classfi fi-${flag}>/span>/span> div classh-info> span classh-country>${country}/span> span classh-meta>${language} / ${currency}/span> /div> span classh-arrow>▼/span> `; } } // Initialize document.addEventListener(DOMContentLoaded, () > { if (!localStorage.getItem(selected_country)) { // First time visit, show overlay setTimeout(openCountrySelector, 1000); } updateHeaderDisplay(); });/script>script> document.addEventListener(DOMContentLoaded, function () { const ua navigator.userAgent.toLowerCase(); if (ua.includes(firefox)) { const notice document.createElement(div); notice.style.cssText position: fixed; bottom: 0; left: 0; right: 0; background: #ffefc5; color: #222; padding: 0.8rem; text-align: center; font-size: 0.9rem; z-index: 9999; border-top: 1px solid #e0b800;; notice.innerHTML ⚠️ This site has not been fully tested in Firefox. For best performance, we recommend using a Chromium-based browser such as strong>Chrome/strong>, strong>Edge/strong>, strong>Brave/strong>, or strong>Opera/strong>.; document.body.appendChild(notice); } });/script> style> html, body, * { touch-action: pan-y pan-x; } #coming-soon-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.85); color: #fff; display: flex; align-items: center; justify-content: center; z-index: 10000; display: none; } #coming-soon-overlay .overlay-content { text-align: center; padding: 2rem; } #coming-soon-overlay img#overlay-logo { width: 200px; height: auto; margin-bottom: 1rem; cursor: pointer; } #coming-soon-overlay h1 { font-size: 2rem; margin: 0.5rem 0 1rem; } #coming-soon-overlay #password-container { display: none; margin-top: 1rem; } #coming-soon-overlay inputtypepassword { padding: 0.6rem 0.8rem; border-radius: 6px; border: 1px solid #ccc; margin-right: 0.5rem; outline: none; } #coming-soon-overlay button#overlay-unlock { padding: 0.6rem 1rem; border-radius: 6px; border: none; background: #96332C; color: #fff; font-weight: 700; cursor: pointer; } #coming-soon-overlay .error { color: #ffb3b3; margin-top: 0.5rem; font-size: 0.9rem; } /style> div idcoming-soon-overlay> div classoverlay-content> img srcafricourier-logo.svg altAfriCourier Logo idoverlay-logo> h1>Coming Soon/h1> p>We are preparing something great./p> div idpassword-container> input typepassword idoverlay-password placeholderEnter password> button idoverlay-unlock>Unlock/button> div idoverlay-error classerror styledisplay:none>Incorrect password/div> /div> /div> /div> script> (function() { const OVERLAY_PASSWORD africourier; const overlay document.getElementById(coming-soon-overlay); const logo document.getElementById(overlay-logo); const passContainer document.getElementById(password-container); const passInput document.getElementById(overlay-password); const unlockBtn document.getElementById(overlay-unlock); const errorEl document.getElementById(overlay-error); function hideOverlay() { overlay.style.display none; } function showPassword() { passContainer.style.display block; errorEl.style.display none; setTimeout(() > passInput.focus(), 0); } function tryUnlock() { const input (passInput.value || ).trim(); if (input OVERLAY_PASSWORD) { localStorage.setItem(ac_overlay_unlocked, 1); hideOverlay(); } else { errorEl.style.display block; } } // Persist unlock state if (localStorage.getItem(ac_overlay_unlocked) 1) { hideOverlay(); } // Show password prompt on logo click logo.addEventListener(click, showPassword); unlockBtn.addEventListener(click, tryUnlock); passInput.addEventListener(keydown, (e) > { if (e.key Enter) tryUnlock(); }); })(); /script> style> .map-container { border-radius: 1rem; z-index: 1; } .search-results { display: none; /* Google Maps Autocomplete handles this */ } .search-results { display: none; /* Google Maps Autocomplete handles this */ } #price-display { margin-top: 0.5rem; padding: 10px; background: #f9f9f9; border-radius: 8px; font-weight: bold; color: #96332C; text-align: center; border: 1px solid #eee; } #price-label { color: #333; margin-right: 5px; } /style> main> style> .hero-grid { display: grid; grid-template-columns: 300px 1fr; gap: 1.5rem; align-items: start; } .service-buttons { display: flex; flex-direction: column; gap: 1rem; width: 100%; /* max-width: 260px; Removed to fill grid column */ } .service-btn { display: flex; align-items: center; justify-content: flex-start; flex-direction: row; gap: .55rem; width: 100%; /* Changed from 190px */ height: auto; /* Override fixed height from styles.css */ padding: 0 0.75rem 0 10px; border: 2px solid #eee; border-radius: 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); cursor: pointer; transition: all .2s ease; } @media (min-width: 901px) { .service-btn:hover { transform: translateY(-2px); box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1); background: #f6f6f7; } } .service-btn.selected { border-color: #2F2F2F; box-shadow: 0 0 0 3px rgba(47, 47, 47, 0.2); background: #f6f6f7; } .service-btn img { height: 80px; object-fit: cover; } .service-text { display: flex; flex-direction: column; text-align: left; gap: 6px; } .service-title { font-weight: 700; font-size: 1.05rem; color: #333; line-height: 1.2; } .service-subtitle { font-size: .95rem; color: #666; } .map-and-inputs { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: 1rem; align-items: start; } .map-container { height: 360px; margin-top: 0; } .map-container #map { width: 100%; height: 100%; min-height: 320px; border-radius: 1rem; } .route-inputs { background: #fff; border: 1px solid #eee; border-radius: 12px; padding: 1rem; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); display: flex; flex-direction: column; gap: .5rem; position: relative; overflow: visible; align-items: stretch; /* Override styles.css */ } .continue-btn { width: 100%; max-width: 200px; height: 44px; border-radius: 10px; margin-top: .25rem; margin-left: auto; margin-right: auto; } .route-input { width: 100%; /* Added to fill container */ height: 48px; padding: 10px 12px; padding-left: 60px !important; border: 1px solid #ddd; border-radius: 10px; box-sizing: border-box; /* Ensure padding doesnt overflow width */ } #from-input { background: url(/images/map-marker-radius-outline.svg) no-repeat 16px center; background-size: 22px 22px; } #to-input { background: url(/images/flag-checkered.svg) no-repeat 16px center; background-size: 22px 22px; } .route-input-group { position: relative; width: 100%; /* Ensure it fills the flex container */ } .route-input-group .input-icon { position: absolute !important; left: 18px; top: 50%; transform: translateY(-50%); width: 22px; height: 22px; pointer-events: none; display: block; } .locate-btn { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); border: 1px solid #ddd; background: #fff; color: #96332C; border-radius: 8px; padding: 6px 10px; cursor: pointer; } .locate-btn:hover { background: #f9f9f9; } .category-card { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .6rem; } .categories-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 1rem; } .category-meta { margin: 0; color: #777; font-size: 0.9rem; font-weight: 600; } .type-icon-wrap { width: 72px; height: 72px; display: flex; align-items: center; justify-content: center; overflow: hidden; } .type-icon-wrap.placeholder { background: #f5f5f5; border-color: #e6e6e6; color: #888; font-weight: 700; font-size: 1.1rem; } .shopping-groups { display: flex; flex-direction: column; gap: 1.25rem; } .shopping-group { border-radius: 16px; padding: 1.25rem; border: 1px solid transparent; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); } .shopping-group.local-group { background: linear-gradient(135deg, #ffeae4, #ffe2da); border-color: #ffd9b8; } .shopping-group.international-group { background: linear-gradient(135deg, #f3f7ff, #e8f1ff); border-color: #cfdcff; } .shopping-group-header { display: flex; align-items: center; justify-content: space-between; font-weight: 700; color: #333; margin-bottom: 0.75rem; } .scope-pill { display: inline-flex; align-items: center; padding: 0.25rem 0.6rem; background: #f7e4e2; color: #96332C; border-radius: 999px; font-size: 0.8rem; font-weight: 700; border: 1px solid #f1c7c3; } .type-icon { width: 100%; height: 100%; object-fit: contain; } @media (max-width: 900px) { .hero-grid { grid-template-columns: 1fr; } .map-and-inputs { grid-template-columns: 1fr; } .map-container { height: auto; } .route-inputs { margin-top: .5rem; } } @media (max-width: 640px) { .service-buttons { flex-direction: row; flex-wrap: wrap; justify-content: center; max-width: none; } .service-btn { min-width: 150px; gap: .4rem; } .service-btn img { height: 72px; } .service-title { font-size: .95rem; } .service-subtitle { font-size: .85rem; } .route-inputs { padding: .85rem; gap: .6rem; } .route-input { height: 44px; padding-left: 52px !important; } .continue-btn { height: 42px; } .map-container { height: 300px; } } /* Past Addresses Styles */ #past-addresses { width: 100%; margin-top: 0.25rem; } .pa-header { font-size: 0.75rem; color: #999; margin-bottom: 0.4rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; } .pa-item { display: flex; align-items: center; gap: 0.6rem; padding: 0.6rem 0.8rem; background: #f8f9fa; border: 1px solid #eee; border-radius: 8px; cursor: pointer; margin-bottom: 0.4rem; font-size: 0.9rem; color: #444; transition: all 0.2s ease; } .pa-item:hover { background: #fff; border-color: #ddd; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); } .pa-icon { color: #96332C; opacity: 0.7; } .pa-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1; } /style> !-- Top Navigation Tabs --> div classhero-tabs> button classhero-tab active onclickswitchTab(local, event)>Local/button> button classhero-tab onclicknavigateTo(/send-shipment/?typedomestic)>City to City/button> button styledisplay: none; classhero-tab onclicknavigateTo(/send-shipment/?typeinternational)>International/button> button styledisplay: none; classhero-tab onclicknavigateTo(/shop-and-ship.php)>Shop Shipping/button> /div> style> @media (max-width: 900px) { .hero-tabs { margin: 0 !important; padding: 0.5rem 1rem !important; /* Keep side padding */ padding-bottom: 0.5rem !important; /* Added small padding bottom */ background: white; position: relative; z-index: 5; border-bottom: 1px solid #eee; /* subtle separation */ } .hero-tab { padding: 0.4rem 0.8rem !important; font-size: 0.85rem !important; } } /* GLOBAL: Hide mobile-specific overlay elements by default */ .mobile-recent-overlay, .recent-btn-container { display: none; } /style> h1 styledisplay: none; classhomepage-headline>Courier Services/h1> !-- Hero Section --> section idafricourier-hero classzoom-fade-up new-hero-layout> !-- Local Tab Content --> div idtab-local classtab-content> style> /* Mobile Overlay & Wizard Styles */ .mobile-wizard-nav { display: none; margin-top: 1rem; } .wizard-step { display: block; /* Default for desktop compatibility logic if needed, but we use wrappers now */ } @media (max-width: 900px) { #africourier-hero { padding: 0; height: 80vh; /* Increased to allow map to extend below overlay */ min-height: 500px; position: relative; overflow: hidden; } #tab-local .hero-grid { display: block; height: 100%; position: static; } /* Map becomes background */ .map-and-inputs { display: block; position: static; } .map-container { position: absolute; top: 0; left: 0; width: 100%; height: 100% !important; z-index: 0; border-radius: 0; } .map-container #map { border-radius: 0; } /* On Mobile, we treat .service-buttons (Step 1) and .route-inputs (Step 2) as overlay cards. */ .service-buttons-wrapper, .route-inputs-wrapper { position: absolute; bottom: 0; left: 0; width: 100%; z-index: 10; pointer-events: none; /* Pass clicks */ display: flex; flex-direction: column; justify-content: flex-end; padding: 1rem 1rem 3rem 1rem; /* Added bottom padding to lift overlay */ box-sizing: border-box; } .mobile-card-content { background: rgba(255, 255, 255, 0); /* Glass effect */ backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.4); border-radius: 20px; padding: 1.5rem 1rem 1.5rem 1rem; pointer-events: auto; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); width: 100%; max-width: 600px; margin: 0 auto; } /* Wizard Visibility Logic */ .wizard-step-container { display: none; } .wizard-step-container.active { display: block; } /* Ensure elements look good in overlay */ .service-buttons { flex-direction: row; overflow-x: auto; padding-bottom: 0.5rem; padding-top: 0.5rem; /* Add space for shadow */ gap: 0.75rem; width: 100%; } .service-btn { min-width: 140px; flex-shrink: 0; } .mobile-wizard-nav { display: flex; width: 100%; margin-top: 1rem; } .mobile-next-btn { width: 100%; background: #2F2F2F; color: white; border: none; padding: 1rem; border-radius: 12px; font-weight: 700; font-size: 1rem; cursor: pointer; } /* Adjust Route Inputs for overlay */ .route-inputs { box-shadow: none; border: none; padding: 0; margin: 0; background: transparent; /* Remove white bg */ } /* iOS Zoom Prevention: Ensure inputs are at least 16px */ .route-inputs input { font-size: 16px !important; } /* Hide price display on step 1, show on step 2 */ #price-display { margin-top: 1rem; background: rgba(249, 249, 249, 0.5); /* Semi-transparent */ backdrop-filter: blur(5px); } /* Recent Addresses Overlay */ .mobile-recent-overlay { position: fixed; inset: 0; background: white; z-index: 100; padding: 2rem; display: flex; flex-direction: column; display: none; /* hidden by default */ } .recent-overlay-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; } .recent-overlay-close { background: #eee; border: none; padding: 0.5rem 1rem; border-radius: 8px; font-weight: bold; cursor: pointer; } .recent-btn-container { display: block; margin-top: 0.5rem; text-align: right; } .btn-link-recent { background: none; border: none; color: #96332C; text-decoration: underline; cursor: pointer; font-size: 0.9rem; padding: 0; } } /style> div classhero-grid> !-- Step 1 Container (Desktop: Left Col, Mobile: Overlay Step 1) --> div idstep-1-wrapper classwizard-step-container active service-buttons-wrapper> !-- Inner wrapper for mobile card styling --> div classmobile-card-content-wrapper mobile-card-content> h3 stylemargin: 0 0 1rem 0; font-size: 1.1rem; color: #333; display: none; classmobile-only-header>Select Parcel Size/h3> div classservice-buttons> button classservice-btn selected onclickselectSize(small, this)> img src/images/type-envelope.png altSmall Package onerrorthis.src/images/box-icon.png stylepadding:0;margin:0;> div classservice-text> span classservice-title>Small/span> span classservice-subtitle>Fits in backpack/span> /div> /button> button classservice-btn onclickselectSize(medium, this)> img src/images/type-medium-box.png altMedium Package onerrorthis.src/images/box-icon.png stylepadding:0;margin:0;> div classservice-text> span classservice-title>Medium/span> span classservice-subtitle>Fits in bike box/span> /div> /button> button classservice-btn onclickselectSize(large, this)> img src/images/type-large-box.png altLarge Package onerrorthis.src/images/box-icon.png stylepadding:0;margin:0;> div classservice-text> span classservice-title>Large/span> span classservice-subtitle>Fits in car/span> /div> /button> /div> div classmobile-wizard-nav> button classmobile-next-btn onclickgoToStep(2)>Next/button> /div> /div> /div> !-- Right Col --> div classmap-and-inputs> !-- Step 2 Container (Desktop: Inside Right Col, visible; Mobile: Overlay Step 2) --> div idstep-2-wrapper classwizard-step-container route-inputs-wrapper> div classmobile-card-content-wrapper mobile-card-content> div classroute-inputs> div classroute-input-group> input typetext idfrom-input placeholderFrom classroute-input autocompleteoff> button classlocate-btn onclicklocateUser(from)> img src/images/map-marker-radius-outline.svg stylewidth:18px;height:18px> /button> /div> div classroute-input-group> input typetext idto-input placeholderTo classroute-input autocompleteoff> button classlocate-btn onclicklocateUser(to)> img src/images/map-marker-radius-outline.svg stylewidth:18px;height:18px> /button> /div> div classrecent-btn-container> button typebutton classbtn-link-recent onclicktoggleRecentAddresses()> Recent Destinations /button> /div> !-- Overlay for recent addresses (Mobile) / Inline for Desktop (handled by JS logic) --> div idpast-addresses-overlay classmobile-recent-overlay> div classrecent-overlay-header> h3> Recent Destinations /h3> button classrecent-overlay-close onclicktoggleRecentAddresses()>Close/button> /div> div idpa-list-overlay>/div> /div> div idpast-addresses styledisplay:none; margin-top: 0.5rem;> div classpa-header>Recent Destinations/div> div idpa-list>/div> /div> button classcontinue-btn onclickproceedToBooking()>Continue/button> div idprice-display> span idprice-label>Estimated Fee:/span> span idprice-value>Enter locations to calculate/span> /div> /div> /div> /div> div classmap-container> div idmap>/div> /div> /div> /div> /div> /section> h1 stylemargin-bottom: 0; display: none; classhomepage-headline>Shopping/h1> !-- Shopping Section (New) --> section styledisplay: none; idshopping-section classzoom-fade-up stylepadding: 2rem; max-width: 1200px; margin: 0 auto;> div classshopping-groups> div classshopping-group local-group> div classshopping-group-header> div>Local shops/div> span classscope-pill>Same-day delivery/span> /div> div idshopping-categories-local classcategories-grid> div stylegrid-column: 1/-1; text-align: center;>Loading local categories.../div> /div> /div> div classshopping-group international-group> div classshopping-group-header> div>International shops/div> span classscope-pill>Delivery ~1 week/span> /div> div idshopping-categories-international classcategories-grid> div stylegrid-column: 1/-1; text-align: center;>Loading international categories.../div> /div> /div> /div> /section> !-- Track Shipment Section (Moved) --> section styledisplay: none; idtrack-shipment-section classzoom-fade-up> div idtrack-shipment> h1>Track your shipment/h1> div idtrack-shipment-form> input typetext idtracking-number placeholderType your tracking number here> button idtrack-btn>Track/button> /div> p>Kindly enter the number exactly as it appears on your receipt./p> /div> script> document.getElementById(track-btn).addEventListener(click, function() { var orderId document.getElementById(tracking-number).value; // Remove AFR from the beginning (case-insensitive) orderId orderId.replace(/^AFR/i, ); // Remove any non-numeric characters from the end orderId orderId.replace(/^0-9+$/, ); if (orderId) { localStorage.setItem(orderId, orderId); window.location.href /status/; } else { const dialog document.querySelector(native-dialog); dialog.alert(Please enter a valid tracking number.); } }); /script> /section> button styledisplay: none; onclickdocument.querySelector(native-dialog).showLoading();>Test/button> script> // Prevent pinch to zoom on iOS (Aggressive) document.addEventListener(gesturestart, function(e) { e.preventDefault(); }); document.addEventListener(gesturechange, function(e) { e.preventDefault(); }); document.addEventListener(gestureend, function(e) { e.preventDefault(); }); // Prevent multi-touch gestures (pinch) from zooming the page document.addEventListener(touchmove, function(e) { if (e.touches.length > 1) { e.preventDefault(); } }, { passive: false }); // Prevent double-tap to zoom let lastTouchEnd 0; document.addEventListener(touchend, function(event) { const now (new Date()).getTime(); if (now - lastTouchEnd 300) { event.preventDefault(); } lastTouchEnd now; }, false); let map, directionsService, directionsRenderer; let pricingConfig null; let selectedSize small; let fromPlace null; let toPlace null; let currentCountry zambia; // Default function navigateTo(url) { const dialog document.querySelector(native-dialog); if (dialog) dialog.showLoading(); setTimeout(() > { window.location.href url; }, 100); } function switchTab(tabName, event) { // Update tab buttons document.querySelectorAll(.hero-tab).forEach(tab > { tab.classList.remove(active); }); if (event) event.target.classList.add(active); // Update tab content document.querySelectorAll(.tab-content).forEach(content > { content.style.display none; }); document.getElementById(tab- + tabName).style.display block; } function selectSize(size, btn) { selectedSize size; document.querySelectorAll(.service-btn).forEach(b > b.classList.remove(selected)); btn.classList.add(selected); calculatePrice(); } async function initMap() { // Default to Lusaka, Zambia const lusaka { lat: -15.3875, lng: 28.3228 }; const isMobile window.innerWidth 900; map new google.maps.Map(document.getElementById(map), { zoom: 13, center: lusaka, disableDefaultUI: true, zoomControl: !isMobile, // Disable on mobile zoomControlOptions: { position: google.maps.ControlPosition.TOP_LEFT }, // Move to top left if enabled gestureHandling: greedy // Enable one-finger panning }); directionsService new google.maps.DirectionsService(); directionsRenderer new google.maps.DirectionsRenderer({ map: map, suppressMarkers: false, polylineOptions: { strokeColor: #96332C, strokeWeight: 4 } }); // Load pricing config try { const response await fetch(pricing_config.json?v + new Date().getTime()); pricingConfig await response.json(); // Set current country based on local storage const storedCode localStorage.getItem(selected_flag) || zm; const countryMap { zm: zambia, mz: mozambique }; if (countryMapstoredCode) { currentCountry countryMapstoredCode; } } catch (e) { console.error(Failed to load pricing config, e); } setupAutocomplete(from-input, from); setupAutocomplete(to-input, to); // Try to center on user location if (navigator.geolocation) { navigator.geolocation.getCurrentPosition( (position) > { const pos { lat: position.coords.latitude, lng: position.coords.longitude, }; if (map) { map.setCenter(pos); map.setZoom(15); } }, () > { // User denied or error, do nothing (stay on default) } ); } } function setupAutocomplete(inputId, type) { const input document.getElementById(inputId); // Get selected country from local storage let countryCode localStorage.getItem(selected_flag) || zm; // Handle edge cases or legacy data if (countryCode.length > 2) countryCode zm; const options { fields: formatted_address, geometry, name, strictBounds: false, componentRestrictions: { country: countryCode } }; const autocomplete new google.maps.places.Autocomplete(input, options); // Bias to current map bounds autocomplete.bindTo(bounds, map); autocomplete.addListener(place_changed, () > { const place autocomplete.getPlace(); if (!place.geometry || !place.geometry.location) { window.alert(No details available for input: + place.name + ); return; } if (type from) { fromPlace place; } else { toPlace place; } updateRoute(); }); } function locateUser(target) { if (!navigator.geolocation) return; const dialog document.querySelector(native-dialog); dialog.showLoading(); navigator.geolocation.getCurrentPosition( (position) > { const pos { lat: position.coords.latitude, lng: position.coords.longitude, }; const geocoder new google.maps.Geocoder(); geocoder.geocode({ location: pos }, (results, status) > { dialog.hideLoading(); if (status OK) { if (results0) { const place results0; document.getElementById(target + -input).value place.formatted_address; // Manually construct a place-like object since Geocoder result structure differs slightly from Autocomplete const placeObj { geometry: place.geometry, name: place.formatted_address, formatted_address: place.formatted_address }; if (target from) { fromPlace placeObj; } else { toPlace placeObj; } map.setCenter(pos); map.setZoom(15); updateRoute(); } else { window.alert(No results found); } } else { window.alert(Geocoder failed due to: + status); } }); }, () > { dialog.hideLoading(); window.alert(Geolocation failed.); } ); } function updateRoute() { if (fromPlace && toPlace) { directionsService.route({ origin: fromPlace.geometry.location, destination: toPlace.geometry.location, travelMode: google.maps.TravelMode.DRIVING, }, (response, status) > { if (status OK) { const isMobile window.innerWidth 900; if (isMobile) { // Manually fit bounds with padding directionsRenderer.setOptions({ preserveViewport: true }); directionsRenderer.setDirections(response); const bounds response.routes0.bounds; // Padding: top 50, right 50, bottom 350 (overlay), left 50 map.fitBounds(bounds, { top: 50, right: 50, bottom: 350, left: 50 }); } else { // Default behavior for desktop directionsRenderer.setOptions({ preserveViewport: false }); directionsRenderer.setDirections(response); } calculatePrice(); } else { window.alert(Directions request failed due to + status); } } ); } else if (fromPlace) { map.setCenter(fromPlace.geometry.location); map.setZoom(15); new google.maps.Marker({ position: fromPlace.geometry.location, map: map, }); } else if (toPlace) { map.setCenter(toPlace.geometry.location); map.setZoom(15); new google.maps.Marker({ position: toPlace.geometry.location, map: map, }); } } function calculatePrice() { if (!fromPlace || !toPlace || !pricingConfig) return; // Calculate distance in km const distanceInMeters google.maps.geometry.spherical.computeDistanceBetween( fromPlace.geometry.location, toPlace.geometry.location ); const distance distanceInMeters / 1000; // Pricing Logic const config pricingConfigcurrentCountry; if (!config) return; let price 0; let rate null; if (selectedSize small) { rate config.small; } else if (selectedSize medium) { rate config.medium.bike; } else if (selectedSize large) { rate config.large; } if (rate) { const baseKm parseFloat(rate.base_km) || 0; const chargeableDistance Math.max(0, distance - baseKm); price rate.base + (chargeableDistance * rate.per_km); if (price rate.min) price rate.min; } // Update UI const priceEl document.getElementById(price-display); const valEl document.getElementById(price-value); // Format currency const currency config.currency || ZMW; valEl.textContent `${currency} ${price.toFixed(2)}`; priceEl.style.display block; } function proceedToBooking() { if (!fromPlace || !toPlace) { const dialog document.querySelector(native-dialog); dialog.alert(Please select both pickup and delivery locations.); return; } // Save destination address saveAddress(toPlace); // Store data for next page localStorage.setItem(booking_from, JSON.stringify({ name: fromPlace.name, address: fromPlace.formatted_address, lat: fromPlace.geometry.location.lat(), lng: fromPlace.geometry.location.lng() })); localStorage.setItem(booking_to, JSON.stringify({ name: toPlace.name, address: toPlace.formatted_address, lat: toPlace.geometry.location.lat(), lng: toPlace.geometry.location.lng() })); localStorage.setItem(booking_size, selectedSize); window.location.href /local-booking.php; } // Past Addresses Logic function saveAddress(place) { if (!place || !place.formatted_address) return; let addresses JSON.parse(localStorage.getItem(ac_saved_addresses) || ); // Remove if exists (to bump to top) addresses addresses.filter(a > a.address ! place.formatted_address); // Add new addresses.unshift({ name: place.name || place.formatted_address.split(,)0, address: place.formatted_address, lat: place.geometry.location.lat(), lng: place.geometry.location.lng() }); // Keep max 3 if (addresses.length > 3) addresses.length 3; localStorage.setItem(ac_saved_addresses, JSON.stringify(addresses)); } function loadAddresses() { const addresses JSON.parse(localStorage.getItem(ac_saved_addresses) || ); // Desktop Container const container document.getElementById(past-addresses); const list document.getElementById(pa-list); // Overlay Container (Mobile) const overlayList document.getElementById(pa-list-overlay); const recentBtn document.querySelector(.btn-link-recent); if (addresses.length 0) { if (container) container.style.display none; if (recentBtn) recentBtn.style.display none; return; } if (recentBtn) recentBtn.style.display window.innerWidth 900 ? inline-block : none; // Helper to build list item const createItem (addr) > { const item document.createElement(div); item.className pa-item; item.innerHTML ` span classpa-icon>📍/span> span classpa-text>${addr.name}/span> `; item.onclick () > { const latLng new google.maps.LatLng(addr.lat, addr.lng); toPlace { name: addr.name, formatted_address: addr.address, geometry: { location: latLng } }; document.getElementById(to-input).value addr.address; updateRoute(); // Close overlay if open document.getElementById(past-addresses-overlay).style.display none; }; return item; }; // Populate Desktop List if (list) { list.innerHTML ; container.style.display window.innerWidth > 900 ? block : none; addresses.forEach(addr > list.appendChild(createItem(addr))); } // Populate Mobile Overlay List if (overlayList) { overlayList.innerHTML ; addresses.forEach(addr > overlayList.appendChild(createItem(addr))); } } function toggleRecentAddresses() { const overlay document.getElementById(past-addresses-overlay); if (overlay.style.display flex) { overlay.style.display none; } else { overlay.style.display flex; } } // Re-check visibility on resize window.addEventListener(resize, loadAddresses); // Fetch Categories (Shop Types) document.addEventListener(DOMContentLoaded, async function() { loadAddresses(); const storeTypes {id:stype_69199bed87b3d,name:Pharmacies,slug:pharmacies,description:,status:active,icon:\/uploads\/store-types\/stype_69199bed87b3d_icon.png,created_at:2025-11-16 04:39:57,updated_at:2025-11-28 05:16:45,created_by:c0d783f6-5c68-46a5-8567-6523594a6889,updated_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d,is_international:0},{id:stype_69199bfd3737c,name:Gas,slug:gas,description:,status:active,icon:\/uploads\/store-types\/stype_69199bfd3737c_icon.png,created_at:2025-11-16 04:40:13,updated_at:2025-11-28 05:16:12,created_by:c0d783f6-5c68-46a5-8567-6523594a6889,updated_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d,is_international:0},{id:stype_69199c0c63595,name:Butcheries,slug:butcheries,description:,status:active,icon:\/uploads\/store-types\/stype_69199c0c63595_icon.png,created_at:2025-11-16 04:40:28,updated_at:2025-11-28 05:19:11,created_by:c0d783f6-5c68-46a5-8567-6523594a6889,updated_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d,is_international:0},{id:stype_69199c25026e8,name:Drinks,slug:drinks,description:,status:active,icon:\/uploads\/store-types\/stype_69199c25026e8_icon.png,created_at:2025-11-16 04:40:53,updated_at:2025-11-28 05:20:54,created_by:c0d783f6-5c68-46a5-8567-6523594a6889,updated_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d,is_international:0},{id:stype_69199c380168a,name:Fresh Produce,slug:fresh-produce,description:,status:active,icon:\/uploads\/store-types\/stype_69199c380168a_icon.png,created_at:2025-11-16 04:41:12,updated_at:2025-11-28 05:16:02,created_by:c0d783f6-5c68-46a5-8567-6523594a6889,updated_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d,is_international:0},{id:stype_691ac8f0e21c8,name:Groceries,slug:groceries,description:,status:active,icon:\/uploads\/store-types\/stype_691ac8f0e21c8_icon.png,created_at:2025-11-17 02:04:16,updated_at:2025-11-27 15:19:08,created_by:2423adb2-dc19-4e8b-8bbb-b6010d5d471f,updated_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d,is_international:0},{id:stype_692ae8f1b4c1c,name:Fashion,slug:fashion,description:,status:active,is_international:1,icon:\/uploads\/store-types\/stype_692ae8f1b4c1c_icon.png,created_at:2025-11-29 07:37:05,updated_at:2025-11-29 09:04:29,created_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d,updated_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d},{id:stype_692ae903a4560,name:Art & Handmade,slug:art-handmade,description:,status:active,is_international:1,icon:\/uploads\/store-types\/stype_692ae903a4560_icon.png,created_at:2025-11-29 07:37:23,updated_at:2025-11-29 09:04:16,created_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d,updated_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d},{id:stype_692ae90da3697,name:Food,slug:food,description:,status:active,is_international:1,icon:\/uploads\/store-types\/stype_692ae90da3697_icon.png,created_at:2025-11-29 07:37:33,updated_at:2025-11-29 09:04:04,created_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d,updated_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d}; const localGrid document.getElementById(shopping-categories-local); const internationalGrid document.getElementById(shopping-categories-international); const localTypes storeTypes.filter(t > !t.is_international); const internationalTypes storeTypes.filter(t > t.is_international); const renderTypes (grid, list, emptyMessage, scopeLabel) > { if (!grid) return; grid.innerHTML ; if (list.length 0) { grid.innerHTML `p stylegrid-column: 1/-1; text-align:center; color:#666;>${emptyMessage}/p>`; return; } list.forEach(type > { const fallbackInitial (type.name || ?).charAt(0).toUpperCase(); const iconMarkup type.icon ? `div classtype-icon-wrap>img src${type.icon} alt${type.name} icon classtype-icon>/div>` : `div classtype-icon-wrap placeholder>span>${fallbackInitial}/span>/div>`; const card document.createElement(div); card.className category-card; card.innerHTML `${iconMarkup}h3>${type.name}/h3>`; card.onclick () > window.location.href /shopping/?type + type.slug; grid.appendChild(card); }); }; renderTypes(localGrid, localTypes, No local shop types found., Same-day delivery); renderTypes(internationalGrid, internationalTypes, No international shop types yet., Delivery ~1 week); // Mobile Wizard Init & Resize Handler function handleResponsiveLayout() { const isMobile window.innerWidth 900; if (isMobile) { // Ensure we are on a valid step, default to 1 if none active if (!document.querySelector(.wizard-step-container.active)) { goToStep(1); } } else { // On desktop, ensure everything is visible document.querySelectorAll(.wizard-step-container).forEach(el > { el.style.display ; // Clear inline styles if any interactions set them el.classList.add(active); }); } } window.addEventListener(resize, handleResponsiveLayout); handleResponsiveLayout(); // Init }); function goToStep(step) { // Only act if we are in mobile mode (or if we want wizard behavior on desktop too, but currently not) if (window.innerWidth > 900) return; document.querySelectorAll(.wizard-step-container).forEach(el > el.classList.remove(active)); const target document.getElementById(step- + step + -wrapper); if (target) { target.classList.add(active); // Specific logic if (step 2) { // resize map if needed or ensure its visible if (map) google.maps.event.trigger(map, resize); } } } /script> /main> div classwave-container> svg classwave flipped viewBox0 0 1440 320 preserveAspectRationone> path fill#992C23 fill-opacity1 dM0,96L60,112C120,128,240,160,360,165.3C480,171,600,149, 720,144C840,139,960,149,1080,165.3C1200,181,1320,203, 1380,213.3L1440,224L1440,320L1380,320C1320,320,1200,320, 1080,320C960,320,840,320,720,320C600,320,480,320, 360,320C240,320,120,320,60,320L0,320Z>/path> /svg> svg classwave overlay viewBox0 0 1440 320 preserveAspectRationone> path fill#992C23 fill-opacity1 dM0,64L60,90.7C120,117,240,171,360,165.3C480,160,600,96, 720,101.3C840,107,960,181,1080,186.7C1200,192,1320,128, 1380,106.7L1440,85.3L1440,320L1380,320C1320,320,1200,320, 1080,320C960,320,840,320,720,320C600,320,480,320, 360,320C240,320,120,320,60,320L0,320Z>/path> /svg>/div>footer> div classfooter-container> div classfooter-links> a href/privacy-policy/>Privacy Policy/a> a href/terms-of-service/>Terms of Service/a> a href/support/>Help Center/a> a href/careers/>Careers/a> /div> div classfooter-social> a hrefhttps://x.com/courierafri>span classicon-wrapper>img src/x.png altTwitter>/span>/a> a hrefhttps://www.facebook.com/profile.php?id61576980615029>span classicon-wrapper>img src/facebook.svg altFacebook>/span>/a> a hrefhttps://www.instagram.com/africourier_africa/>span classicon-wrapper>img src/instagram.svg altInstagram>/span>/a> a hrefhttps://www.linkedin.com/company/africourier/posts/?feedViewall>span classicon-wrapper>img src/linkedin.svg altLinkedIn>/span>/a> /div> p idcopyright-notice>© 2025 - 2026 AfriCourier Logistics Ltd./p> /div>/footer>!--Start of Tawk.to Script-->script typetext/javascript> var Tawk_API Tawk_API || {}; Tawk_API.visitor { name: localStorage.getItem(user_name) || Guest, email: localStorage.getItem(user_email) || guest@guest.com }; Tawk_LoadStart new Date(); (function () { var s1 document.createElement(script), s0 document.getElementsByTagName(script)0; s1.async true; s1.src https://embed.tawk.to/68420af074a763190ce37b86/1it0vlelb; s1.charset UTF-8; s1.setAttribute(crossorigin, *); s0.parentNode.insertBefore(s1, s0); })();/script>!--End of Tawk.to Script-->style> .wave-container { position: relative; line-height: 0; margin-bottom: -20px; } .wave { display: block; transform: scaleX(-1); width: 100vw; height: 60px; } .wave.overlay { position: absolute; top: 0px; left: 0px; opacity: 0.5; margin-top: -8px; pointer-events: none; } footer { background-color: #992C23; color: #fff; padding: 2rem 1rem; text-align: center; } .footer-container { max-width: 1200px; margin: 0 auto; } .footer-links, .footer-social { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.8rem; margin-bottom: 1rem; } .footer-links a { color: #ffffff; text-decoration: none; font-weight: 500; transition: color 0.3s ease; font-size: 0.8rem; } .footer-links a:hover { color: #D47A41; /* Light orange hover */ } .footer-social a .icon-wrapper { display: inline-block; border: solid 2px white; border-radius: 50%; width: 36px; height: 36px; overflow: hidden; position: relative; transition: transform 0.3s ease; } .footer-social a:hover .icon-wrapper { transform: scale(1.1); } .footer-social a .icon-wrapper::after { content: ; position: absolute; top: 0; left: -100%; width: 200%; height: 100%; background: linear-gradient(120deg, rgba(255, 255, 255, 0.0) 60%, rgba(255, 255, 255, 0.5) 80%, rgba(255, 255, 255, 0.0) 100%); transform: skewX(-20deg) translateX(0); pointer-events: none; animation: none; } .footer-social a:hover .icon-wrapper::after { transform: skewX(-20deg) translateX(100%); animation: shine-pre 1.5s cubic-bezier(0.4, 0.2, 0.2, 1); } .footer-social a .icon-wrapper img { width: 100%; height: 100%; display: block; border-radius: 50%; filter: grayscale(100%); } footer p { margin-top: 1rem; font-size: 0.8rem; } @keyframes shine-pre { 0% { transform: skewX(-20deg) translateX(0); } 60% { transform: skewX(-20deg) translateX(100%); } 100% { transform: skewX(-20deg) translateX(0); } }/style> native-dialog>/native-dialog> script srcnative-dialog.js>/script> script srchttps://maps.googleapis.com/maps/api/js?keyAIzaSyDyQZeDTlJsT8m3uUDQe7aJswn_60mEP-4&librariesplaces,geometry&callbackinitMap async defer>/script>/body>/html>
Port 443
HTTP/1.1 200 OKContent-Type: text/html; charsetUTF-8Transfer-Encoding: chunkedConnection: keep-aliveX-WS-RateLimit-Limit: 1000X-WS-RateLimit-Remaining: 998Date: Thu, 05 Feb 2026 00:05:07 GMTServer: Apache !DOCTYPE html>html langen>head> meta charsetUTF-8> meta nameviewport contentwidthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalableno> title>Home - AfriCourier/title> link relstylesheet href/styles.css> link relicon href/favicon.ico> link relstylesheet hrefhttps://cdn.jsdelivr.net/gh/lipis/flag-icons@7.2.3/css/flag-icons.min.css />/head>body> style> * { -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent; } header { margin-bottom: 2em; } .account { position: absolute; display: flex; align-items: center; justify-content: center; right: 12px; top: 0; height: 60px; z-index: 1; gap: 0px; } .account select, .account button { width: auto; max-width: 200px; min-width: 0; height: 36px; box-sizing: border-box; margin: 0px; display: flex; align-items: center; text-align: center; } .account button { margin-left: 8px; background: #7D382E; color: #fff; border: 1px solid #7D382E; border-radius: 6px; padding-left: 12px; padding-right: 12px; cursor: pointer; } .account button:hover { background-color: #652D25; border-color: #652D25; } /* Global Header Button */ #header-country-btn { display: flex; align-items: center; background: #f0f0f0; /* Light gray */ border: 1px solid #ddd; border-radius: 8px; padding: 4px 12px; cursor: pointer; transition: all 0.2s; height: 40px; margin-right: 12px; } #header-country-btn:hover { border-color: #7D382E; background: #e0e0e0; } .h-flag { font-size: 1.5rem; margin-right: 8px; } .h-info { display: flex; flex-direction: column; align-items: flex-start; margin-right: 12px; line-height: 1.1; } .h-country { font-weight: 600; font-size: 0.85rem; color: #333; } .h-meta { font-size: 0.7rem; color: #777; } .h-arrow { font-size: 0.7rem; color: #999; } /* Main Nav Styles */ #main-nav { position: relative; display: flex; align-items: center; justify-content: center; width: 100vw; height: 60px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); background: #fff; } .logo { display: none; } .menu-toggle { position: absolute; top: 50%; transform: translateY(-50%); left: 16px; display: block; cursor: pointer; color: #96332C; width: 30px; height: 30px; font-size: 30px; line-height: 1; padding: 0; margin: 0; display: flex; justify-content: center; align-items: center; z-index: 10001; } .menu-toggle svg { fill: #96332C; } .off-canvas { position: fixed; top: 0; left: -100%; width: 80dvw; max-width: 300px; height: 100%; background: #ffffffd4; box-shadow: 2px 0 5px rgba(0, 0, 0, 0.2); transition: left 0.3s ease; padding: 80px 2rem 2rem 2rem; z-index: 20000; display: flex; flex-direction: column; justify-content: flex-start; overflow-y: auto; backdrop-filter: blur(10px); } .off-canvas.active { left: 0; } .off-canvas a { color: inherit; text-decoration: none; margin-bottom: 2rem; display: block; } .nav-links { display: none; justify-content: center; align-items: center; } .nav-links a { color: inherit; font-size: 0.9rem; text-decoration: none; padding: 0.5rem 0.8rem; display: block; background: linear-gradient(to bottom, transparent 95%, #96332C 96%, #96332C 100%); background-position: -400px 0; background-repeat: no-repeat; transition: background-position 0.2s; user-select: none; } .nav-links a:hover { background-position: 0 0; } #countryDropdown, #currencyDropdown { border: 1px solid #ccc; border-right: none; padding: 5px 10px; font-size: 14px; appearance: none; background-color: white; height: 36px; } #countryDropdown { border-top-left-radius: 6px; border-bottom-left-radius: 6px; } #currencyDropdown { border-top-right-radius: 6px; border-bottom-right-radius: 6px; border-right: 1px solid #ccc; } @media (max-width: 1099px) { header { margin-bottom: 0; } .account .flag-toggle { display: block; background-color: #fff; border: 1px solid #ccc; border-radius: 50%; color: #000; cursor: pointer; width: 36px; height: 36px; padding: 0; margin-right: 8px; } #countryCurrencyContainer { display: none; flex-direction: column; position: absolute; top: 60px; right: 12px; background: white; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); padding: 0.5rem; z-index: 2; min-width: 200px; max-width: 90vw; gap: 0.5rem; align-items: stretch; } #countryCurrencyContainer.active { display: flex; } #countryCurrencyContainer select { display: block; width: 100%; padding: 8px 12px; border: 1px solid #ccc; border-radius: 4px; appearance: none; background-color: #fff; } #countryCurrencyContainer button { display: block; width: 100%; background-color: #fff; border: 1px solid #ccc; border-radius: 4px; color: #000; padding: 8px 0; cursor: pointer; margin: 0; max-width: none; } #countryCurrencyContainer button#applySelection { background-color: #28a745; border-color: #28a745; color: #fff; } #countryCurrencyContainer select, #countryCurrencyContainer button { box-sizing: border-box; } #header-country-btn { margin-right: 8px; padding: 4px 8px; } .h-info, .h-arrow { display: none; } .h-flag { margin-right: 0; font-size: 1.8rem; } } @media (min-width: 1100px) { .account .flag-toggle { display: none; } .logo { display: block; } .menu-toggle { display: none; } #main-nav { justify-content: start; } .nav-links { display: flex; justify-content: start; position: relative; margin-left: 190px; gap: 1.25rem; } .nav-links a { color: inherit; } .nav-links .login { position: absolute; right: 0; } .off-canvas { display: none; } #countryCurrencyContainer { display: none !important; } }/style>header> div classaccount> button idheader-country-btn onclickopenCountrySelector()> !-- Populated by JS --> span classh-flag>🌐/span> /button> button idaccountButton onclicklocation.href/account/> svg xmlnshttp://www.w3.org/2000/svg viewBox0 0 24 24 width24 height24 fillwhite stylemargin-right: 8px;> path dM12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z /> /svg> Account /button> /div> nav idmain-nav> div classmenu-toggle onclicktoggleMenu()> svg xmlnshttp://www.w3.org/2000/svg viewBox0 0 24 24> path dM12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M6,7H18V9H6V7M6,11H18V13H6V11M6,15H18V17H6V15Z /> /svg> /div> div classlogo onclicklocation.href/;> img src/africourier-logo.svg altAfriCourier Logo> /div> div classoff-canvas idoffCanvasMenu>/div> div classnav-links> a href/ship-and-track/>Ship & Track/a> a styledisplay: none; href/shopping/>Shopping/a> a href/about-us/>About us/a> a href/support/>Support/a> /div> /nav>/header>script> function toggleMenu() { const menu document.getElementById(offCanvasMenu); const navLinks document.querySelector(.nav-links); menu.innerHTML a href\/\>Home/a> + navLinks.innerHTML; menu.classList.toggle(active); loadCountriesAndCurrencies(); } document.addEventListener(click, function (event) { const menu document.getElementById(offCanvasMenu); const toggle document.querySelector(.menu-toggle); if (!menu.contains(event.target) && !toggle.contains(event.target)) { menu.classList.remove(active); } }); function loadCountriesAndCurrencies() { // This function is kept empty or minimal if needed by other parts, // but the new overlay handles its own logic. // We might need to ensure the button is updated if this is called. if (typeof updateHeaderDisplay function) { updateHeaderDisplay(); } } document.addEventListener(DOMContentLoaded, function () { // loadCountriesAndCurrencies(); // Handled by country-selector.php }); // Handle swipe left to close the menu let touchStartX 0; let touchEndX 0; document.addEventListener(touchstart, function (event) { touchStartX event.changedTouches0.screenX; }, false); document.addEventListener(touchend, function (event) { touchEndX event.changedTouches0.screenX; handleSwipeGesture(); }, false); function handleSwipeGesture() { const menu document.getElementById(offCanvasMenu); if (touchStartX > touchEndX + 50) { // Swipe left detected (50px threshold) menu.classList.remove(active); } }/script>script> // Removed toggleCountryCurrency as it is no longer used/script>!-- Country & Language Selector Overlay -->div idcountry-selector-overlay classcs-overlay styledisplay: none;> div classcs-modal> div classcs-header> h2>Select your country & language/h2> button classcs-close-btn onclickcloseCountrySelector()>×/button> /div> div classcs-content> p classcs-subtitle>Please select your country and preferred language to continue./p> div classcs-featured-options> !-- Zambia --> div classcs-option-card onclickselectCountry(Zambia, English, ZMW, zm)> div classcs-flag>span classfi fi-zm>/span>/div> div classcs-details> h3>Zambia/h3> span classcs-lang>English/span> /div> /div> !-- Mozambique --> div classcs-option-card-group> div classcs-flag-large>span classfi fi-mz>/span>/div> div classcs-details-group> h3>Mozambique/h3> div classcs-lang-buttons> button onclickselectCountry(Mozambique, English, MZN, mz)>English/button> button onclickselectCountry(Mozambique, Portuguese, MZN, mz)>Português/button> /div> /div> /div> /div> div classcs-divider> span>or/span> /div> button classcs-other-btn onclicktoggleOtherCountries()>Select other country/button> div idcs-country-search classcs-search styledisplay: none;> input idcs-country-search-input typetext placeholderSearch country or currency... oninputhandleCountrySearch(this.value) aria-labelSearch country /> /div> div idcs-other-countries-grid classcs-regions styledisplay: none;> !-- Populated by JS --> p>Loading countries.../p> /div> /div> /div>/div>style> .cs-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.7); backdrop-filter: blur(5px); z-index: 9999; display: flex; justify-content: center; align-items: center; opacity: 0; pointer-events: none; transition: opacity 0.3s ease; } .cs-overlay.active { opacity: 1; pointer-events: all; } .cs-modal { background: white; width: 90%; max-width: 600px; max-height: 90vh; border-radius: 16px; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); display: flex; flex-direction: column; overflow: hidden; transform: translateY(20px); transition: transform 0.3s ease; } .cs-overlay.active .cs-modal { transform: translateY(0); } .cs-header { padding: 1.5rem; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center; } .cs-header h2 { margin: 0; font-size: 1.25rem; color: #333; } .cs-close-btn { background: none; border: none; font-size: 2rem; line-height: 1; cursor: pointer; color: #999; padding: 0; } .cs-content { padding: 2rem; overflow-y: auto; } .cs-subtitle { text-align: center; color: #666; margin-bottom: 2rem; } .cs-featured-options { display: flex; flex-direction: column; gap: 1rem; margin-bottom: 2rem; } .cs-option-card { display: flex; align-items: center; padding: 1rem; border: 2px solid #eee; border-radius: 12px; cursor: pointer; transition: all 0.2s ease; } .cs-option-card:hover { border-color: #dc281d; background: #fff5f5; } .cs-flag { font-size: 2.5rem; margin-right: 1rem; } .cs-details h3 { margin: 0; font-size: 1.1rem; color: #333; } .cs-lang { color: #666; font-size: 0.9rem; } /* Mozambique Special Group */ .cs-option-card-group { display: flex; align-items: center; padding: 1rem; border: 2px solid #eee; border-radius: 12px; } .cs-flag-large { font-size: 2.5rem; margin-right: 1rem; } .cs-details-group { flex: 1; } .cs-details-group h3 { margin: 0 0 0.5rem 0; font-size: 1.1rem; color: #333; } .cs-lang-buttons { display: flex; gap: 0.5rem; } .cs-lang-buttons button { padding: 0.4rem 0.8rem; border: 1px solid #ddd; border-radius: 6px; background: white; cursor: pointer; font-size: 0.9rem; transition: all 0.2s; } .cs-lang-buttons button:hover { border-color: #dc281d; color: #dc281d; background: #fff5f5; } .cs-divider { display: flex; align-items: center; text-align: center; margin: 1rem 0; color: #999; } .cs-divider::before, .cs-divider::after { content: ; flex: 1; border-bottom: 1px solid #eee; } .cs-divider span { padding: 0 10px; font-size: 0.9rem; } .cs-other-btn { width: 100%; padding: 1rem; background: #f8f9fa; border: 1px solid #ddd; border-radius: 8px; color: #555; font-weight: 600; cursor: pointer; transition: all 0.2s; } .cs-other-btn:hover { background: #eee; color: #333; } .cs-regions { display: flex; flex-direction: column; gap: 1.25rem; margin-top: 1.5rem; padding-top: 1.5rem; border-top: 1px solid #eee; } .cs-region { display: flex; flex-direction: column; gap: 0.75rem; } .cs-region-title { margin: 0.5rem 0 0; font-size: 0.95rem; color: #444; font-weight: 700; } .cs-region-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 0.75rem; } .cs-grid-item { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 1rem; border: 1px solid #eee; border-radius: 8px; cursor: pointer; transition: all 0.2s; } .cs-grid-item:hover { border-color: #dc281d; background: #fff5f5; } .cs-grid-flag { font-size: 2rem; margin-bottom: 0.5rem; } .cs-grid-name { font-size: 0.9rem; color: #333; line-height: 1.2; } .cs-grid-currency { margin-top: 0.3rem; font-size: 0.8rem; color: #777; } .cs-search { margin-top: 1rem; } .cs-search input { width: 100%; padding: 0.75rem 0.9rem; border: 1px solid #ddd; border-radius: 8px; font-size: 0.95rem; outline: none; transition: border-color 0.2s, box-shadow 0.2s; } .cs-search input:focus { border-color: #dc281d; box-shadow: 0 0 0 3px rgba(220, 40, 29, 0.08); }/style>script> let worldCountryGroups ; function openCountrySelector() { const overlay document.getElementById(country-selector-overlay); overlay.style.display flex; // Force reflow overlay.offsetHeight; overlay.classList.add(active); } function closeCountrySelector() { const overlay document.getElementById(country-selector-overlay); overlay.classList.remove(active); setTimeout(() > { overlay.style.display none; }, 300); } function selectCountry(country, language, currency, flag) { localStorage.setItem(selected_country, country); localStorage.setItem(selected_language, language); localStorage.setItem(selected_currency, currency); localStorage.setItem(selected_flag, flag); // Set cookies for PHP document.cookie site_country + encodeURIComponent(country) + ; path/; max-age31536000; document.cookie site_lang + encodeURIComponent(language) + ; path/; max-age31536000; document.cookie site_currency + encodeURIComponent(currency) + ; path/; max-age31536000; updateHeaderDisplay(); closeCountrySelector(); // Reload page to apply language changes location.reload(); } function toggleOtherCountries() { const grid document.getElementById(cs-other-countries-grid); const btn document.querySelector(.cs-other-btn); const search document.getElementById(cs-country-search); if (grid.style.display none) { grid.style.display block; search.style.display block; btn.textContent Hide other countries; loadOtherCountries(); } else { grid.style.display none; search.style.display none; btn.textContent Select other country; } } function loadOtherCountries() { const grid document.getElementById(cs-other-countries-grid); if (grid.dataset.loaded) return; fetch(/json/world-countries.json?v + new Date().getTime()) .then(res > res.json()) .then(groups > { worldCountryGroups groups || ; grid.dataset.loaded true; renderOtherCountries(document.getElementById(cs-country-search-input)?.value || ); }) .catch(err > { grid.innerHTML p>Failed to load countries./p>; console.error(err); }); } function renderOtherCountries(filterTerm ) { const grid document.getElementById(cs-other-countries-grid); if (!worldCountryGroups.length) return; const term filterTerm.trim().toLowerCase(); grid.innerHTML ; worldCountryGroups.forEach(group > { const filteredCountries (group.countries || ).filter(c > { if (c.name Zambia || c.name Mozambique) return false; if (!term) return true; const currencyCode (c.currencyCode || ).toLowerCase(); const currencyName (c.currencyName || ).toLowerCase(); return (c.name || ).toLowerCase().includes(term) || currencyCode.includes(term) || currencyName.includes(term); }); if (!filteredCountries.length) return; const section document.createElement(div); section.className cs-region; const heading document.createElement(h4); heading.className cs-region-title; heading.textContent group.region || Other; section.appendChild(heading); const regionGrid document.createElement(div); regionGrid.className cs-region-grid; filteredCountries.forEach(c > { const item document.createElement(div); item.className cs-grid-item; const currencyCode c.currencyCode || USD; const currencyName c.currencyName || currencyCode; const iso c.iso ? c.iso.toLowerCase() : xx; item.onclick () > selectCountry(c.name, English, currencyCode, iso); const currencyLabel currencyName && currencyName ! currencyCode ? `${currencyCode} · ${currencyName}` : currencyCode; item.innerHTML ` div classcs-grid-flag>span classfi fi-${iso}>/span>/div> div classcs-grid-name>${c.name}/div> div classcs-grid-currency>${currencyLabel}/div> `; regionGrid.appendChild(item); }); section.appendChild(regionGrid); grid.appendChild(section); }); if (!grid.children.length) { grid.innerHTML p>No countries match your search./p>; } } function handleCountrySearch(value) { renderOtherCountries(value); } function updateHeaderDisplay() { const country localStorage.getItem(selected_country) || Zambia; const language localStorage.getItem(selected_language) || English; const currency localStorage.getItem(selected_currency) || ZMW; // Default to zm if not set, or handle legacy emoji if present (simple check: length > 2) let flag localStorage.getItem(selected_flag); if (!flag || flag.length > 2) { flag (country Zambia ? zm : (country Mozambique ? mz : xx)); } // Find the button in header and update it const btn document.getElementById(header-country-btn); if (btn) { btn.innerHTML ` span classh-flag>span classfi fi-${flag}>/span>/span> div classh-info> span classh-country>${country}/span> span classh-meta>${language} / ${currency}/span> /div> span classh-arrow>▼/span> `; } } // Initialize document.addEventListener(DOMContentLoaded, () > { if (!localStorage.getItem(selected_country)) { // First time visit, show overlay setTimeout(openCountrySelector, 1000); } updateHeaderDisplay(); });/script>script> document.addEventListener(DOMContentLoaded, function () { const ua navigator.userAgent.toLowerCase(); if (ua.includes(firefox)) { const notice document.createElement(div); notice.style.cssText position: fixed; bottom: 0; left: 0; right: 0; background: #ffefc5; color: #222; padding: 0.8rem; text-align: center; font-size: 0.9rem; z-index: 9999; border-top: 1px solid #e0b800;; notice.innerHTML ⚠️ This site has not been fully tested in Firefox. For best performance, we recommend using a Chromium-based browser such as strong>Chrome/strong>, strong>Edge/strong>, strong>Brave/strong>, or strong>Opera/strong>.; document.body.appendChild(notice); } });/script> style> html, body, * { touch-action: pan-y pan-x; } #coming-soon-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.85); color: #fff; display: flex; align-items: center; justify-content: center; z-index: 10000; display: none; } #coming-soon-overlay .overlay-content { text-align: center; padding: 2rem; } #coming-soon-overlay img#overlay-logo { width: 200px; height: auto; margin-bottom: 1rem; cursor: pointer; } #coming-soon-overlay h1 { font-size: 2rem; margin: 0.5rem 0 1rem; } #coming-soon-overlay #password-container { display: none; margin-top: 1rem; } #coming-soon-overlay inputtypepassword { padding: 0.6rem 0.8rem; border-radius: 6px; border: 1px solid #ccc; margin-right: 0.5rem; outline: none; } #coming-soon-overlay button#overlay-unlock { padding: 0.6rem 1rem; border-radius: 6px; border: none; background: #96332C; color: #fff; font-weight: 700; cursor: pointer; } #coming-soon-overlay .error { color: #ffb3b3; margin-top: 0.5rem; font-size: 0.9rem; } /style> div idcoming-soon-overlay> div classoverlay-content> img srcafricourier-logo.svg altAfriCourier Logo idoverlay-logo> h1>Coming Soon/h1> p>We are preparing something great./p> div idpassword-container> input typepassword idoverlay-password placeholderEnter password> button idoverlay-unlock>Unlock/button> div idoverlay-error classerror styledisplay:none>Incorrect password/div> /div> /div> /div> script> (function() { const OVERLAY_PASSWORD africourier; const overlay document.getElementById(coming-soon-overlay); const logo document.getElementById(overlay-logo); const passContainer document.getElementById(password-container); const passInput document.getElementById(overlay-password); const unlockBtn document.getElementById(overlay-unlock); const errorEl document.getElementById(overlay-error); function hideOverlay() { overlay.style.display none; } function showPassword() { passContainer.style.display block; errorEl.style.display none; setTimeout(() > passInput.focus(), 0); } function tryUnlock() { const input (passInput.value || ).trim(); if (input OVERLAY_PASSWORD) { localStorage.setItem(ac_overlay_unlocked, 1); hideOverlay(); } else { errorEl.style.display block; } } // Persist unlock state if (localStorage.getItem(ac_overlay_unlocked) 1) { hideOverlay(); } // Show password prompt on logo click logo.addEventListener(click, showPassword); unlockBtn.addEventListener(click, tryUnlock); passInput.addEventListener(keydown, (e) > { if (e.key Enter) tryUnlock(); }); })(); /script> style> .map-container { border-radius: 1rem; z-index: 1; } .search-results { display: none; /* Google Maps Autocomplete handles this */ } .search-results { display: none; /* Google Maps Autocomplete handles this */ } #price-display { margin-top: 0.5rem; padding: 10px; background: #f9f9f9; border-radius: 8px; font-weight: bold; color: #96332C; text-align: center; border: 1px solid #eee; } #price-label { color: #333; margin-right: 5px; } /style> main> style> .hero-grid { display: grid; grid-template-columns: 300px 1fr; gap: 1.5rem; align-items: start; } .service-buttons { display: flex; flex-direction: column; gap: 1rem; width: 100%; /* max-width: 260px; Removed to fill grid column */ } .service-btn { display: flex; align-items: center; justify-content: flex-start; flex-direction: row; gap: .55rem; width: 100%; /* Changed from 190px */ height: auto; /* Override fixed height from styles.css */ padding: 0 0.75rem 0 10px; border: 2px solid #eee; border-radius: 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); cursor: pointer; transition: all .2s ease; } @media (min-width: 901px) { .service-btn:hover { transform: translateY(-2px); box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1); background: #f6f6f7; } } .service-btn.selected { border-color: #2F2F2F; box-shadow: 0 0 0 3px rgba(47, 47, 47, 0.2); background: #f6f6f7; } .service-btn img { height: 80px; object-fit: cover; } .service-text { display: flex; flex-direction: column; text-align: left; gap: 6px; } .service-title { font-weight: 700; font-size: 1.05rem; color: #333; line-height: 1.2; } .service-subtitle { font-size: .95rem; color: #666; } .map-and-inputs { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: 1rem; align-items: start; } .map-container { height: 360px; margin-top: 0; } .map-container #map { width: 100%; height: 100%; min-height: 320px; border-radius: 1rem; } .route-inputs { background: #fff; border: 1px solid #eee; border-radius: 12px; padding: 1rem; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); display: flex; flex-direction: column; gap: .5rem; position: relative; overflow: visible; align-items: stretch; /* Override styles.css */ } .continue-btn { width: 100%; max-width: 200px; height: 44px; border-radius: 10px; margin-top: .25rem; margin-left: auto; margin-right: auto; } .route-input { width: 100%; /* Added to fill container */ height: 48px; padding: 10px 12px; padding-left: 60px !important; border: 1px solid #ddd; border-radius: 10px; box-sizing: border-box; /* Ensure padding doesnt overflow width */ } #from-input { background: url(/images/map-marker-radius-outline.svg) no-repeat 16px center; background-size: 22px 22px; } #to-input { background: url(/images/flag-checkered.svg) no-repeat 16px center; background-size: 22px 22px; } .route-input-group { position: relative; width: 100%; /* Ensure it fills the flex container */ } .route-input-group .input-icon { position: absolute !important; left: 18px; top: 50%; transform: translateY(-50%); width: 22px; height: 22px; pointer-events: none; display: block; } .locate-btn { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); border: 1px solid #ddd; background: #fff; color: #96332C; border-radius: 8px; padding: 6px 10px; cursor: pointer; } .locate-btn:hover { background: #f9f9f9; } .category-card { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .6rem; } .categories-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 1rem; } .category-meta { margin: 0; color: #777; font-size: 0.9rem; font-weight: 600; } .type-icon-wrap { width: 72px; height: 72px; display: flex; align-items: center; justify-content: center; overflow: hidden; } .type-icon-wrap.placeholder { background: #f5f5f5; border-color: #e6e6e6; color: #888; font-weight: 700; font-size: 1.1rem; } .shopping-groups { display: flex; flex-direction: column; gap: 1.25rem; } .shopping-group { border-radius: 16px; padding: 1.25rem; border: 1px solid transparent; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); } .shopping-group.local-group { background: linear-gradient(135deg, #ffeae4, #ffe2da); border-color: #ffd9b8; } .shopping-group.international-group { background: linear-gradient(135deg, #f3f7ff, #e8f1ff); border-color: #cfdcff; } .shopping-group-header { display: flex; align-items: center; justify-content: space-between; font-weight: 700; color: #333; margin-bottom: 0.75rem; } .scope-pill { display: inline-flex; align-items: center; padding: 0.25rem 0.6rem; background: #f7e4e2; color: #96332C; border-radius: 999px; font-size: 0.8rem; font-weight: 700; border: 1px solid #f1c7c3; } .type-icon { width: 100%; height: 100%; object-fit: contain; } @media (max-width: 900px) { .hero-grid { grid-template-columns: 1fr; } .map-and-inputs { grid-template-columns: 1fr; } .map-container { height: auto; } .route-inputs { margin-top: .5rem; } } @media (max-width: 640px) { .service-buttons { flex-direction: row; flex-wrap: wrap; justify-content: center; max-width: none; } .service-btn { min-width: 150px; gap: .4rem; } .service-btn img { height: 72px; } .service-title { font-size: .95rem; } .service-subtitle { font-size: .85rem; } .route-inputs { padding: .85rem; gap: .6rem; } .route-input { height: 44px; padding-left: 52px !important; } .continue-btn { height: 42px; } .map-container { height: 300px; } } /* Past Addresses Styles */ #past-addresses { width: 100%; margin-top: 0.25rem; } .pa-header { font-size: 0.75rem; color: #999; margin-bottom: 0.4rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; } .pa-item { display: flex; align-items: center; gap: 0.6rem; padding: 0.6rem 0.8rem; background: #f8f9fa; border: 1px solid #eee; border-radius: 8px; cursor: pointer; margin-bottom: 0.4rem; font-size: 0.9rem; color: #444; transition: all 0.2s ease; } .pa-item:hover { background: #fff; border-color: #ddd; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); } .pa-icon { color: #96332C; opacity: 0.7; } .pa-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1; } /style> !-- Top Navigation Tabs --> div classhero-tabs> button classhero-tab active onclickswitchTab(local, event)>Local/button> button classhero-tab onclicknavigateTo(/send-shipment/?typedomestic)>City to City/button> button styledisplay: none; classhero-tab onclicknavigateTo(/send-shipment/?typeinternational)>International/button> button styledisplay: none; classhero-tab onclicknavigateTo(/shop-and-ship.php)>Shop Shipping/button> /div> style> @media (max-width: 900px) { .hero-tabs { margin: 0 !important; padding: 0.5rem 1rem !important; /* Keep side padding */ padding-bottom: 0.5rem !important; /* Added small padding bottom */ background: white; position: relative; z-index: 5; border-bottom: 1px solid #eee; /* subtle separation */ } .hero-tab { padding: 0.4rem 0.8rem !important; font-size: 0.85rem !important; } } /* GLOBAL: Hide mobile-specific overlay elements by default */ .mobile-recent-overlay, .recent-btn-container { display: none; } /style> h1 styledisplay: none; classhomepage-headline>Courier Services/h1> !-- Hero Section --> section idafricourier-hero classzoom-fade-up new-hero-layout> !-- Local Tab Content --> div idtab-local classtab-content> style> /* Mobile Overlay & Wizard Styles */ .mobile-wizard-nav { display: none; margin-top: 1rem; } .wizard-step { display: block; /* Default for desktop compatibility logic if needed, but we use wrappers now */ } @media (max-width: 900px) { #africourier-hero { padding: 0; height: 80vh; /* Increased to allow map to extend below overlay */ min-height: 500px; position: relative; overflow: hidden; } #tab-local .hero-grid { display: block; height: 100%; position: static; } /* Map becomes background */ .map-and-inputs { display: block; position: static; } .map-container { position: absolute; top: 0; left: 0; width: 100%; height: 100% !important; z-index: 0; border-radius: 0; } .map-container #map { border-radius: 0; } /* On Mobile, we treat .service-buttons (Step 1) and .route-inputs (Step 2) as overlay cards. */ .service-buttons-wrapper, .route-inputs-wrapper { position: absolute; bottom: 0; left: 0; width: 100%; z-index: 10; pointer-events: none; /* Pass clicks */ display: flex; flex-direction: column; justify-content: flex-end; padding: 1rem 1rem 3rem 1rem; /* Added bottom padding to lift overlay */ box-sizing: border-box; } .mobile-card-content { background: rgba(255, 255, 255, 0); /* Glass effect */ backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.4); border-radius: 20px; padding: 1.5rem 1rem 1.5rem 1rem; pointer-events: auto; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); width: 100%; max-width: 600px; margin: 0 auto; } /* Wizard Visibility Logic */ .wizard-step-container { display: none; } .wizard-step-container.active { display: block; } /* Ensure elements look good in overlay */ .service-buttons { flex-direction: row; overflow-x: auto; padding-bottom: 0.5rem; padding-top: 0.5rem; /* Add space for shadow */ gap: 0.75rem; width: 100%; } .service-btn { min-width: 140px; flex-shrink: 0; } .mobile-wizard-nav { display: flex; width: 100%; margin-top: 1rem; } .mobile-next-btn { width: 100%; background: #2F2F2F; color: white; border: none; padding: 1rem; border-radius: 12px; font-weight: 700; font-size: 1rem; cursor: pointer; } /* Adjust Route Inputs for overlay */ .route-inputs { box-shadow: none; border: none; padding: 0; margin: 0; background: transparent; /* Remove white bg */ } /* iOS Zoom Prevention: Ensure inputs are at least 16px */ .route-inputs input { font-size: 16px !important; } /* Hide price display on step 1, show on step 2 */ #price-display { margin-top: 1rem; background: rgba(249, 249, 249, 0.5); /* Semi-transparent */ backdrop-filter: blur(5px); } /* Recent Addresses Overlay */ .mobile-recent-overlay { position: fixed; inset: 0; background: white; z-index: 100; padding: 2rem; display: flex; flex-direction: column; display: none; /* hidden by default */ } .recent-overlay-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; } .recent-overlay-close { background: #eee; border: none; padding: 0.5rem 1rem; border-radius: 8px; font-weight: bold; cursor: pointer; } .recent-btn-container { display: block; margin-top: 0.5rem; text-align: right; } .btn-link-recent { background: none; border: none; color: #96332C; text-decoration: underline; cursor: pointer; font-size: 0.9rem; padding: 0; } } /style> div classhero-grid> !-- Step 1 Container (Desktop: Left Col, Mobile: Overlay Step 1) --> div idstep-1-wrapper classwizard-step-container active service-buttons-wrapper> !-- Inner wrapper for mobile card styling --> div classmobile-card-content-wrapper mobile-card-content> h3 stylemargin: 0 0 1rem 0; font-size: 1.1rem; color: #333; display: none; classmobile-only-header>Select Parcel Size/h3> div classservice-buttons> button classservice-btn selected onclickselectSize(small, this)> img src/images/type-envelope.png altSmall Package onerrorthis.src/images/box-icon.png stylepadding:0;margin:0;> div classservice-text> span classservice-title>Small/span> span classservice-subtitle>Fits in backpack/span> /div> /button> button classservice-btn onclickselectSize(medium, this)> img src/images/type-medium-box.png altMedium Package onerrorthis.src/images/box-icon.png stylepadding:0;margin:0;> div classservice-text> span classservice-title>Medium/span> span classservice-subtitle>Fits in bike box/span> /div> /button> button classservice-btn onclickselectSize(large, this)> img src/images/type-large-box.png altLarge Package onerrorthis.src/images/box-icon.png stylepadding:0;margin:0;> div classservice-text> span classservice-title>Large/span> span classservice-subtitle>Fits in car/span> /div> /button> /div> div classmobile-wizard-nav> button classmobile-next-btn onclickgoToStep(2)>Next/button> /div> /div> /div> !-- Right Col --> div classmap-and-inputs> !-- Step 2 Container (Desktop: Inside Right Col, visible; Mobile: Overlay Step 2) --> div idstep-2-wrapper classwizard-step-container route-inputs-wrapper> div classmobile-card-content-wrapper mobile-card-content> div classroute-inputs> div classroute-input-group> input typetext idfrom-input placeholderFrom classroute-input autocompleteoff> button classlocate-btn onclicklocateUser(from)> img src/images/map-marker-radius-outline.svg stylewidth:18px;height:18px> /button> /div> div classroute-input-group> input typetext idto-input placeholderTo classroute-input autocompleteoff> button classlocate-btn onclicklocateUser(to)> img src/images/map-marker-radius-outline.svg stylewidth:18px;height:18px> /button> /div> div classrecent-btn-container> button typebutton classbtn-link-recent onclicktoggleRecentAddresses()> Recent Destinations /button> /div> !-- Overlay for recent addresses (Mobile) / Inline for Desktop (handled by JS logic) --> div idpast-addresses-overlay classmobile-recent-overlay> div classrecent-overlay-header> h3> Recent Destinations /h3> button classrecent-overlay-close onclicktoggleRecentAddresses()>Close/button> /div> div idpa-list-overlay>/div> /div> div idpast-addresses styledisplay:none; margin-top: 0.5rem;> div classpa-header>Recent Destinations/div> div idpa-list>/div> /div> button classcontinue-btn onclickproceedToBooking()>Continue/button> div idprice-display> span idprice-label>Estimated Fee:/span> span idprice-value>Enter locations to calculate/span> /div> /div> /div> /div> div classmap-container> div idmap>/div> /div> /div> /div> /div> /section> h1 stylemargin-bottom: 0; display: none; classhomepage-headline>Shopping/h1> !-- Shopping Section (New) --> section styledisplay: none; idshopping-section classzoom-fade-up stylepadding: 2rem; max-width: 1200px; margin: 0 auto;> div classshopping-groups> div classshopping-group local-group> div classshopping-group-header> div>Local shops/div> span classscope-pill>Same-day delivery/span> /div> div idshopping-categories-local classcategories-grid> div stylegrid-column: 1/-1; text-align: center;>Loading local categories.../div> /div> /div> div classshopping-group international-group> div classshopping-group-header> div>International shops/div> span classscope-pill>Delivery ~1 week/span> /div> div idshopping-categories-international classcategories-grid> div stylegrid-column: 1/-1; text-align: center;>Loading international categories.../div> /div> /div> /div> /section> !-- Track Shipment Section (Moved) --> section styledisplay: none; idtrack-shipment-section classzoom-fade-up> div idtrack-shipment> h1>Track your shipment/h1> div idtrack-shipment-form> input typetext idtracking-number placeholderType your tracking number here> button idtrack-btn>Track/button> /div> p>Kindly enter the number exactly as it appears on your receipt./p> /div> script> document.getElementById(track-btn).addEventListener(click, function() { var orderId document.getElementById(tracking-number).value; // Remove AFR from the beginning (case-insensitive) orderId orderId.replace(/^AFR/i, ); // Remove any non-numeric characters from the end orderId orderId.replace(/^0-9+$/, ); if (orderId) { localStorage.setItem(orderId, orderId); window.location.href /status/; } else { const dialog document.querySelector(native-dialog); dialog.alert(Please enter a valid tracking number.); } }); /script> /section> button styledisplay: none; onclickdocument.querySelector(native-dialog).showLoading();>Test/button> script> // Prevent pinch to zoom on iOS (Aggressive) document.addEventListener(gesturestart, function(e) { e.preventDefault(); }); document.addEventListener(gesturechange, function(e) { e.preventDefault(); }); document.addEventListener(gestureend, function(e) { e.preventDefault(); }); // Prevent multi-touch gestures (pinch) from zooming the page document.addEventListener(touchmove, function(e) { if (e.touches.length > 1) { e.preventDefault(); } }, { passive: false }); // Prevent double-tap to zoom let lastTouchEnd 0; document.addEventListener(touchend, function(event) { const now (new Date()).getTime(); if (now - lastTouchEnd 300) { event.preventDefault(); } lastTouchEnd now; }, false); let map, directionsService, directionsRenderer; let pricingConfig null; let selectedSize small; let fromPlace null; let toPlace null; let currentCountry zambia; // Default function navigateTo(url) { const dialog document.querySelector(native-dialog); if (dialog) dialog.showLoading(); setTimeout(() > { window.location.href url; }, 100); } function switchTab(tabName, event) { // Update tab buttons document.querySelectorAll(.hero-tab).forEach(tab > { tab.classList.remove(active); }); if (event) event.target.classList.add(active); // Update tab content document.querySelectorAll(.tab-content).forEach(content > { content.style.display none; }); document.getElementById(tab- + tabName).style.display block; } function selectSize(size, btn) { selectedSize size; document.querySelectorAll(.service-btn).forEach(b > b.classList.remove(selected)); btn.classList.add(selected); calculatePrice(); } async function initMap() { // Default to Lusaka, Zambia const lusaka { lat: -15.3875, lng: 28.3228 }; const isMobile window.innerWidth 900; map new google.maps.Map(document.getElementById(map), { zoom: 13, center: lusaka, disableDefaultUI: true, zoomControl: !isMobile, // Disable on mobile zoomControlOptions: { position: google.maps.ControlPosition.TOP_LEFT }, // Move to top left if enabled gestureHandling: greedy // Enable one-finger panning }); directionsService new google.maps.DirectionsService(); directionsRenderer new google.maps.DirectionsRenderer({ map: map, suppressMarkers: false, polylineOptions: { strokeColor: #96332C, strokeWeight: 4 } }); // Load pricing config try { const response await fetch(pricing_config.json?v + new Date().getTime()); pricingConfig await response.json(); // Set current country based on local storage const storedCode localStorage.getItem(selected_flag) || zm; const countryMap { zm: zambia, mz: mozambique }; if (countryMapstoredCode) { currentCountry countryMapstoredCode; } } catch (e) { console.error(Failed to load pricing config, e); } setupAutocomplete(from-input, from); setupAutocomplete(to-input, to); // Try to center on user location if (navigator.geolocation) { navigator.geolocation.getCurrentPosition( (position) > { const pos { lat: position.coords.latitude, lng: position.coords.longitude, }; if (map) { map.setCenter(pos); map.setZoom(15); } }, () > { // User denied or error, do nothing (stay on default) } ); } } function setupAutocomplete(inputId, type) { const input document.getElementById(inputId); // Get selected country from local storage let countryCode localStorage.getItem(selected_flag) || zm; // Handle edge cases or legacy data if (countryCode.length > 2) countryCode zm; const options { fields: formatted_address, geometry, name, strictBounds: false, componentRestrictions: { country: countryCode } }; const autocomplete new google.maps.places.Autocomplete(input, options); // Bias to current map bounds autocomplete.bindTo(bounds, map); autocomplete.addListener(place_changed, () > { const place autocomplete.getPlace(); if (!place.geometry || !place.geometry.location) { window.alert(No details available for input: + place.name + ); return; } if (type from) { fromPlace place; } else { toPlace place; } updateRoute(); }); } function locateUser(target) { if (!navigator.geolocation) return; const dialog document.querySelector(native-dialog); dialog.showLoading(); navigator.geolocation.getCurrentPosition( (position) > { const pos { lat: position.coords.latitude, lng: position.coords.longitude, }; const geocoder new google.maps.Geocoder(); geocoder.geocode({ location: pos }, (results, status) > { dialog.hideLoading(); if (status OK) { if (results0) { const place results0; document.getElementById(target + -input).value place.formatted_address; // Manually construct a place-like object since Geocoder result structure differs slightly from Autocomplete const placeObj { geometry: place.geometry, name: place.formatted_address, formatted_address: place.formatted_address }; if (target from) { fromPlace placeObj; } else { toPlace placeObj; } map.setCenter(pos); map.setZoom(15); updateRoute(); } else { window.alert(No results found); } } else { window.alert(Geocoder failed due to: + status); } }); }, () > { dialog.hideLoading(); window.alert(Geolocation failed.); } ); } function updateRoute() { if (fromPlace && toPlace) { directionsService.route({ origin: fromPlace.geometry.location, destination: toPlace.geometry.location, travelMode: google.maps.TravelMode.DRIVING, }, (response, status) > { if (status OK) { const isMobile window.innerWidth 900; if (isMobile) { // Manually fit bounds with padding directionsRenderer.setOptions({ preserveViewport: true }); directionsRenderer.setDirections(response); const bounds response.routes0.bounds; // Padding: top 50, right 50, bottom 350 (overlay), left 50 map.fitBounds(bounds, { top: 50, right: 50, bottom: 350, left: 50 }); } else { // Default behavior for desktop directionsRenderer.setOptions({ preserveViewport: false }); directionsRenderer.setDirections(response); } calculatePrice(); } else { window.alert(Directions request failed due to + status); } } ); } else if (fromPlace) { map.setCenter(fromPlace.geometry.location); map.setZoom(15); new google.maps.Marker({ position: fromPlace.geometry.location, map: map, }); } else if (toPlace) { map.setCenter(toPlace.geometry.location); map.setZoom(15); new google.maps.Marker({ position: toPlace.geometry.location, map: map, }); } } function calculatePrice() { if (!fromPlace || !toPlace || !pricingConfig) return; // Calculate distance in km const distanceInMeters google.maps.geometry.spherical.computeDistanceBetween( fromPlace.geometry.location, toPlace.geometry.location ); const distance distanceInMeters / 1000; // Pricing Logic const config pricingConfigcurrentCountry; if (!config) return; let price 0; let rate null; if (selectedSize small) { rate config.small; } else if (selectedSize medium) { rate config.medium.bike; } else if (selectedSize large) { rate config.large; } if (rate) { const baseKm parseFloat(rate.base_km) || 0; const chargeableDistance Math.max(0, distance - baseKm); price rate.base + (chargeableDistance * rate.per_km); if (price rate.min) price rate.min; } // Update UI const priceEl document.getElementById(price-display); const valEl document.getElementById(price-value); // Format currency const currency config.currency || ZMW; valEl.textContent `${currency} ${price.toFixed(2)}`; priceEl.style.display block; } function proceedToBooking() { if (!fromPlace || !toPlace) { const dialog document.querySelector(native-dialog); dialog.alert(Please select both pickup and delivery locations.); return; } // Save destination address saveAddress(toPlace); // Store data for next page localStorage.setItem(booking_from, JSON.stringify({ name: fromPlace.name, address: fromPlace.formatted_address, lat: fromPlace.geometry.location.lat(), lng: fromPlace.geometry.location.lng() })); localStorage.setItem(booking_to, JSON.stringify({ name: toPlace.name, address: toPlace.formatted_address, lat: toPlace.geometry.location.lat(), lng: toPlace.geometry.location.lng() })); localStorage.setItem(booking_size, selectedSize); window.location.href /local-booking.php; } // Past Addresses Logic function saveAddress(place) { if (!place || !place.formatted_address) return; let addresses JSON.parse(localStorage.getItem(ac_saved_addresses) || ); // Remove if exists (to bump to top) addresses addresses.filter(a > a.address ! place.formatted_address); // Add new addresses.unshift({ name: place.name || place.formatted_address.split(,)0, address: place.formatted_address, lat: place.geometry.location.lat(), lng: place.geometry.location.lng() }); // Keep max 3 if (addresses.length > 3) addresses.length 3; localStorage.setItem(ac_saved_addresses, JSON.stringify(addresses)); } function loadAddresses() { const addresses JSON.parse(localStorage.getItem(ac_saved_addresses) || ); // Desktop Container const container document.getElementById(past-addresses); const list document.getElementById(pa-list); // Overlay Container (Mobile) const overlayList document.getElementById(pa-list-overlay); const recentBtn document.querySelector(.btn-link-recent); if (addresses.length 0) { if (container) container.style.display none; if (recentBtn) recentBtn.style.display none; return; } if (recentBtn) recentBtn.style.display window.innerWidth 900 ? inline-block : none; // Helper to build list item const createItem (addr) > { const item document.createElement(div); item.className pa-item; item.innerHTML ` span classpa-icon>📍/span> span classpa-text>${addr.name}/span> `; item.onclick () > { const latLng new google.maps.LatLng(addr.lat, addr.lng); toPlace { name: addr.name, formatted_address: addr.address, geometry: { location: latLng } }; document.getElementById(to-input).value addr.address; updateRoute(); // Close overlay if open document.getElementById(past-addresses-overlay).style.display none; }; return item; }; // Populate Desktop List if (list) { list.innerHTML ; container.style.display window.innerWidth > 900 ? block : none; addresses.forEach(addr > list.appendChild(createItem(addr))); } // Populate Mobile Overlay List if (overlayList) { overlayList.innerHTML ; addresses.forEach(addr > overlayList.appendChild(createItem(addr))); } } function toggleRecentAddresses() { const overlay document.getElementById(past-addresses-overlay); if (overlay.style.display flex) { overlay.style.display none; } else { overlay.style.display flex; } } // Re-check visibility on resize window.addEventListener(resize, loadAddresses); // Fetch Categories (Shop Types) document.addEventListener(DOMContentLoaded, async function() { loadAddresses(); const storeTypes {id:stype_69199bed87b3d,name:Pharmacies,slug:pharmacies,description:,status:active,icon:\/uploads\/store-types\/stype_69199bed87b3d_icon.png,created_at:2025-11-16 04:39:57,updated_at:2025-11-28 05:16:45,created_by:c0d783f6-5c68-46a5-8567-6523594a6889,updated_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d,is_international:0},{id:stype_69199bfd3737c,name:Gas,slug:gas,description:,status:active,icon:\/uploads\/store-types\/stype_69199bfd3737c_icon.png,created_at:2025-11-16 04:40:13,updated_at:2025-11-28 05:16:12,created_by:c0d783f6-5c68-46a5-8567-6523594a6889,updated_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d,is_international:0},{id:stype_69199c0c63595,name:Butcheries,slug:butcheries,description:,status:active,icon:\/uploads\/store-types\/stype_69199c0c63595_icon.png,created_at:2025-11-16 04:40:28,updated_at:2025-11-28 05:19:11,created_by:c0d783f6-5c68-46a5-8567-6523594a6889,updated_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d,is_international:0},{id:stype_69199c25026e8,name:Drinks,slug:drinks,description:,status:active,icon:\/uploads\/store-types\/stype_69199c25026e8_icon.png,created_at:2025-11-16 04:40:53,updated_at:2025-11-28 05:20:54,created_by:c0d783f6-5c68-46a5-8567-6523594a6889,updated_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d,is_international:0},{id:stype_69199c380168a,name:Fresh Produce,slug:fresh-produce,description:,status:active,icon:\/uploads\/store-types\/stype_69199c380168a_icon.png,created_at:2025-11-16 04:41:12,updated_at:2025-11-28 05:16:02,created_by:c0d783f6-5c68-46a5-8567-6523594a6889,updated_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d,is_international:0},{id:stype_691ac8f0e21c8,name:Groceries,slug:groceries,description:,status:active,icon:\/uploads\/store-types\/stype_691ac8f0e21c8_icon.png,created_at:2025-11-17 02:04:16,updated_at:2025-11-27 15:19:08,created_by:2423adb2-dc19-4e8b-8bbb-b6010d5d471f,updated_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d,is_international:0},{id:stype_692ae8f1b4c1c,name:Fashion,slug:fashion,description:,status:active,is_international:1,icon:\/uploads\/store-types\/stype_692ae8f1b4c1c_icon.png,created_at:2025-11-29 07:37:05,updated_at:2025-11-29 09:04:29,created_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d,updated_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d},{id:stype_692ae903a4560,name:Art & Handmade,slug:art-handmade,description:,status:active,is_international:1,icon:\/uploads\/store-types\/stype_692ae903a4560_icon.png,created_at:2025-11-29 07:37:23,updated_at:2025-11-29 09:04:16,created_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d,updated_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d},{id:stype_692ae90da3697,name:Food,slug:food,description:,status:active,is_international:1,icon:\/uploads\/store-types\/stype_692ae90da3697_icon.png,created_at:2025-11-29 07:37:33,updated_at:2025-11-29 09:04:04,created_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d,updated_by:bd02319a-e6e7-4caa-ae58-de0c9eea5e9d}; const localGrid document.getElementById(shopping-categories-local); const internationalGrid document.getElementById(shopping-categories-international); const localTypes storeTypes.filter(t > !t.is_international); const internationalTypes storeTypes.filter(t > t.is_international); const renderTypes (grid, list, emptyMessage, scopeLabel) > { if (!grid) return; grid.innerHTML ; if (list.length 0) { grid.innerHTML `p stylegrid-column: 1/-1; text-align:center; color:#666;>${emptyMessage}/p>`; return; } list.forEach(type > { const fallbackInitial (type.name || ?).charAt(0).toUpperCase(); const iconMarkup type.icon ? `div classtype-icon-wrap>img src${type.icon} alt${type.name} icon classtype-icon>/div>` : `div classtype-icon-wrap placeholder>span>${fallbackInitial}/span>/div>`; const card document.createElement(div); card.className category-card; card.innerHTML `${iconMarkup}h3>${type.name}/h3>`; card.onclick () > window.location.href /shopping/?type + type.slug; grid.appendChild(card); }); }; renderTypes(localGrid, localTypes, No local shop types found., Same-day delivery); renderTypes(internationalGrid, internationalTypes, No international shop types yet., Delivery ~1 week); // Mobile Wizard Init & Resize Handler function handleResponsiveLayout() { const isMobile window.innerWidth 900; if (isMobile) { // Ensure we are on a valid step, default to 1 if none active if (!document.querySelector(.wizard-step-container.active)) { goToStep(1); } } else { // On desktop, ensure everything is visible document.querySelectorAll(.wizard-step-container).forEach(el > { el.style.display ; // Clear inline styles if any interactions set them el.classList.add(active); }); } } window.addEventListener(resize, handleResponsiveLayout); handleResponsiveLayout(); // Init }); function goToStep(step) { // Only act if we are in mobile mode (or if we want wizard behavior on desktop too, but currently not) if (window.innerWidth > 900) return; document.querySelectorAll(.wizard-step-container).forEach(el > el.classList.remove(active)); const target document.getElementById(step- + step + -wrapper); if (target) { target.classList.add(active); // Specific logic if (step 2) { // resize map if needed or ensure its visible if (map) google.maps.event.trigger(map, resize); } } } /script> /main> div classwave-container> svg classwave flipped viewBox0 0 1440 320 preserveAspectRationone> path fill#992C23 fill-opacity1 dM0,96L60,112C120,128,240,160,360,165.3C480,171,600,149, 720,144C840,139,960,149,1080,165.3C1200,181,1320,203, 1380,213.3L1440,224L1440,320L1380,320C1320,320,1200,320, 1080,320C960,320,840,320,720,320C600,320,480,320, 360,320C240,320,120,320,60,320L0,320Z>/path> /svg> svg classwave overlay viewBox0 0 1440 320 preserveAspectRationone> path fill#992C23 fill-opacity1 dM0,64L60,90.7C120,117,240,171,360,165.3C480,160,600,96, 720,101.3C840,107,960,181,1080,186.7C1200,192,1320,128, 1380,106.7L1440,85.3L1440,320L1380,320C1320,320,1200,320, 1080,320C960,320,840,320,720,320C600,320,480,320, 360,320C240,320,120,320,60,320L0,320Z>/path> /svg>/div>footer> div classfooter-container> div classfooter-links> a href/privacy-policy/>Privacy Policy/a> a href/terms-of-service/>Terms of Service/a> a href/support/>Help Center/a> a href/careers/>Careers/a> /div> div classfooter-social> a hrefhttps://x.com/courierafri>span classicon-wrapper>img src/x.png altTwitter>/span>/a> a hrefhttps://www.facebook.com/profile.php?id61576980615029>span classicon-wrapper>img src/facebook.svg altFacebook>/span>/a> a hrefhttps://www.instagram.com/africourier_africa/>span classicon-wrapper>img src/instagram.svg altInstagram>/span>/a> a hrefhttps://www.linkedin.com/company/africourier/posts/?feedViewall>span classicon-wrapper>img src/linkedin.svg altLinkedIn>/span>/a> /div> p idcopyright-notice>© 2025 - 2026 AfriCourier Logistics Ltd./p> /div>/footer>!--Start of Tawk.to Script-->script typetext/javascript> var Tawk_API Tawk_API || {}; Tawk_API.visitor { name: localStorage.getItem(user_name) || Guest, email: localStorage.getItem(user_email) || guest@guest.com }; Tawk_LoadStart new Date(); (function () { var s1 document.createElement(script), s0 document.getElementsByTagName(script)0; s1.async true; s1.src https://embed.tawk.to/68420af074a763190ce37b86/1it0vlelb; s1.charset UTF-8; s1.setAttribute(crossorigin, *); s0.parentNode.insertBefore(s1, s0); })();/script>!--End of Tawk.to Script-->style> .wave-container { position: relative; line-height: 0; margin-bottom: -20px; } .wave { display: block; transform: scaleX(-1); width: 100vw; height: 60px; } .wave.overlay { position: absolute; top: 0px; left: 0px; opacity: 0.5; margin-top: -8px; pointer-events: none; } footer { background-color: #992C23; color: #fff; padding: 2rem 1rem; text-align: center; } .footer-container { max-width: 1200px; margin: 0 auto; } .footer-links, .footer-social { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.8rem; margin-bottom: 1rem; } .footer-links a { color: #ffffff; text-decoration: none; font-weight: 500; transition: color 0.3s ease; font-size: 0.8rem; } .footer-links a:hover { color: #D47A41; /* Light orange hover */ } .footer-social a .icon-wrapper { display: inline-block; border: solid 2px white; border-radius: 50%; width: 36px; height: 36px; overflow: hidden; position: relative; transition: transform 0.3s ease; } .footer-social a:hover .icon-wrapper { transform: scale(1.1); } .footer-social a .icon-wrapper::after { content: ; position: absolute; top: 0; left: -100%; width: 200%; height: 100%; background: linear-gradient(120deg, rgba(255, 255, 255, 0.0) 60%, rgba(255, 255, 255, 0.5) 80%, rgba(255, 255, 255, 0.0) 100%); transform: skewX(-20deg) translateX(0); pointer-events: none; animation: none; } .footer-social a:hover .icon-wrapper::after { transform: skewX(-20deg) translateX(100%); animation: shine-pre 1.5s cubic-bezier(0.4, 0.2, 0.2, 1); } .footer-social a .icon-wrapper img { width: 100%; height: 100%; display: block; border-radius: 50%; filter: grayscale(100%); } footer p { margin-top: 1rem; font-size: 0.8rem; } @keyframes shine-pre { 0% { transform: skewX(-20deg) translateX(0); } 60% { transform: skewX(-20deg) translateX(100%); } 100% { transform: skewX(-20deg) translateX(0); } }/style> native-dialog>/native-dialog> script srcnative-dialog.js>/script> script srchttps://maps.googleapis.com/maps/api/js?keyAIzaSyDyQZeDTlJsT8m3uUDQe7aJswn_60mEP-4&librariesplaces,geometry&callbackinitMap async defer>/script>/body>/html>
View on OTX
|
View on ThreatMiner
Please enable JavaScript to view the
comments powered by Disqus.
Data with thanks to
AlienVault OTX
,
VirusTotal
,
Malwr
and
others
. [
Sitemap
]