{"id":5241,"date":"2025-10-07T14:39:25","date_gmt":"2025-10-07T13:39:25","guid":{"rendered":"https:\/\/paragliding-in-madeira.com\/weather\/?page_id=5241"},"modified":"2026-04-25T15:44:35","modified_gmt":"2026-04-25T14:44:35","slug":"live-tracking","status":"publish","type":"page","link":"https:\/\/paragliding-in-madeira.com\/weather\/live-tracking\/","title":{"rendered":""},"content":{"rendered":"\n<figure class=\"wp-block-image alignfull size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"2560\" height=\"1379\" src=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/banner-live-tracking-title-xc-track-scaled.webp\" alt=\"Banner live tracking title xc track\" class=\"wp-image-5249\" srcset=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/banner-live-tracking-title-xc-track-scaled.webp 2560w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/banner-live-tracking-title-xc-track-300x162.webp 300w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/banner-live-tracking-title-xc-track-1024x552.webp 1024w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/banner-live-tracking-title-xc-track-768x414.webp 768w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/banner-live-tracking-title-xc-track-1536x828.webp 1536w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/banner-live-tracking-title-xc-track-2048x1103.webp 2048w\" sizes=\"auto, (max-width: 2560px) 100vw, 2560px\" \/><\/figure>\n\n\n\n<div class=\"wp-block-buttons is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-a89b3969 wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/paragliding-in-madeira.com\/weather\/\">Home<\/a><\/div>\n\n\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/paragliding-in-madeira.com\/weather\/weather-info\/\">Weather Info<\/a><\/div>\n\n\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/paragliding-in-madeira.com\/weather\/weather-stations\/\">Weather Stations<\/a><\/div>\n<\/div>\n\n\n\n<div style=\"height:11px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div class=\"wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained\">\n<div class=\"wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained\">\n<div class=\"wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained\">\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<div class=\"wp-block-group has-global-padding is-layout-constrained wp-block-group-is-layout-constrained\">\n<p class=\"has-text-align-center\">Unified Madeira sky experience<br>Merging Live Paragliding flights with general Aviation<br>To co-create a <strong>culture of mutual respect and safety for everyone<\/strong><br>Bridging the aviation gap one flight at a time<\/p>\n<\/div>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n<\/div>\n<\/div>\n<\/div>\n\n\n\n<div style=\"height:11px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-container-core-details-is-layout-c836557f wp-block-details-is-layout-flow\"><summary><\/summary>\n<!-- Previous map weather station widget from windguru\n<style>\n  \/* --- CONTAINER & MAP --- *\/\n  .madeira-weather-map {\n    position: relative;\n    width: 100%;\n    max-width: 1000px;\n    margin: 0 auto;\n    overflow: hidden;\n  }\n\n  \/* VRB text style *\/\n.airport-vrb {\n    position: absolute;\n    top: 3px;\n    font-size: 10px;\n    font-weight: normal;\n    color: #185e96;\n    display: none;\n    z-index: 3;\n    width: 100%;\n    text-align: center;\n}\n\n\/* ADDED: Gust speed number style (Orange for Gust, with spacing from the separator) *\/\n.gust-value {\n    color: orange; \n    font-weight: 500; \n    padding-left: 1px; \/* The fix you discovered *\/\n}\n\n  .madeira-weather-map img {\n    width: 100%;\n    height: auto;\n    display: block;\n  }\n\n  \/* --- WIDGET ANCHOR --- *\/\n  .weather-pin {\n    position: absolute;\n    z-index: 10;\n    transform: translate(-50%, -50%);\n    transition: transform 0.3s ease;\n    cursor: pointer;\n  }\n  \n  \/* Hover effects *\/\n @media (min-width: 768px) {\n    .weather-pin:hover { transform: translate(-50%, -50%) scale(2); z-index: 100; }\n  }\n  .weather-pin:active { transform: translate(-50%, -50%) scale(1.5); }\n\n  \/* --- THE CROPPER (The Circular Mask) --- *\/\n  .pin-crop {\n    width: 44px;  \n    height: 33px; \n    border-radius: 50%;\n    background: rgba(255, 255, 255, 0.7); \n    box-shadow: 0 2px 4px rgba(0,0,0,0.2);\n    overflow: hidden;\n    position: relative;\n    transition: all 0.3s ease;\n  }\n\n  \/* --- WINDGURU IFRAME ADJUSTMENTS (Hides the Title) --- *\/\n  .wg-content { \nwidth: 100%; \nmargin-top: -11px; \npadding-right: 10px;\n }\n  .wg-sizer { \nmax-width: 48px; \n}\n\n\/* --- Custom Style for LPMA (Madeira) Pin --- *\/\n  .pos-lpma .pin-crop {\n    background: rgba(255, 255, 255, 0.85); \n    width: 46px;  \n    height: 46px; \/* Taller for 3 lines *\/\n    border-radius: 50%;\n  }\n  \n  \/* --- Custom Style for LPPS (Porto Santo) Pin --- *\/\n  .pos-lpps .pin-crop {\n    width: 48px;   \/* custom mobile width *\/\n    height: 45px; \/* Taller for 3 lines *\/\n    background: rgba(255, 255, 255, 0.8); \n  }\n\n  \/* --- The Common Airport Widget Styles (LPMA and LPPS)  --- *\/\n  .airport-container {\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    height: 100%;\n    width: 100%;\n    position: relative;\n    font-family: Arial, sans-serif;\n    color: #333;\n  }\n\n  .airport-arrow-icon {\n    width: 21px;\n    height: 17px;\n    fill: #185e96;\n    position: absolute;\n    top: 1px;\n    transition: transform 1s ease-out;\n    display: block;\n  }\n\n  \/* The Speed Number (Applies to the whole Speed \/ Gust text area) *\/\n  .airport-speed-text {\n    color: darkblue; \/* Wind Speed color *\/\n    font-weight: bold;\n    font-size: 12px;\n    margin-top: 14px; \n    z-index: 2;\n  }\n\n\/* The 'km\/h' unit text *\/\n  .airport-unit {\n    font-size: 7px;\n    line-height: 8px;\n    color: #666;\n    margin-top: -2px; \/* Close to the number text *\/\n  }\n\n  \/* Desktop \"GROW\" MODE *\/\n  @media (min-width: 768px) {\n    .weather-pin { \ntransform: translate(-50%, -50%) scale(1.7); }\n\n\/* Adjust Airport Custom Widget for Desktop Size *\/\n    .airport-arrow-icon { width: 22px; \nheight: 18px; \ntop: 1px; }\n    .airport-speed-text { \nfont-size: 12px; \nmargin-top: 11px; \n}\n    .airport-unit { \nfont-size: 8px; }\n\n    .airport-vrb { \ntop: 1px;  \n}\n  }\n\n  \/* --- COORDINATES --- *\/\n  .pos-lpma { top: 73%; left: 79%; } \n  .pos-santacruz { top: 85%; left: 68%; }\n  .pos-canhas { top: 69%; left: 25%; }\n  .pos-portela { top: 55%; left: 69%; }\n  .pos-maiata { top: 36%; left: 76%; }\n  .pos-lpps { top: 15%; left: 92%; } \n\n<\/style>\n\n<div class=\"madeira-weather-map\">\n\n  <img decoding=\"async\" src=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/11\/Madeira-and-Porto-Santo2.webp\" alt=\"Madeira Weather Map\">\n\n  <div class=\"weather-pin pos-canhas\">\n    <div class=\"pin-crop\">\n      <div class=\"wg-content\"><div class=\"wg-sizer\">\n        <script id=\"wglive_6014_1728522217581\">\n          (function (window, document) {\n            var loader = function () {\n              var arg = [\"spot=6014\",\"uid=wglive_6014_1728522217581\",\"color=light\",\"wj=kmh\",\"tj=c\",\"avg_min=0\",\"gsize=200\",\"msize=250\",\"m=3\",\"arrow=y\",\"show=n,g,c,f,m\"];\n              var script = document.createElement(\"script\");\n              var tag = document.getElementsByTagName(\"script\")[0];\n              script.src = \"https:\/\/www.windguru.cz\/js\/wglive.php?\"+(arg.join(\"&\"));\n              tag.parentNode.insertBefore(script, tag);\n            };\n            window.addEventListener ? window.addEventListener(\"load\", loader, false) : window.attachEvent(\"onload\", loader);\n          })(window, document);\n        <\/script>\n      <\/div><\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"weather-pin pos-portela\">\n    <div class=\"pin-crop\">\n      <div class=\"wg-content\"><div class=\"wg-sizer\">\n        <script id=\"wglive_6008_1730946002158\">\n          (function (window, document) {\n            var loader = function () {\n              var arg = [\"spot=6008\",\"uid=wglive_6008_1730946002158\",\"color=light\",\"wj=kmh\",\"tj=c\",\"avg_min=0\",\"gsize=200\",\"msize=250\",\"m=3\",\"arrow=y\",\"show=n,g,c,f,m\"];\n              var script = document.createElement(\"script\");\n              var tag = document.getElementsByTagName(\"script\")[0];\n              script.src = \"https:\/\/www.windguru.cz\/js\/wglive.php?\"+(arg.join(\"&\"));\n              tag.parentNode.insertBefore(script, tag);\n            };\n            window.addEventListener ? window.addEventListener(\"load\", loader, false) : window.attachEvent(\"onload\", loader);\n          })(window, document);\n        <\/script>\n      <\/div><\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"weather-pin pos-maiata\">\n    <div class=\"pin-crop\">\n      <div class=\"wg-content\"><div class=\"wg-sizer\">\n        <script id=\"wglive_5899_1728522217581\">\n          (function (window, document) {\n            var loader = function () {\n              var arg = [\"spot=5899\",\"uid=wglive_5899_1728522217581\",\"color=light\",\"wj=kmh\",\"tj=c\",\"avg_min=0\",\"gsize=200\",\"msize=250\",\"m=3\",\"arrow=y\",\"show=n,g,c,f,m\"];\n              var script = document.createElement(\"script\");\n              var tag = document.getElementsByTagName(\"script\")[0];\n              script.src = \"https:\/\/www.windguru.cz\/js\/wglive.php?\"+(arg.join(\"&\"));\n              tag.parentNode.insertBefore(script, tag);\n            };\n            window.addEventListener ? window.addEventListener(\"load\", loader, false) : window.attachEvent(\"onload\", loader);\n          })(window, document);\n        <\/script>\n      <\/div><\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"weather-pin pos-santacruz\">\n    <div class=\"pin-crop\">\n      <div class=\"wg-content\"><div class=\"wg-sizer\">\n        <script id=\"wglive_15575_1764891719321\">\n          (function (window, document) {\n            var loader = function () {\n              var arg = [\"spot=15575\",\"uid=wglive_15575_1764891719321\",\"color=light\",\"wj=kmh\",\"tj=c\",\"avg_min=0\",\"gsize=200\",\"msize=250\",\"m=45\",\"arrow=y\",\"show=n,g,c,f,m\"];\n              var script = document.createElement(\"script\");\n              var tag = document.getElementsByTagName(\"script\")[0];\n              script.src = \"https:\/\/www.windguru.cz\/js\/wglive.php?\"+(arg.join(\"&\"));\n              tag.parentNode.insertBefore(script, tag);\n            };\n            window.addEventListener ? window.addEventListener(\"load\", loader, false) : window.attachEvent(\"onload\", loader);\n          })(window, document);\n        <\/script>\n      <\/div><\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"weather-pin pos-lpma\" data-airport=\"lpma\">\n    <div class=\"pin-crop\">\n      <div class=\"airport-container\">\n        <svg class=\"airport-arrow-icon\" id=\"lpma-arrow\" viewBox=\"0 0 24 24\"><path d=\"M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71z\"\/><\/svg>\n        <div class=\"airport-vrb\" id=\"lpma-vrb\">VRB<\/div>\n        <div class=\"airport-speed-text\" id=\"lpma-speed-gust\">--<\/div>\n        <div class=\"airport-unit\" id=\"lpma-unit\">km\/h<\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"weather-pin pos-lpps\" data-airport=\"lpps\">\n    <div class=\"pin-crop\">\n      <div class=\"airport-container\">\n        <svg class=\"airport-arrow-icon\" id=\"lpps-arrow\" viewBox=\"0 0 24 24\"><path d=\"M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71z\"\/><\/svg>\n        <div class=\"airport-vrb\" id=\"lpps-vrb\">VRB<\/div>\n        <div class=\"airport-speed-text\" id=\"lpps-speed-gust\">--<\/div>\n        <div class=\"airport-unit\" id=\"lpps-unit\">km\/h<\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n\n<\/div>\n\n<script>\ndocument.addEventListener(\"DOMContentLoaded\", function() {\n\n  const STALE_MS = 60 * 60 * 1000; \/\/ 60 minutes\n  const REFRESH_MS = 5 * 60 * 1000; \/\/ refresh every 5 minutes\n\n  const airports = [\n    {\n      id: 'lpma',\n      jsonUrl: 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/metar-wind-lpma.json',\n      speedId: 'lpma-speed-gust', \/\/ Now holds the combined HTML\n      arrowId: 'lpma-arrow',\n      vrbId: 'lpma-vrb',\n      unitId: 'lpma-unit',\n      lightbox: 'https:\/\/paragliding-in-madeira.com\/weather\/lpma\/'\n    },\n    {\n      id: 'lpps',\n      jsonUrl: 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/metar-wind-lpps.json',\n      speedId: 'lpps-speed-gust', \/\/ Now holds the combined HTML\n      arrowId: 'lpps-arrow',\n      vrbId: 'lpps-vrb',\n      unitId: 'lpps-unit',\n      lightbox: 'https:\/\/paragliding-in-madeira.com\/weather\/lpps\/'\n    }\n  ];\n\n  \/**\n   * Shows primary display elements (used when fresh data is found).\n   *\/\n  function showWidget(a) {\n    const speedEl = document.getElementById(a.speedId);\n    const unitEl  = document.getElementById(a.unitId);\n    if (speedEl) { speedEl.style.display = 'block'; }\n    if (unitEl)  { unitEl.style.display = 'block'; }\n  }\n\n\n  \/**\n   * Hides all display elements (used when data is stale or on error).\n   *\/\n  function hideWidget(a) {\n    const speedEl = document.getElementById(a.speedId);\n    const arrowEl = document.getElementById(a.arrowId);\n    const vrbEl   = document.getElementById(a.vrbId);\n    const unitEl  = document.getElementById(a.unitId);\n\n    \/\/ Hide all elements\n    if (speedEl) { speedEl.innerHTML = '--'; speedEl.style.display = 'none'; }\n    if (unitEl)  unitEl.style.display = 'none';\n    if (arrowEl) arrowEl.style.display = 'none';\n    if (vrbEl)   vrbEl.style.display = 'none';\n  }\n\n\n  async function updateAirportWidget(a) {\n    try {\n      const res = await fetch(a.jsonUrl + '?t=' + Date.now());\n      if (!res.ok) throw new Error('HTTP ' + res.status);\n      const data = await res.json();\n      if (!data || !Array.isArray(data) || data.length === 0) {\n        hideWidget(a); return;\n      }\n\n      const latest = data[data.length - 1];\n      if (!latest || !latest.time) { hideWidget(a); return; }\n\n      const age = Date.now() - new Date(latest.time).getTime();\n      if (age > STALE_MS) { hideWidget(a); return; }\n      \n      showWidget(a); \n\n      const speedEl = document.getElementById(a.speedId);\n      const arrowEl = document.getElementById(a.arrowId);\n      const vrbEl   = document.getElementById(a.vrbId);\n\n      \/\/ Sanitize numbers\n      const speed = (latest.speed === null || typeof latest.speed === 'undefined') ? null : Math.round(latest.speed);\n      const gust = (latest.gust === null || typeof latest.gust === 'undefined') ? null : Math.round(latest.gust);\n      const dir = (typeof latest.direction === 'number') ? latest.direction : null;\n\n      \/\/ --- SPEED\/GUST LOGIC ---\n      if (speedEl) {\n          let displayContent = '--';\n          \n          if (speed !== null) {\n              if (gust !== null && gust > speed) {\n                  \/\/ Format as Speed \/ <span class=\"gust-value\">Gust<\/span>\n                  \/\/ Space added before \/ for visual separation\n                  displayContent = `${speed} \/<span class=\"gust-value\">${gust}<\/span>`;\n              } else {\n                  \/\/ Format as Speed only\n                  displayContent = String(speed);\n              }\n          }\n          \n          \/\/ Use innerHTML to render the span element\n          speedEl.innerHTML = displayContent;\n      }\n\n\n      \/\/ --- DIRECTION LOGIC ---\n      const isVRB = dir === 1;\n      const isCalm = dir === 0 && speed === 0;\n\n      if (isVRB) {\n        if (arrowEl) arrowEl.style.display = 'none';\n        if (vrbEl) vrbEl.style.display = 'block';\n      } else if (isCalm) {\n        if (arrowEl) arrowEl.style.display = 'none';\n        if (vrbEl) vrbEl.style.display = 'none';\n        if (speedEl) speedEl.innerHTML = '0'; \/\/ Use innerHTML for consistency\n      } else if (dir === null) {\n        if (arrowEl) arrowEl.style.display = 'none';\n        if (vrbEl) vrbEl.style.display = 'none';\n      } else {\n        \/\/ normal numeric direction\n        if (arrowEl) {\n          const rotation = (dir + 180) % 360;\n          arrowEl.style.transform = `rotate(${rotation}deg)`;\n          arrowEl.style.display = 'block';\n        }\n        if (vrbEl) vrbEl.style.display = 'none';\n      }\n\n    } catch (err) {\n      console.error('Airport error:', a.id, err);\n      hideWidget(a);\n    }\n  }\n\n  \/\/ initial + periodic refresh\n  function refreshAll() {\n    airports.forEach(a => updateAirportWidget(a));\n  }\n  refreshAll();\n  setInterval(refreshAll, REFRESH_MS);\n\n  \/* ---------------- LIGHTBOX (UNCHANGED) ---------------- *\/\n  if (!document.getElementById('airport-lightbox')) {\n    document.body.insertAdjacentHTML('beforeend', `\n      <div id=\"airport-lightbox\" style=\"display:none;position:fixed;top:0;left:0;width:100vw;height:100vh;background:rgba(0,0,0,0.7);z-index:9999;justify-content:center;align-items:center;\">\n        <div style=\"position:relative;width:90%;max-width:1000px;height:75%;border-radius:8px;overflow:hidden;\">\n          <iframe id=\"airport-lightbox-iframe\" src=\"\" style=\"width:100%;height:100%;border:none;\"><\/iframe>\n          <button id=\"airport-lightbox-close\" style=\"position:absolute;top:7px;right:11px;background:transparent;border:none;color:white;font-size:24px;cursor:pointer;\">\u2715<\/button>\n        <\/div>\n      <\/div>\n    `);\n  }\n\n  const lightbox = document.getElementById('airport-lightbox');\n  const iframe = document.getElementById('airport-lightbox-iframe');\n\n  document.querySelectorAll('.weather-pin[data-airport]').forEach(pin => {\n    pin.addEventListener('click', () => {\n      const id = pin.getAttribute('data-airport');\n      const a = airports.find(x => x.id === id);\n      if (a) {\n        iframe.src = a.lightbox + '?nocache=' + Date.now();\n        lightbox.style.display = 'flex';\n      }\n    });\n  });\n\n  document.getElementById('airport-lightbox-close').addEventListener('click', () => {\n    iframe.src = '';\n    lightbox.style.display = 'none';\n  });\n\n  lightbox.addEventListener('click', (e) => { if (e.target === lightbox) { iframe.src=''; lightbox.style.display='none'; } });\n\n});\n<\/script>\n-->\n\n\n\n<!-- Brilliant with day and night from our solunar and direction logic. windguru lazy load, independent station wind and direction limits and gust check -->\n<!-- Added temp and rain LAUNCH VERSION 14-03-2026  -->\n<style>\n  \/* --- CONTAINER & MAP --- *\/\n  .madeira-weather-map {\n    position: relative;\n    width: 100%;\n    max-width: 1000px;\n    margin: 0 auto;\n    overflow: hidden;\n  }\n\n\/* 1. The Toggle: Hide arrow if NOT in wind mode *\/\n.madeira-weather-map:not([data-view=\"wind\"]) .airport-arrow-icon,\n.madeira-weather-map:not([data-view=\"wind\"]) .airport-unit { \n  display: none !important; \n}\n\n\/* Create a simple grey overlay for Temp and Rain views *\/\n.madeira-weather-map:not([data-view=\"wind\"]) .pin-overlay {\n  background-color: #e5e5e5 !important; \/* Light grey *\/\n opacity: 0.8 !important; \/* increased slightly for better readability *\/\n}\n\n.madeira-weather-map:not([data-view=\"wind\"]) .airport-container { \n  justify-content: center !important; \n}\n\n.madeira-weather-map:not([data-view=\"wind\"]) .airport-speed-text { \n  margin-top: 0 !important; \n  line-height: 1.1; \n}\n\n\/* Sub-text Styling (Humidity % and Total Rain mm) *\/\n.madeira-weather-map:not([data-view=\"wind\"]) .airport-speed-text {\n  font-size: 14px ; \n  color: #185e96; \/* Deep blue for clear visibility *\/\n  letter-spacing: 3px; \/* Reset any mobile wind tracking *\/\n}\n\n\/* Top Value: Temperature or Live Rain mm\/h *\/\n.live-value, .live-rain-val {\nfont-size: 11px;\n  margin-top: -12px; \/* Replaces the inline JS style *\/\n  display: block;\n  line-height: 2;\n}\n\n\/* Sub-label styling for Total Rain - Absolute Centering *\/\n.rain-total {\n  font-size: 10px;\n  font-weight: bold;\n  opacity: 0.85;\n  color: #444; \n  text-shadow: none;\n  position: absolute;\n  bottom: 6px; \/* Locks to bottom of circle *\/\n  left: 0;\n  right: 0;\n  text-align: center;\n  display: block;\n  line-height: 0.7; \/* Tighter vertical stacking for the mm *\/\n}\n\n.temp-hum {\n  font-size: 10px;\n  font-weight: bold;\n  opacity: 0.85;\n  color: #444; \n  text-shadow: none;\n  position: absolute;\n  bottom: 10px; \/* pulls hum closer to temp *\/\n  left: 0;\n  right: 0;\n  text-align: center;\n  display: block;\n  line-height: 0.5;\n}\n\n\/* Individually target the mm unit *\/\n.unit-mm {\n  font-size: 8px;\n  font-weight: normal; \/* making it thinner makes the number pop more *\/\n  text-transform: lowercase;\n  display: inline-block;\n  margin-top: -3px; \/* Pulls it up slightly closer to its number *\/\n}\n\n\/* --- VIEW SWITCHER BUTTONS --- *\/\n.map-view-switcher {\n  display: flex;\n  gap: 10px;\n  justify-content: center;\n  margin-bottom: 15px;\n}\n\n.switcher-btn {\n  padding: 8px 16px;\n  border: 1px solid #185e96;\n  background: white;\n  color: #185e96;\n  font-weight: bold;\n  border-radius: 20px;\n  cursor: pointer;\n  transition: all 0.3s ease;\n  font-size: 12px;\n}\n\n.switcher-btn.active {\n  background: #185e96;\n  color: white;\n}\n\n  \/* VRB text style *\/\n  .airport-vrb {\n    position: absolute;\n    top: 3px;\n    font-size: 10px;\n    font-weight: normal;\n    color: #185e96;\n    display: none;\n    z-index: 3;\n    width: 100%;\n    text-align: center;\n  }\n\n  .gust-value {\n    color: #853e08; \/* Slightly darker orange for better contrast *\/\n    font-weight: 500; \n    padding-left: 1px;\nletter-spacing: -0.1px; \/* Keeps the gust digits tight to the slash *\/\n  }\n\n  .madeira-weather-map img {\n    width: 100%;\n    height: auto;\n    display: block;\n  }\n\n  \/* --- WIDGET ANCHOR --- *\/\n  .weather-pin {\n    position: absolute;\n    z-index: 10;\n    transform: translate(-50%, -50%);\n    transition: transform 0.3s ease;\n    cursor: pointer;\n  }\n  \n  @media (min-width: 768px) {\n    .weather-pin:hover { transform: translate(-50%, -50%) scale(2); z-index: 100; }\n  }\n  .weather-pin:active { transform: translate(-50%, -50%) scale(1.5); }\n\n  \/* --- THE CROPPER --- *\/\n  .pin-crop {\n    width: 46px;   \n    height: 46px; \n    border-radius: 50%;\n    background: rgba(255, 255, 255, 0.85);  \/* fff if needed Solid White Base *\/\n    box-shadow: 0 1px 3px rgba(0,0,0,0.2);\n    overflow: hidden;\n    position: relative;\n    transition: all 0.3s ease;\n  }\n\n  \/* --- COLOR OVERLAY --- \n     This sits on top of the white bg but behind the text \n     We use JS to change the background-color of this layer \n  *\/\n  .pin-overlay {\n    position: absolute;\n    top: 0; left: 0;\n    width: 100%; height: 100%;\n    opacity: 0.2; \/* The magic opacity for the color overlay *\/\n    z-index: 1;\n    transition: background-color 0.5s ease;\n    background-color: transparent; \/* Default *\/\n  }\n  \/* DESKTOP OVERRIDE: Richer opacity for larger screens *\/\n  @media (min-width: 768px) {\n    .pin-overlay { opacity: 0.4; }\n}\n\n  \/* Specific Adjustments for Porto Santo Pin *\/\n  .pos-lpps .pin-crop {\n    width: 48px;\n    height: 45px;\n  }\n\n  .airport-container {\n    display: flex; \n    flex-direction: column; \n    align-items: center; \n    justify-content: center;\n    height: 100%; \n    width: 100%; \n    position: relative; \n    font-family: Arial, sans-serif; \n    color: #333;\n    z-index: 2; \/* Text sits ABOVE the overlay *\/\n  }\n\n  .airport-arrow-icon {\n    width: 21px; \n    height: 17px; \n    fill: #185e96; \n    position: absolute; \n    top: 1px;\n    transition: transform 1s ease-out; \n    display: block;\n  }\n\n  \/* This is the Wind speed and Gust text size and style *\/\n  .airport-speed-text {\n    color: darkblue; \n    font-weight: bold; \n    font-size: 11px; \n    margin-top: 14px; \n    z-index: 2;\n    \/* Gentle white shadow to ensure text pops against any color *\/\n    text-shadow: 0px 0px 1px rgba(0, 0, 0, 0.3);\nwhite-space: nowrap; \n\/* makes everything into one line *\/\n  display: inline-block; \n\/* for transform scale to work *\/\n  }\n\n  .airport-unit { \n    font-size: 7px; \n    line-height: 8px; \n    color: #444; \n    margin-top: -2px; \n    text-shadow: 0px 0px 1px rgba(0, 0, 0, 0.4);\n  }\n\n  \/* --- TRIGGER LAYER (Invisible but clickable) --- *\/\n  .wg-proxy-layer { \n    position: absolute; \n    top: 0; left: 0; \n    width: 100%; \n    height: 100%; \n    opacity: 0; \n    z-index: 5; \n  }\n\n  \/* Desktop \"GROW\" MODE *\/\n  @media (min-width: 768px) {\n    .weather-pin { transform: translate(-50%, -50%) scale(1.7); }\n    .airport-arrow-icon { width: 22px; height: 18px; top: 1px; }\n    .airport-speed-text { font-size: 11px; margin-top: 11px; }\n    .airport-unit { font-size: 8px; }\n    .airport-vrb { top: 1px; }\n  }\n\n  \/* --- COORDINATES --- *\/\n  .pos-lpma { top: 73%; left: 79%; } \n  .pos-santacruz { top: 83%; left: 67%; }\n  .pos-canhas { top: 69%; left: 25%; }\n  .pos-portela { top: 55%; left: 69%; }\n  .pos-maiata { top: 36%; left: 76%; }\n  .pos-lpps { top: 15%; left: 92%; } \n\n  \/* desktop-only adjustment for Santa Cruz Pin position *\/\n  @media (min-width: 768px) {\n    .pos-santacruz { left: 72% ; }\n  }\n\n  \/* 4. MOBILE OVERRIDES *\/\n  @media (max-width: 480px) {\n    .pin-crop { width: 40px; height: 40px; }\n    .airport-speed-text { font-size: 11px; margin-top: 8px; }\n    .airport-unit { font-size: 8px; }\n    .airport-arrow-icon { width: 16px; height: 14px; }\n    .pos-maiata { left: 78%; }\n  }\n\n\/* Hide LPPS and LPMA stations on TEMP and RAIN views *\/\n.madeira-weather-map:not([data-view=\"wind\"]) .pos-lpps,\n.madeira-weather-map:not([data-view=\"wind\"]) .pos-lpma {\n  display: none;\n}\n\n<\/style>\n\n<div class=\"map-view-switcher\">\n  <button class=\"switcher-btn active\" id=\"btn-wind\" onclick=\"setMapView('wind')\">WIND<\/button>\n  <button class=\"switcher-btn\" id=\"btn-temp\" onclick=\"setMapView('temp')\">TEMP<\/button>\n  <button class=\"switcher-btn\" id=\"btn-rain\" onclick=\"setMapView('rain')\">RAIN<\/button>\n<\/div>\n\n<div class=\"madeira-weather-map\" id=\"lazy-weather-map\" data-view=\"wind\">\n\n  <img decoding=\"async\" src=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/11\/Madeira-and-Porto-Santo2.webp\" alt=\"Madeira Weather Map\">\n\n  <div class=\"weather-pin pos-canhas\" data-station=\"canhas\">\n    <div class=\"pin-crop\">\n      <div class=\"pin-overlay\" id=\"canhas-overlay\"><\/div>\n      <div class=\"wg-proxy-layer\"><\/div>\n      <div class=\"airport-container\">\n        <svg class=\"airport-arrow-icon\" id=\"canhas-arrow\" viewBox=\"0 0 24 24\"><path d=\"M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71z\"\/><\/svg>\n        <div class=\"airport-speed-text\" id=\"canhas-speed-gust\">&#8212;<\/div>\n        <div class=\"airport-unit\" id=\"canhas-unit\">km\/h<\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"weather-pin pos-portela\" data-station=\"portela\">\n    <div class=\"pin-crop\">\n      <div class=\"pin-overlay\" id=\"portela-overlay\"><\/div>\n      <div class=\"wg-proxy-layer\">\n              <\/div>\n      <div class=\"airport-container\">\n        <svg class=\"airport-arrow-icon\" id=\"portela-arrow\" viewBox=\"0 0 24 24\"><path d=\"M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71z\"\/><\/svg>\n        <div class=\"airport-speed-text\" id=\"portela-speed-gust\">&#8212;<\/div>\n        <div class=\"airport-unit\" id=\"portela-unit\">km\/h<\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"weather-pin pos-maiata\" data-station=\"maiata\">\n    <div class=\"pin-crop\">\n      <div class=\"pin-overlay\" id=\"maiata-overlay\"><\/div>\n      <div class=\"wg-proxy-layer\">\n              <\/div>\n      <div class=\"airport-container\">\n        <svg class=\"airport-arrow-icon\" id=\"maiata-arrow\" viewBox=\"0 0 24 24\"><path d=\"M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71z\"\/><\/svg>\n        <div class=\"airport-speed-text\" id=\"maiata-speed-gust\">&#8212;<\/div>\n        <div class=\"airport-unit\" id=\"maiata-unit\">km\/h<\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"weather-pin pos-santacruz\" data-station=\"santacruz\">\n    <div class=\"pin-crop\">\n<div class=\"pin-overlay\" id=\"santacruz-overlay\"><\/div>\n      <div class=\"wg-proxy-layer\">\n             <\/div>\n      <div class=\"airport-container\">\n        <svg class=\"airport-arrow-icon\" id=\"santacruz-arrow\" viewBox=\"0 0 24 24\"><path d=\"M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71z\"\/><\/svg>\n        <div class=\"airport-speed-text\" id=\"santacruz-speed-gust\">&#8212;<\/div>\n        <div class=\"airport-unit\" id=\"santacruz-unit\">km\/h<\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"weather-pin pos-lpma\" data-station=\"lpma\">\n    <div class=\"pin-crop\">\n      <div class=\"airport-container\">\n        <svg class=\"airport-arrow-icon\" id=\"lpma-arrow\" viewBox=\"0 0 24 24\"><path d=\"M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71z\"\/><\/svg>\n        <div class=\"airport-vrb\" id=\"lpma-vrb\">VRB<\/div>\n        <div class=\"airport-speed-text\" id=\"lpma-speed-gust\">&#8212;<\/div>\n        <div class=\"airport-unit\" id=\"lpma-unit\">km\/h<\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"weather-pin pos-lpps\" data-station=\"lpps\">\n    <div class=\"pin-crop\">\n      <div class=\"airport-container\">\n        <svg class=\"airport-arrow-icon\" id=\"lpps-arrow\" viewBox=\"0 0 24 24\"><path d=\"M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71z\"\/><\/svg>\n        <div class=\"airport-vrb\" id=\"lpps-vrb\">VRB<\/div>\n        <div class=\"airport-speed-text\" id=\"lpps-speed-gust\">&#8212;<\/div>\n        <div class=\"airport-unit\" id=\"lpps-unit\">km\/h<\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script>\n\/\/ Returns \"YYYY-MM-DD\" locked to Madeira time\nfunction getMadeiraDateString() {\n  return new Date().toLocaleDateString('en-CA', { timeZone: 'Atlantic\/Madeira' });\n}\n\n\/\/ Global state - default wind\nlet currentMapMode = 'wind';\n\n\/\/ 1. GLOBAL SWITCHER FUNCTION\nwindow.setMapView = function(mode) {\n  currentMapMode = mode;\ndocument.getElementById('lazy-weather-map').setAttribute('data-view', mode);\n  \n\/\/ Update Map Attribute for CSS centering\n  const map = document.getElementById('lazy-weather-map');\n  if (map) map.setAttribute('data-view', mode);\n  \n  \/\/ 2. Update Button UI\ndocument.querySelectorAll('.switcher-btn').forEach(btn => btn.classList.remove('active'));\n  const activeBtn = document.getElementById('btn-' + mode);\n  if (activeBtn) activeBtn.classList.add('active');\n  \n  \/\/ 3. Trigger Re-render using the global bridge\n  if (typeof window.refreshAll === \"function\") {\n    window.refreshAll();\n  }\n};\n\ndocument.addEventListener(\"DOMContentLoaded\", function() {\n  const STALE_MS = 60 * 60 * 1000;\n  const REFRESH_MS = 1 * 60 * 1000;\n\n  \/\/ --- 1. ROBUST DAY\/NIGHT CYCLE (Moon Script Logic) ---\n  \/\/ Default fallbacks in case fetch is slow (09:00 to 21:00)\n  \/\/ We use simple strings \"HH:MM\" to compare, just like your Moon script.\n  let globalSunrise = \"09:00\"; \n  let globalSunset = \"21:00\";\n\n  \/\/ Fetch only once per session (or every hour) to save resources\n  async function fetchSolunarData() {\n    try {\n      const res = await fetch('https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/moon-time\/solunar-data.json?t=' + Date.now());\n      const data = await res.json();\n      if (data && data.today) {\n        \/\/ We just store the strings. No complex Date math needed.\n        if(data.today.sunrise) globalSunrise = data.today.sunrise; \/\/ e.g., \"07:56\"\n        if(data.today.sunset) globalSunset = data.today.sunset;    \/\/ e.g., \"18:48\"\n        \/\/ Force a refresh of the widgets now that we have accurate times\n        refreshAll();\n      }\n    } catch (e) {\n      console.log(\"Solunar sync failed, using defaults:\", e);\n    }\n  }\n\n  \/\/ The Check: Atlantic\/Madeira time for DST Safety\n  function isNightTime() {\n    const now = new Date();\n    \n\/\/ toLocaleString extracts exact HH:mm in Madeira, handles DST shifts (WET - WEST)\n    const madeiraTimeStr = now.toLocaleString('en-GB', {\n      hour: '2-digit',\n      minute: '2-digit',\n      hour12: false,\n      timeZone: 'Atlantic\/Madeira'\n    });\n    \n\/\/ It is DAY if current Madeira time is between Rise and Set, Otherwise Night.\n    const isDay = (madeiraTimeStr >= globalSunrise && madeiraTimeStr < globalSunset);\n\n    return !isDay;\n  }\n\n  \/\/ --- STATION CONFIGURATION ---\n  \/\/ ADJUST EACH STATION LIMITS HERE INDEPENDENTLY\n  \/\/ low\/med\/high correspond to Light Blue \/ Green \/ Yellow\n  \/\/ Anything above 'high' becomes Orange\n  \/\/ gustMax: If gust exceeds this, it turns Orange\n  const stations = [\n    {\n       id: 'canhas', isWindguru: true, wgSpot: '6014',\n      jsonUrl: 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/canhas\/' + getMadeiraDateString() + '.json',\n      speedId: 'canhas-speed-gust', arrowId: 'canhas-arrow', unitId: 'canhas-unit', overlayId: 'canhas-overlay',\n      \/\/ Canhas: Takeoff orientation SW (222) - Flyable from 180 to 280\n      sector: { start: 180, end: 280 },\n      \/\/ Speed limits: <= 5 is blue, <= 17 green, <= 22 yellow, >22 orange\n      limits: { low: 5, med: 17, high: 22 }, \n      gustMax: 27 \/\/ Above this gust limit shows orange\n    },\n    {\n      id: 'portela', isWindguru: true, wgSpot: '6008',\n      jsonUrl: 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/portela\/' + getMadeiraDateString() + '.json',\n      speedId: 'portela-speed-gust', arrowId: 'portela-arrow', unitId: 'portela-unit', overlayId: 'portela-overlay',\n      \/\/ Portela: Takeoff orientation North (360) - Flyable from 330 to 050 \n      sector: { start: 330, end: 50 },\n      \/\/ Speed limits: <= 5 is blue, <= 18 green, <= 24 yellow, >24 orange\n      limits: { low: 5, med: 18, high: 24 }, \n      gustMax: 30 \/\/ Above this gust limit shows orange\n    },\n    {\n       id: 'maiata', isWindguru: true, wgSpot: '5899',\n      jsonUrl: 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/maiata\/' + getMadeiraDateString() + '.json',\n      speedId: 'maiata-speed-gust', arrowId: 'maiata-arrow', unitId: 'maiata-unit', overlayId: 'maiata-overlay',\n      \/\/ Maiata: Takeoff orientation North (360) - Flyable from 333 to 055 \n      sector: { start: 333, end: 55 },\n      \/\/ Speed limits: <= 17 is blue, <= 26 green, <= 33 yellow, >33 orange\n      limits: { low: 17, med: 26, high: 33 },\n      gustMax: 35 \/\/ Above this gust limit shows orange\n    },\n    {\n        id: 'santacruz', isWindguru: true, wgSpot: '15575',\n      jsonUrl: 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/santacruz\/' + getMadeiraDateString() + '.json',\n      speedId: 'santacruz-speed-gust', arrowId: 'santacruz-arrow', unitId: 'santacruz-unit', overlayId: 'santacruz-overlay'\n    },\n    {\n      id: 'lpma',\n      jsonUrl: 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/metar-wind-lpma.json',\n      speedId: 'lpma-speed-gust', arrowId: 'lpma-arrow', vrbId: 'lpma-vrb', unitId: 'lpma-unit',\n      lightbox: 'https:\/\/paragliding-in-madeira.com\/weather\/lpma\/'\n    },\n    {\n      id: 'lpps',\n      jsonUrl: 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/metar-wind-lpps.json',\n      speedId: 'lpps-speed-gust', arrowId: 'lpps-arrow', vrbId: 'lpps-vrb', unitId: 'lpps-unit',\n      lightbox: 'https:\/\/paragliding-in-madeira.com\/weather\/lpps\/'\n    }\n  ];\n\n\/\/ Delayed windguru load logic\nlet windguruInitialized = false;\n\n  function injectWindguru() {\n    if (windguruInitialized) return;\n    console.log(\"Master Switch: Loading Windguru Widgets...\");\n    \n    stations.forEach(s => {\n      if (s.isWindguru && s.wgSpot) {\n        const container = document.querySelector(`.pos-${s.id} .wg-proxy-layer`);\n        if (container) {\n          container.innerHTML = ''; \/\/ Clear any old scripts\n          const script = document.createElement('script');\n          const uid = `wglive_${s.wgSpot}_${Math.floor(Math.random()*1000)}`;\n          script.id = uid;\n          \n          \/\/ Using the specific Windguru URL pattern\n          const params = [\n            `spot=${s.wgSpot}`,\n            `uid=${uid}`,\n            \"color=light\", \"wj=kmh\", \"tj=c\", \"avg_min=0\",\n            \"gsize=200\", \"msize=250\", \"m=3\", \"arrow=y\", \"show=n,g,c,f,m\"\n          ];\n          \n          script.src = \"https:\/\/www.windguru.cz\/js\/wglive.php?\" + params.join(\"&\");\n          container.appendChild(script);\n        }\n      }\n    });\n    windguruInitialized = true;\n  }\n\n function showWidget(s) {\n    [s.speedId, s.unitId, s.arrowId].forEach(id => { \n      const el = document.getElementById(id);\n      if(el) el.style.display = 'block'; \n    });\n  }\n\n  function hideWidget(s) {\n    const el = document.getElementById(s.speedId); if(el){ el.innerHTML = '--'; el.style.display = 'none'; }\n    [s.unitId, s.arrowId, s.vrbId].forEach(id => { if(document.getElementById(id)) document.getElementById(id).style.display = 'none'; });\n  }\n\n  \/\/ --- HELPER: CHECK DIRECTION SECTOR ---\n  function isFlyableDirection(deg, sector) {\n    if (deg === null || !sector) return false;\n    if (sector.start < sector.end) {\n      \/\/ Standard case (e.g., 210 to 280)\n      return deg >= sector.start && deg <= sector.end;\n    } else {\n      \/\/ Crossing North sector (e.g., 330 to 40)\n      return deg >= sector.start || deg <= sector.end;\n    }\n  }\n\n  async function updateStationWidget(s) {\nconsole.log(\"Checking station:\", s.id); \/\/ This is our single live log line\n    try {\n      const res = await fetch(s.jsonUrl + '?t=' + Date.now());\n      const data = await res.json();\n      if (!data || data.length === 0) { hideWidget(s); return; }\n      \n      const latest = data[data.length - 1];\n      if (Date.now() - new Date(latest.time).getTime() > STALE_MS) { hideWidget(s); return; }\n      \n      showWidget(s); \n      let speed, gust, dir;\n      if (s.isWindguru) {\n        speed = Math.round(latest.windSpeed.kph); gust = Math.round(latest.windGust.kph); dir = latest.winddir;\n      } else {\n        speed = latest.speed === null ? null : Math.round(latest.speed);\n        gust = latest.gust === null ? null : Math.round(latest.gust);\n        dir = typeof latest.direction === 'number' ? latest.direction : null;\n      }\n\n      const speedEl = document.getElementById(s.speedId);\n      const overlayEl = s.overlayId ? document.getElementById(s.overlayId) : null;\n\n          \n \/\/ --- GUST DISPLAY LOGIC (Nested to avoid wordpress cleaning the &&) ---\n        if (speedEl) {\n\n\/\/ Clear the display first to prevent old data showing\n  speedEl.innerHTML = '';\n  const arrowEl = document.getElementById(s.arrowId);\n  const unitEl = document.getElementById(s.unitId);\n\nif (currentMapMode === 'wind') {\n  let content = String(speed);\n  if (gust !== null && gust > speed) {\n    content = `${speed} \/<span class=\"gust-value\">${gust}<\/span>`;\n  }\n  speedEl.innerHTML = content;\n} \nelse if (currentMapMode === 'temp') {\n  const hum = latest.humidity ? Math.round(latest.humidity) : '--';\n\/\/ Wrapped temp in div to nudge it up and keep separate from the absolute label\n  speedEl.innerHTML = `<div  class=\"live-value\">${latest.temp.c.toFixed(1)}\u00b0<\/div><span class=\"temp-hum\">${hum}%<\/span>`;\n\n} \nelse if (currentMapMode === 'rain') {\n\/\/ 1. LIVE RAIN: Pulled from the raw daily JSON already in memory ('latest')\n  const liveR = (latest.precipRate && latest.precipRate.mm) ? latest.precipRate.mm : 0;\n  \n\/\/ 2. TOTAL RAIN: get summary file only for our station (excluding LPPS and LPMA)\n  let totalR = 0;\n  if (s.id !== 'lpps' && s.id !== 'lpma') { \/\/ Added LPMA\n    const sRes = await fetch(s.jsonUrl.replace('.json', '.summary.json'));\n    if (sRes.ok) { \n      const sData = await sRes.json(); \n      totalR = (sData.stats && sData.stats.rain_total) ? sData.stats.rain_total : 0; \n    }\n  }\n\n\/\/ 3. DISPLAY: Live rate on top (blue), Total accumulation on bottom (grey)\n  speedEl.innerHTML = `<div class=\"live-rain-val\">${liveR.toFixed(1)}<\/div><span class=\"rain-total\">${totalR.toFixed(1)}<br><span class=\"unit-mm\">mm<\/span><\/span>`;\n}\n\n \/\/ --- COLOR & LOGIC ---\n        if (overlayEl) {\n          let color = \"transparent\"; \/\/ Default\n\n          \/\/ 1. Determine Speed Color (Based on Station Limits)\n          if (speed > 0) {\n            if (s.limits) {\n              if (speed <= s.limits.low) color = \"#add8e6\";       \/\/ Light Blue\n              else if (speed <= s.limits.med) color = \"#90ee90\";  \/\/ Green\n              else if (speed <= s.limits.high) color = \"#ffff96\"; \/\/ Yellow\n              else color = \"#ffa500\";                             \/\/ Orange\n            }\n          }\n\n    \/\/ 2. Gust Check (Overrides speed color if gust is high) (Nested to avoid &#038;&#038;)\n          if (gust !== null) {\n            if (s.gustMax) {\n              if (gust > s.gustMax) {\n                color = \"#ffa500\"; \/\/ Makes it Orange on high Gust\n              }\n            }\n          }\n\n          \/\/ 3. Night Mode Check (Overrides everything)\n          \/\/ We call the synchronous helper here. Fast and safe\n          if (isNightTime()) {\n            color = \"#808080\"; \/\/ Grey\n          }\n\n          \/\/ 4. Direction Check  (Overrides everything) (Nested to avoid &&)\n          if (dir !== null) {\n            if (s.sector) {\n              if (!isFlyableDirection(dir, s.sector)) {\n                color = \"#808080\"; \n              }\n            }\n          }\n\n          overlayEl.style.backgroundColor = color;\n        }\n\n\/\/ --- AUTO-SCALE FONT 3 digit wind and gust ---\nconst charCount = speedEl.textContent.length;\n\nif (window.innerWidth <= 600) {\n  if (charCount >= 8) {\n\/\/ Triple digit (ex. 104 \/ 117) - fit 40px circle\n    speedEl.style.setProperty('font-size', '9.9px', 'important');\n    speedEl.style.setProperty('letter-spacing', '-0.3px', 'important');\n  } else {\n\/\/ RESET for 1 or 2 digits on mobile to 11px\n    speedEl.style.setProperty('font-size', '11px', 'important');\n    speedEl.style.setProperty('letter-spacing', '-0.1px', 'important');\n  }\n} else {\n  \/\/ Desktop handling: Let the CSS take over naturally\n  speedEl.style.fontSize = \"\";\n  speedEl.style.letterSpacing = \"\";\n}\n}\n\n      const arrowEl = document.getElementById(s.arrowId);\n      const vrbEl = document.getElementById(s.vrbId);\n      \n      if (vrbEl && dir === 1) { \n        if(arrowEl) arrowEl.style.display = 'none'; \n        vrbEl.style.display = 'block'; \n      } else {\n        if(vrbEl) vrbEl.style.display = 'none';\n        if(arrowEl) {\n          arrowEl.style.transform = `rotate(${(dir + 180) % 360}deg)`;\n          arrowEl.style.display = (dir === null || (dir === 0 && speed === 0)) ? 'none' : 'block';\n        }\n      }\n    } catch (e) { hideWidget(s); }\n  }\n\n  let refreshCount = 0;\n\n  function refreshAll() { \n    \/\/ Update data for all stations\n\/\/ We just update the stations, the Solunar data updates in the background\n    stations.forEach(updateStationWidget); \n    \n    \/\/ MASTER SWITCH: If this is the second update (the 1-min mark), load Windguru\n    refreshCount++;\n    if (refreshCount === 2) {\n      injectWindguru();\n    }\n  }\n\n  \/\/ GLOBAL BRIDGE\n  window.refreshAll = refreshAll;\n\n \/\/ --- STARTUP SEQUENCE ---\n\/\/ Get Solunar Data (Async, load & keep cached)\n  fetchSolunarData();\n  \n\/\/ Phase 1: Load Map & Airport Stations immediately\n  refreshAll(); \n\n\/\/ Phase 2: Set 1-minute interval that will eventually trigger Phase 3 (Windguru)\n  setInterval(refreshAll, REFRESH_MS);\n  \n\/\/ Refresh Solunar Data every hour\n setInterval(fetchSolunarData, 60 * 60 * 1000);\n\n  \/\/ LIGHTBOX\n  if (!document.getElementById('airport-lightbox')) {\n    document.body.insertAdjacentHTML('beforeend', `<div id=\"airport-lightbox\" style=\"display:none;position:fixed;top:0;left:0;width:100vw;height:100vh;background:rgba(0,0,0,0.7);z-index:9999;justify-content:center;align-items:center;\"><div style=\"position:relative;width:90%;max-width:1000px;height:75%;border-radius:8px;overflow:hidden;background:white;\"><iframe id=\"airport-lightbox-iframe\" src=\"\" style=\"width:100%;height:100%;border:none;\"><\/iframe><button id=\"airport-lightbox-close\" style=\"position:absolute;top:7px;right:11px;background:rgba(0,0,0,0.2);border:none;color:white;font-size:24px;cursor:pointer;border-radius:50%;width:35px;height:35px;\">\u2715<\/button><\/div><\/div>`);\n  }\n  const lb = document.getElementById('airport-lightbox'), ifr = document.getElementById('airport-lightbox-iframe');\n\n  document.querySelectorAll('.weather-pin[data-station]').forEach(pin => {\n    pin.addEventListener('click', () => {\n      const s = stations.find(x => x.id === pin.getAttribute('data-station'));\n      if (s.isWindguru) {\n        const trig = pin.querySelector('.wg-proxy-layer div div');\n        if (trig) trig.click();\n      } else {\n        ifr.src = s.lightbox + '?nocache=' + Date.now(); lb.style.display = 'flex';\n      }\n    });\n  });\n\n  document.getElementById('airport-lightbox-close').addEventListener('click', () => { ifr.src = ''; lb.style.display = 'none'; });\n  lb.addEventListener('click', (e) => { if (e.target === lb) { ifr.src=''; lb.style.display='none'; } });\n});\n<\/script>\n<\/details>\n\n\n\n<!-- added all aviation tracker below\n<div align=\"center\" >\n<iframe loading=\"lazy\" style=\"margin-left:-30px;\" name=\"Live Flights tracking\" width=\"345px\" height=\"609px\" src=\"https:\/\/www.xcontest.org\/api.s\/widget\/live-map\/all\/\" scrolling=\"no\" marginwidth=\"0\" marginheight=\"0\" style=\"border:none;\" allow=\"geolocation\" \nremember=\"true\"\nallowfullscreen>\n<\/iframe>\n<\/div>\n-->\n\n\n\n<!-- tests all for all below\n<div align=\"center\">\n  <div style=\"margin-bottom: 10px;\">\n    <button onclick=\"document.getElementById('map-frame').src='https:\/\/puretrack.io\/?l=32.74344,-16.75651&z=7.5'\" style=\"padding: 8px 12px; cursor: pointer;\">All aviation tracker<\/button>\n    <button onclick=\"document.getElementById('map-frame').src='https:\/\/www.xcontest.org\/api.s\/widget\/live-map\/all\/'\" style=\"padding: 8px 12px; cursor: pointer;\">XC Track<\/button>\n  <\/div>\n\n  <iframe loading=\"lazy\" style=\"margin-left:-30px;\"\n    id=\"map-frame\"\n  name=\"Live Flights tracking\" \n    width=\"345px\" \n    height=\"609px\" \n    src=\"https:\/\/puretrack.io\/?l=32.74344,-16.75651&z=7.5\" \n    style=\"border:none;\"\n    scrolling=\"no\" \n    allow=\"geolocation\" \n    remember=\"true\"\n    allowfullscreen>\n  <\/iframe>\n<\/div>\n-->\n\n\n\n<div align=\"center\">\n  <div id=\"radar-controls\" style=\"margin-bottom: 10px; margin-left:-30px;\">\n    <button id=\"btn-pure\" \n            onclick=\"updateMap('https:\/\/puretrack.io\/?l=32.74344,-16.75651&#038;z=7.5', this)\" \n style=\"padding: 8px 12px; cursor: pointer; font-weight: bold; background-color: #e6f7ff; border: 2px solid #1890ff;\">\n    PureTrack All Aviation\n    <\/button>\n\n    <button id=\"btn-safe\"           onclick=\"updateMap('https:\/\/live.safesky.app\/map?lat=32.75058&#038;lng=-16.74798&#038;zoom=7.77', this)\" \n  style=\"padding: 8px 12px; cursor: pointer; background-color: #f0f0f0; border: 1px solid #ccc;\">\n         SafeSky\n    <\/button>\n    \n    <button id=\"btn-xc\"            onclick=\"updateMap('https:\/\/www.xcontest.org\/api.s\/widget\/live-map\/all\/', this)\" \n  style=\"padding: 8px 12px; cursor: pointer; background-color: #f0f0f0; border: 1px solid #ccc;\">\n       XC Track\n    <\/button>\n  <\/div>\n\n  <iframe loading=\"lazy\" \n    id=\"map-frame\"\n    name=\"Live Flights tracking\" \n    width=\"345px\" \n    height=\"609px\" \n    src=\"https:\/\/puretrack.io\/?l=32.74344,-16.75651&#038;z=7.5\" \n    style=\"border:none; margin-left:-30px;\"\n    scrolling=\"no\" \n    allow=\"geolocation\" \n    allowfullscreen>\n  <\/iframe>\n\n  <script>\n\/\/ 1 Update the iframe source\nfunction updateMap(url, btn) {\n      document.getElementById('map-frame').src = url;\n\n\/\/ 2 Reset all buttons in control div to default style\n      const buttons = document.querySelectorAll('#radar-controls button');\n      buttons.forEach(b => {\n        b.style.fontWeight = \"normal\";\n        b.style.backgroundColor = \"#f0f0f0\";\n        b.style.border = \"1px solid #ccc\";\n      });\n\n\/\/ 3 Apply \"Active\" style to the clicked button\n      btn.style.fontWeight = \"bold\";\n      btn.style.backgroundColor = \"#e6f7ff\";\n      btn.style.border = \"2px solid #1890ff\";\n    }\n  <\/script>\n<\/div>\n\n\n\n<div align=\"center\" style=\"margin-top:-5px; transform: translateX(-18px);\"><sup><em><small><i><a style=\"text-decoration:none\" href=\"https:\/\/www.xcontest.org\/api.s\/widget\/live-map\/all\/\" target=\"_blank\" rel=\"noopener\">Share your flight live and follow other pilots<\/a><\/i><\/small><\/em><\/sup><\/div>\n\n\n\n<div style=\"height:27px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Unified Madeira sky experienceMerging Live Paragliding flights with general AviationTo co-create a culture of mutual respect and safety for everyoneBridging the aviation gap one flight at a time PureTrack All Aviation SafeSky XC Track Share your flight live and follow other pilots<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"slim_seo":{"title":"Paragliding Flights live tracking - Madeira Island","description":"Unified Madeira sky experience. To co-create a culture of mutual respect and safety for everyone. Share your flight live and follow other pilots"},"footnotes":""},"class_list":["post-5241","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages\/5241","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/comments?post=5241"}],"version-history":[{"count":128,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages\/5241\/revisions"}],"predecessor-version":[{"id":10697,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages\/5241\/revisions\/10697"}],"wp:attachment":[{"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/media?parent=5241"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}