{"id":6847,"date":"2025-12-05T00:17:27","date_gmt":"2025-12-05T00:17:27","guid":{"rendered":"https:\/\/paragliding-in-madeira.com\/weather\/?page_id=6847"},"modified":"2026-06-13T20:30:47","modified_gmt":"2026-06-13T19:30:47","slug":"golden-stream","status":"publish","type":"page","link":"https:\/\/paragliding-in-madeira.com\/weather\/golden-stream\/","title":{"rendered":"Golden stream"},"content":{"rendered":"\n<figure class=\"wp-block-image alignfull size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"577\" src=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2026\/02\/Golden-Hour-livestream-banner-1024x577.jpg\" alt=\"Golden Hour livestream banner\" class=\"wp-image-9632\" srcset=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2026\/02\/Golden-Hour-livestream-banner-1024x577.jpg 1024w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2026\/02\/Golden-Hour-livestream-banner-300x169.jpg 300w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2026\/02\/Golden-Hour-livestream-banner-768x432.jpg 768w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2026\/02\/Golden-Hour-livestream-banner-1536x865.jpg 1536w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2026\/02\/Golden-Hour-livestream-banner.jpg 2000w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/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\/contribute\/\">Contribute<\/a><\/div>\n<\/div>\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\/rain 14-03-2026 and uv 25-05-26 style below\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, RAIN, and UV 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-uv\" onclick=\"setMapView('uv')\">UV<\/button>\n  <button class=\"switcher-btn\" id=\"btn-rain\" onclick=\"setMapView('rain')\">RAIN<\/button>\n  \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\">--<\/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\">--<\/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\">--<\/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\">--<\/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\">--<\/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\">--<\/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}\nelse if (currentMapMode === 'uv') {\n\/\/ 1. Calculate 10-minute maximum UV\n  const latestTime = new Date(latest.time).getTime();\n  const tenMinsMs = 10 * 60 * 1000;\n  \n  let maxUv = 0;\n\/\/ Loop backwards through data to find the highest UV in the last 10 minutes\n  for (let i = data.length - 1; i >= 0; i--) {\n    const entryTime = new Date(data[i].time).getTime();\n    if (latestTime - entryTime > tenMinsMs) {\n      break; \n    }\n    const currentUv = data[i].uv !== undefined ? data[i].uv : 0;\n    if (currentUv > maxUv) {\n      maxUv = currentUv;\n    }\n  }\n  \n\/\/ 2. DISPLAY: Max UV on top, \"UV\" label on bottom\n  speedEl.innerHTML = `<div class=\"live-value\">${maxUv.toFixed(1)}<\/div><span class=\"temp-hum\">UV<\/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 &&)\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-->\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\/rain 14-03-2026 and uv 25-05-26  -->\n<!-- fine tuned below \n<style>\n\/* Gentle fadein for the map *\/\n.madeira-weather-map {\n  opacity: 0;\n  transition: opacity 0.8s ease-in-out;\n}\n\n.madeira-weather-map.visible {\n  opacity: 1;\n}\n\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  opacity: 0;\n  transition: opacity 0.2s ease;\n  gap: 10px;\n  justify-content: center;\n  margin-bottom: 15px;\n}\n\n.map-view-switcher.ready {\n  opacity: 1;\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, RAIN, and UV 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\/* Ensure switcher transparent until JS initializes state *\/\n.map-view-switcher {\n  opacity: 0;\n  transition: opacity 0.3s ease;\n}\n.map-view-switcher.ready {\n  opacity: 1;\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-uv\" onclick=\"setMapView('uv')\">UV<\/button>\n\n  <button class=\"switcher-btn\" id=\"btn-rain\" onclick=\"setMapView('rain')\">RAIN<\/button>\n  \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\">--<\/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\">--<\/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\">--<\/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\">--<\/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\">--<\/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\">--<\/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  \/\/ If trying to set to UV at night, default back to Wind\n  if (mode === 'uv' && isNightTime()) {\n    mode = 'wind';\n  }\n  \n  currentMapMode = mode;\n  document.getElementById('lazy-weather-map').setAttribute('data-view', mode);\n  \n  \/\/ Update Button UI\n  document.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  \/\/ Trigger Re-render\n  if (typeof window.refreshAll === \"function\") {\n    window.refreshAll();\n  }\n};\n\n function setInitialUIState() {\n  const uvBtn = document.getElementById('btn-uv');\n  if (!uvBtn) return;\n\n  if (isNightTime()) {\n    uvBtn.style.display = 'none';\n  } else {\n    uvBtn.style.display = 'inline-block';\n  }\n\n  document.querySelector('.map-view-switcher').classList.add('ready');\n} \n\ndocument.addEventListener(\"DOMContentLoaded\", function() {\n  const STALE_MS = 60 * 60 * 1000;\n  const REFRESH_MS = 1 * 60 * 1000;\n\n\/\/ Centralized visibility controller for the uv button\nfunction updateUiVisibility() {\n  const uvBtn = document.getElementById('btn-uv');\n  if (!uvBtn) return;\n  \n  const isNight = isNightTime();\n  uvBtn.style.display = isNight ? 'none' : 'inline-block';\n  \n  \/\/ Return to wind mode if user was on UV and it became night\n  if (isNight && currentMapMode === 'uv') {\n    setMapView('wind');\n  }\n  \n  document.querySelector('.map-view-switcher').classList.add('ready');\n}\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\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}\nelse if (currentMapMode === 'uv') {\n\/\/ 1. Calculate 10-minute maximum UV\n  const latestTime = new Date(latest.time).getTime();\n  const tenMinsMs = 10 * 60 * 1000;\n  \n  let maxUv = 0;\n\/\/ Loop backwards through data to find the highest UV in the last 10 minutes\n  for (let i = data.length - 1; i >= 0; i--) {\n    const entryTime = new Date(data[i].time).getTime();\n    if (latestTime - entryTime > tenMinsMs) {\n      break; \n    }\n    const currentUv = data[i].uv !== undefined ? data[i].uv : 0;\n    if (currentUv > maxUv) {\n      maxUv = currentUv;\n    }\n  }\n  \n\/\/ 2. DISPLAY: Max UV on top, \"UV\" label on bottom\n  speedEl.innerHTML = `<div class=\"live-value\">${maxUv.toFixed(1)}<\/div><span class=\"temp-hum\">UV<\/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 &&)\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\/\/ Ensure button visibility is correct before they are visible\n \n  fetchSolunarData().finally(() => {\n    refreshAll();\n    updateUiVisibility();\n    \n\/\/ Reveal the map after the initial data pull\n    const mapEl = document.getElementById('lazy-weather-map');\n    if (mapEl) {\n      setTimeout(() => {\n        mapEl.classList.add('visible');\n      }, 000); \/\/ no delay to ensure map and buttons load at the same time\n    }\n  });\n \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-->\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\/rain 14-03-2026 and uv 25-05-26  -->\n<style>\n\/* --- MASTER FADE-IN WRAPPER --- *\/\n  .weather-widget-wrapper {\n    opacity: 0;\n    transition: opacity 0.8s ease-in-out;\n  }\n  .weather-widget-wrapper.revealed {\n    opacity: 1;\n  }\n\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, RAIN, and UV 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\/* --- PRESSURE COCKPIT --- *\/\n  .pressure-cockpit {\n    position: absolute;\n    bottom: 3px;\n    right: 1px;\n background: rgba(255, 255, 255, 0.22);\n    padding: 2px 4px;\n    border-radius: 8px;\n    font-family: Arial, sans-serif;\n    z-index: 12;\n    box-shadow: 0 2px 1px rgba(0,0,0,0.09);\n    display: flex;\n    align-items: baseline;\n    gap: 1px;\n    pointer-events: auto; \/* Allows clicks to pass through to the map if needed *\/\n  }\n  .pressure-label {\n    font-size: 5px;\n    font-weight: 800;\n    color: #185e96;\n    letter-spacing: 0.5px;\n  }\n  .pressure-value {\n    font-size: 10px;\n    font-weight: bold;\n    color: #333;\n  }\n\n<\/style>\n\n<div class=\"weather-widget-wrapper\" id=\"weather-master-wrapper\">\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-uv\" onclick=\"setMapView('uv')\">UV<\/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<!-- Map Pressure Cockpit -->\n    <div class=\"pressure-cockpit\" title=\"Click for info\" onclick=\"togglePressureText()\" style=\"cursor: pointer;\">\n  <span class=\"pressure-value\" id=\"cockpit-pressure\">&#8212; hPa<\/span>\n<\/div>\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\"><\/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\"><\/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\"><\/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<\/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\/\/ Pressure toggle message\nlet isShowingAvgInfo = false;\n\nwindow.togglePressureText = function() {\n  const cockpitEl = document.getElementById('cockpit-pressure');\n  isShowingAvgInfo = !isShowingAvgInfo;\n\n  if (isShowingAvgInfo) {\n    \/\/ Show the descriptive text\n    cockpitEl.innerText = \"Avg pressure\";\n  } else {\n    \/\/ Show the real value\n    updateIslandPressure();\n  }\n};\n\n\/\/ Global state - default wind\nlet currentMapMode = 'wind';\n\n\/\/ Global state for Island Average Pressure\nlet activePressures = {};\n\nfunction updateIslandPressure() {\n  const pressureValues = Object.values(activePressures);\n  const cockpitEl = document.getElementById('cockpit-pressure');\n  if (!cockpitEl) return;\n  \n  if (pressureValues.length === 0) {\n    cockpitEl.innerHTML = '-- hPa';\n    return;\n  }\n  \n\/\/ Calculate the average of all active stations\n  const sum = pressureValues.reduce((a, b) => a + b, 0);\n  const avg = Math.round(sum \/ pressureValues.length);\n  cockpitEl.innerHTML = avg + ' hPa';\n}\n\n\/\/ 1. GLOBAL SWITCHER FUNCTION\nwindow.setMapView = function(mode) {\n  currentMapMode = mode;\n  document.getElementById('lazy-weather-map').setAttribute('data-view', mode);\n\nisShowingAvgInfo = false; \n  updateIslandPressure();\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\n  document.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\n\/\/ const STALE_MS = 75 * 60 * 1000; old way of hiding stopped data\n\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\/\/ staleLimitMs: Dynamic timeout to hide station if data stops uploading\n\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      staleLimitMs: 10 * 60 * 1000 \/\/ 10 minutes window for 1-minute stations\n\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      staleLimitMs: 10 * 60 * 1000 \/\/ 10 minutes window for 1-minute stations\n\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      staleLimitMs: 10 * 60 * 1000 \/\/ 10 minutes window for 1-minute stations\n\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\nstaleLimitMs: 10 * 60 * 1000 \/\/ 10 minutes window for 1-minute stations\n\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\nstaleLimitMs: 75 * 60 * 1000 \/\/ 75 minutes window for 30-minute METARs 2 full updates\n\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      staleLimitMs: 75 * 60 * 1000 \/\/ 75 minutes window for 30-minute METARs 2 full updates\n\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) {\n    console.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() > s.staleLimitMs) { \n        hideWidget(s); \n        delete activePressures[s.id]; \/\/ Remove from average if stale\n        updateIslandPressure();\n        return; \n      }\n      \n\/\/ - PRESSURE MASTER COCKPIT -\n      if (latest.pressure && latest.pressure.hPa) {\n        activePressures[s.id] = latest.pressure.hPa;\n      } else {\n        delete activePressures[s.id]; \/\/ Protect against null values\n      }\n      updateIslandPressure();\n\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}\nelse if (currentMapMode === 'uv') {\n  \/\/ If it does not have a value hide it, if it's night time, UV is naturally zero\n  if (latest.uv == null) {\n    speedEl.innerHTML = '';\n  } else if (isNightTime()) {\n\n    speedEl.innerHTML = `<div class=\"live-value\">0.0<\/div><span class=\"temp-hum\" style=\"font-size: 10px; font-weight: normal; opacity: 0.6; text-transform: lowercase;\">night<\/span>`;\n  } else {\n    \/\/ 1. Calculate 10-minute maximum UV during daytime\n    const latestTime = new Date(latest.time).getTime();\n    const tenMinsMs = 10 * 60 * 1000;\n    \n    let maxUv = 0;\n    for (let i = data.length - 1; i >= 0; i--) {\n      const entryTime = new Date(data[i].time).getTime();\n      if (latestTime - entryTime > tenMinsMs) {\n        break; \n      }\n      const currentUv = data[i].uv !== undefined ? data[i].uv : 0;\n      if (currentUv > maxUv) {\n        maxUv = currentUv;\n      }\n    }\n    \n    \/\/ 2. DISPLAY: Max UV on top, \"UV\" label on bottom\n    speedEl.innerHTML = `<div class=\"live-value\">${maxUv.toFixed(1)}<\/div><span class=\"temp-hum\">UV<\/span>`;\n  }\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  \/\/ Reveal everything seamlessly once initial positioning\/rendering calculations settle\n  setTimeout(() => {\n    const wrapper = document.getElementById('weather-master-wrapper');\n    if (wrapper) wrapper.classList.add('revealed');\n  }, 000);\n\/\/ Change 000 to 50 for delay start\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\n\n\n<div style=\"height:21px\" 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<figure data-wp-context=\"{&quot;imageId&quot;:&quot;6a30fe67c0144&quot;}\" data-wp-interactive=\"core\/image\" data-wp-key=\"6a30fe67c0144\" class=\"wp-block-image aligncenter is-resized wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"1024\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/11\/wind-rose-1024x1024.png\" alt=\"\" class=\"wp-image-1702\" style=\"width:111px\" srcset=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/11\/wind-rose-1024x1024.png 1024w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/11\/wind-rose-300x300.png 300w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/11\/wind-rose-150x150.png 150w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/11\/wind-rose-768x768.png 768w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/11\/wind-rose.png 1111w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\taria-label=\"Enlarge\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"state.imageButtonRight\"\n\t\t\tdata-wp-style--top=\"state.imageButtonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<p class=\"has-text-align-center\">Airport &#8211; Ros\u00e1rio <a style=\"text-decoration:none\" href=\"#autonomy\" title=\"\">weather<\/a> station<\/p>\n<\/div>\n\n\n\n<!-- Real Time Widget -->\n<style>\n  \/* --- WIDGET CONTAINER --- *\/\n  .sc-weather-box {\n    font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n    background: rgba(0,0,0,0.03);\n    border: 1px solid #eaeaea;\n    border-radius: 8px;\n    padding: 15px 20px;\n    max-width: 400px;\n    margin: 20px auto;\n    color: #333;\n    box-shadow: 0 2px 10px rgba(0,0,0,0.03);\n  }\n\n  \/* --- HEADER \/ TIMESTAMP --- *\/\n  .sc-header {\n    display: grid; \n    grid-template-columns: 2fr auto 1fr; \n    align-items: baseline;\n    padding: 0 10px 8px 10px;\n    margin-bottom: 12px;\n    border-bottom: 1px solid #f0f0f0;\n  }\n  .sc-timestamp { font-size: 13px; color: #333; font-weight: 600; grid-column: 2; text-align: center; white-space: nowrap; }\n  .sc-station-name { font-size: 14px; font-weight: 600; grid-column: 1; text-align: center; color: #555; }\n\n  \/* --- MAIN CONTENT LAYOUT --- *\/\n  .sc-content { display: flex; align-items: center; gap: 15px; }\n\n  \/* --- DIRECTION --- *\/\n  .sc-direction-group { display: flex; flex-direction: column; align-items: center; margin: -3px; min-width: 65px; }\n  .sc-arrow-svg { width: 32px; height: 32px; fill: #185e96; transition: transform 1s ease-out; }\n  .sc-deg-text { font-size: 13px; font-weight: bold; margin-top: 4px; color: #185e96; }\n  .sc-cardinal { font-size: 12px; color: #333; letter-spacing: 1px; }\n\n  \/* --- SPEED --- *\/\n  .sc-speed-group { display: flex; flex-direction: column; margin-left: -10px; min-width: 110px; }\n  .sc-speed-value { font-size: 32px; font-weight: 700; line-height: 1; margin-top: 2px; margin-bottom: -3px; color: #333; }\n  .sc-gust-label { font-size: 12px; color: #333; margin-top: 5px; }\n  .sc-gust-value { color: orange; font-weight: 600; font-size: 20px; }\n  .sc-unit { font-size: 12px; font-weight: normal; color: #333; margin-left: 4px; }\n\n  \/* --- TEMP & HUMIDITY --- *\/\n  .sc-env-group { display: flex; flex-direction: column; gap: 2px; border-left: 2px solid #ddd; padding-left: 8px; margin-left: 5px; }\n  .sc-env-item { display: flex; align-items: baseline; gap: 5px; }\n  .sc-env-label { font-size: 11px; color: #888; text-transform: uppercase; font-weight: 600; width: 35px; }\n  .sc-env-value { font-size: 18px; font-weight: 600; color: #333; }\n  .sc-env-unit { font-size: 11px; color: #666; }\n\n  \/* --- BOTTOM ROW (The 3-Column Breathe Layout) --- *\/\n  .sc-bottom-row {\n    display: grid;\n    grid-template-columns: repeat(3, 1fr);\n    margin-top: 15px;\n    padding-top: 10px;\n    border-top: 1px solid #f0f0f0;\n    text-align: center;\n  }\n  .sc-bottom-item { display: flex; flex-direction: column; align-items: center; transition: opacity 0.2s; }\n  .sc-bottom-item:active { opacity: 0.6; } \/* Visual feedback for touch *\/\n  .sc-bottom-label { font-size: 8px; color: #999; text-transform: uppercase; font-weight: 800; margin-bottom: 2px; }\n  .sc-bottom-value { font-size: 14px; font-weight: 700; color: #444; }\n  .sc-bottom-unit { font-size: 9px; color: #888; margin-left: 1px; }\n\n  \/* --- DESKTOP --- *\/\n  @media (min-width: 768px) {\n    .sc-content { justify-content: center; gap: 20px; }\n    .sc-weather-box { max-width: 500px; padding: 20px 30px; }\n    .sc-env-group { padding-left: 70px; margin-left: 15px; }\n    .sc-bottom-row { gap: 30px; }\n  }\n<\/style>\n\n<div class=\"sc-weather-box\">\n  <div class=\"sc-header\">\n    <span class=\"sc-station-name\">Real Time<\/span>\n    <span class=\"sc-timestamp\" id=\"sc-time\">&#8212;<\/span>\n  <\/div>\n  \n  <div class=\"sc-content\">\n    <div class=\"sc-direction-group\">\n      <svg class=\"sc-arrow-svg\" id=\"sc-arrow\" viewBox=\"0 0 24 24\"><path d=\"M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71z\"\/><\/svg>\n      <div class=\"sc-deg-text\" id=\"sc-deg\">&#8211;\u00b0<\/div>\n      <div class=\"sc-cardinal\" id=\"sc-card\">&#8212;<\/div>\n    <\/div>\n\n    <div class=\"sc-speed-group\">\n      <div class=\"sc-speed-value\"><span id=\"sc-speed\">&#8212;<\/span><span class=\"sc-unit\">km\/h<\/span><\/div>\n      <div class=\"sc-gust-label\">MAX &nbsp;<span class=\"sc-gust-value\" id=\"sc-gust\">&#8212;<\/span> <span class=\"sc-unit\">km\/h<\/span><\/div>\n    <\/div>\n\n    <div class=\"sc-env-group\">\n      <div class=\"sc-env-item\">\n        <span class=\"sc-env-label\">Temp<\/span>\n        <span class=\"sc-env-value\" id=\"sc-temp\">&#8212;<\/span><span class=\"sc-env-unit\">\u00b0C<\/span>\n      <\/div>\n      <div class=\"sc-env-item\">\n        <span class=\"sc-env-label\">Hum<\/span>\n        <span class=\"sc-env-value\" id=\"sc-hum\">&#8212;<\/span><span class=\"sc-env-unit\">%<\/span>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <div class=\"sc-bottom-row\">\n    <div class=\"sc-bottom-item\" id=\"sc-rain-toggle\" style=\"cursor:pointer;\" title=\"Click to toggle Intensity\/Daily\">\n      <span class=\"sc-bottom-label\" id=\"sc-rain-label\">Rain Live<\/span>\n      <div><span class=\"sc-bottom-value\" id=\"sc-rain-val\">&#8212;<\/span><span class=\"sc-bottom-unit\" id=\"sc-rain-unit\">mm\/h<\/span><\/div>\n    <\/div>\n\n    <div class=\"sc-bottom-item\" id=\"sc-solar-toggle\" style=\"cursor:pointer;\" title=\"Click to toggle UV\/Solar\">\n      <span class=\"sc-bottom-label\" id=\"sc-solar-label\">Solar<\/span>\n      <div><span class=\"sc-bottom-value\" id=\"sc-solar-val\">&#8212;<\/span><span class=\"sc-bottom-unit\" id=\"sc-solar-unit\">W\/m\u00b2<\/span><\/div>\n    <\/div>\n\n    <div class=\"sc-bottom-item\">\n      <span class=\"sc-bottom-label\">Pressure<\/span>\n      <div><span class=\"sc-bottom-value\" id=\"sc-pres\">&#8212;<\/span><span class=\"sc-bottom-unit\">hPa<\/span><\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script>\n(function() {\n  const baseDir = 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/santacruz\/';\n\n\/\/ NEW TIME FORMAT (Madeira based)\nconst madeiraDate = new Date().toLocaleString(\"en-CA\", {timeZone: \"Atlantic\/Madeira\", hour12: false}).split(',')[0];\nconst todayPrefix = madeiraDate;\n  const rawUrl = baseDir + todayPrefix + '.json';\n  const summaryUrl = baseDir + todayPrefix + '.summary.json';\n  \n  let showUV = false;\n  let showDailyRain = false;\n  let globalLatestData = null;\n  let globalSummaryStats = null;\n\n  function getCardinal(angle) {\n    const directions = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'];\n    return directions[Math.round(angle \/ 22.5) % 16];\n  }\n\n  function renderInteractive() {\n    if (!globalLatestData) return;\n    \n    \/\/ Solar\/UV Render\n    const sLabel = document.getElementById('sc-solar-label');\n    const sVal = document.getElementById('sc-solar-val');\n    const sUnit = document.getElementById('sc-solar-unit');\n    if (showUV) {\n      sLabel.innerText = \"UV Index\";\n      sVal.innerText = globalLatestData.uv !== undefined ? globalLatestData.uv.toFixed(1) : \"0.0\";\n      sUnit.innerText = \"\";\n    } else {\n      sLabel.innerText = \"Solar\";\n       sVal.innerText = globalLatestData.solarRadiation !== undefined ? Math.round(globalLatestData.solarRadiation) : \"--\";\n      sUnit.innerText = \"W\/m\u00b2\";\n    }\n\n  \/\/ Rain Render\n    const rLabel = document.getElementById('sc-rain-label');\n    const rVal = document.getElementById('sc-rain-val');\n    const rUnit = document.getElementById('sc-rain-unit');\n\n    if (showDailyRain) {\n      rLabel.innerText = \"Rain Daily\";\n\n      \/\/ THE TRUTH CHAIN: \n      \/\/ 1. Try our refined summary total first.\n      \/\/ 2. If that's missing (new day), fall back to station midnight total.\n      \/\/ 3. If both are missing, use 0.\n      const rainTotal = globalSummaryStats?.rain_total \n                        ?? globalLatestData?.precipSinceMidnight?.mm \n                        ?? 0;\n\n      rVal.innerText = rainTotal.toFixed(1);\n      rUnit.innerText = \"mm\";\n    } else {\n      rLabel.innerText = \"Rain Live\";\n      \n      \/\/ Safety for Live Rain Rate\n      const rainRate = globalLatestData?.precipRate?.mm ?? 0;\n      rVal.innerText = rainRate.toFixed(1);\n      rUnit.innerText = \"mm\/h\";\n    }\n}\n\n  \/\/ Event Listeners\n  document.getElementById('sc-solar-toggle').addEventListener('click', () => { showUV = !showUV; renderInteractive(); });\n  document.getElementById('sc-rain-toggle').addEventListener('click', () => { showDailyRain = !showDailyRain; renderInteractive(); });\n\n  async function updateWidget() {\n    try {\n      const cacheBust = '?t=' + Date.now();\n      \n      \/\/ Fetch Raw and Summary in parallel for efficiency\n      const [rawRes, summaryRes] = await Promise.all([\n        fetch(rawUrl + cacheBust),\n        fetch(summaryUrl + cacheBust).catch(() => null) \/\/ Fallback if summary doesn't exist yet\n      ]);\n\n      const data = await rawRes.json();\n      if (!data || data.length === 0) return;\n\n      if (summaryRes && summaryRes.ok) {\n        const summaryData = await summaryRes.json();\n        globalSummaryStats = summaryData.stats;\n      }\n      \n      const latest = data[data.length - 1];\n      globalLatestData = latest;\n      \n      \/\/ 1. Timestamp\n     if (latest.time) {\n          const dateObj = new Date(latest.time);\n          document.getElementById('sc-time').innerText = `${String(dateObj.getDate()).padStart(2, '0')}-${String(dateObj.getMonth() + 1).padStart(2, '0')}-${dateObj.getFullYear()} ${String(dateObj.getHours()).padStart(2, '0')}:${String(dateObj.getMinutes()).padStart(2, '0')}`;\n      }\n\n      \/\/ 2. Temperature & Humidity (Safety fallback)\n      const tempVal = latest.temp?.c;\n      document.getElementById('sc-temp').innerText = (tempVal !== undefined && tempVal !== null) ? tempVal.toFixed(1) : \"--\";\n      \n      const humVal = latest.humidity;\n      document.getElementById('sc-hum').innerText = (humVal !== undefined && humVal !== null) ? Math.round(humVal) : \"--\";\n      \n      \/\/ 3. Wind Data\n      document.getElementById('sc-speed').innerText = latest.windSpeed?.kph?.toFixed(1) ?? \"--\";\n      document.getElementById('sc-gust').innerText = latest.windGust?.kph?.toFixed(1) ?? \"--\";\n\n      \/\/ 4. Direction & Arrow\n      if (latest.winddir !== undefined) {\n        document.getElementById('sc-deg').innerText = latest.winddir + '\u00b0';\n        document.getElementById('sc-card').innerText = getCardinal(latest.winddir);\n        const arrow = document.getElementById('sc-arrow');\n        arrow.style.transform = `rotate(${(latest.winddir + 180) % 360}deg)`;\n      }\n\n      \/\/ 5. Pressure\n      document.getElementById('sc-pres').innerText = latest.pressure?.hPa ? Math.round(latest.pressure.hPa) : \"--\";\n\n      \/\/ 6. Rain\/Solar Toggles\n      renderInteractive();\n\n    } catch (e) { console.error(\"Widget error:\", e); }\n  }\n\n  updateWidget();\n  setInterval(updateWidget, 60000);\n})();\n<\/script>\n\n\n\n<!-- 2h wind widget -->\n<!-- added zoom below\n<style>\n  .sc-pro-cockpit {\n    font-family: 'Segoe UI', Roboto, Arial, sans-serif;\n    max-width: 500px;\n    margin: 20px auto;\n    background: rgba(0,0,0,0.02);\n    border: 1px solid #eee;\n    border-radius: 12px;\n    padding: 15px;\n    color: #333;\n  }\n  .sc-graph-label {\n    font-size: 11px;\n    font-weight: 700;\n    color: #888;\n\/* text-transform: uppercase; *\/\n    margin-bottom: 8px;\n    letter-spacing: 1px;\n    display: flex;\n    justify-content: space-between;\n  }\n  .sc-chart-wrapper { margin-bottom: 25px; position: relative; height: 180px; }\n<\/style>\n\n<div class=\"sc-pro-cockpit\">\n  <div class=\"sc-graph-label\"><span>2h Wind Speed & Gust evolution (km\/h)<\/span><\/div>\n  <div class=\"sc-chart-wrapper\">\n    <canvas id=\"sc-speed-chart\"><\/canvas>\n  <\/div>\n  <div class=\"sc-graph-label\"><span>2h Wind Direction<\/span><\/div>\n  <div class=\"sc-chart-wrapper\" style=\"height: 160px;\">\n    <canvas id=\"sc-dir-chart\"><\/canvas>\n  <\/div>\n<\/div>\n\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js\"><\/script>\n\n<script>\n(function() {\n  let speedChart, dirChart;\n\n  const baseDir = 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/santacruz\/';\n\n  const getCardinal = (angle) => {\n    const directions = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'];\n    return directions[Math.round(angle \/ 22.5) % 16];\n  };\n\n\/\/ Auto hide Tooltip function\n  function autoHideTooltip(chart, delay = 5000) {\n    let hideTimer;\n    const canvas = chart.canvas;\n\n    const clearActiveElements = () => {\n      if (chart) {\n\n\/\/ Removes highlight dots from the lines\/points\n        chart.setActiveElements([]);\n  \/\/ Hides the tooltip box\n        chart.tooltip.setActiveElements([], { x: 0, y: 0 });\n        chart.update();\n      }\n    };\n\n    \/\/ Listen for touch end or mouse out to start the timer\n    const startTimer = () => {\n      clearTimeout(hideTimer);\n      hideTimer = setTimeout(clearActiveElements, delay);\n    };\n\n    \/\/ Reset timer if the user interacts again before it hides\n    const resetTimer = () => {\n      clearTimeout(hideTimer);\n    };\n\n    canvas.addEventListener('touchend', startTimer);\n    canvas.addEventListener('touchstart', resetTimer); \n  }\n\n  async function updateLighthouse() {\n    try {\n      const now = Date.now();\n      const cacheBust = '?t=' + now; \/\/ Defines the cache cleaner variable\n      const twoHoursAgo = now - (120 * 60 * 1000);\n      \n  \/\/ 1. Fetch Solar Data (UPDATED: Uses local json instead of external API)\n      const solunarRes = await fetch('https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/moon-time\/solunar-data.json');\n      const solunarData = await solunarRes.json();\n\n\/\/ turn HH:mm into a real timestamp for today\n      const parseTime = (timeStr) => {\n        const [h, m] = timeStr.split(':');\n        const d = new Date();\n        d.setHours(h, m, 0, 0);\n        return d.getTime();\n      };\n\n      const sunrise = parseTime(solunarData.today.sunrise);\n      const sunset = parseTime(solunarData.today.sunset);\n\n      const timeFormatter = (val) => {\n        const d = new Date(val);\n        return `${d.getHours()}:${String(d.getMinutes()).padStart(2, '0')}`;\n      };\n\n           const getFileUrl = (date) => {\n    const madeiraDate = date.toLocaleString(\"en-CA\", {timeZone: \"Atlantic\/Madeira\", hour12: false}).split(',')[0];\n    return baseDir + `${madeiraDate}.json`;\n  };\n      \n\/\/ Get Today with Cache Busting\n      const resToday = await fetch(getFileUrl(new Date()) + cacheBust);\n      let combinedData = await resToday.json();\n\n\/\/ New check added\nif (!Array.isArray(combinedData)) {\n  console.warn(\"Data malformed. Holding previous state.\");\n  return; \n}\n\n\/\/ Handle Midnight Cross-over\n      if (combinedData.length === 0 || (combinedData.length > 0 && combinedData[0].time > twoHoursAgo)) {\n        try {\n          const yesterday = new Date();\n          yesterday.setDate(yesterday.getDate() - 1);\n          const resYesterday = await fetch(getFileUrl(yesterday) + cacheBust);\n          const yesterdayData = await resYesterday.json();\n          combinedData = [...yesterdayData, ...combinedData];\n        } catch(e) {}\n      }\n      \n      const windowData = combinedData.filter(d => d.time > (twoHoursAgo - 10000));\n\n  \/\/ 2. THE SOLAR PLUGIN (Defined once, used for both charts)\n      const solarBackgroundPlugin = {\n        id: 'solarBackground',\n        beforeDraw: (chart) => {\n          const { ctx, chartArea: { top, bottom, left, right }, scales: { x } } = chart;\n          function drawSection(startTime, endTime, color) {\n            const startPx = Math.max(left, x.getPixelForValue(startTime));\n            const endPx = Math.min(right, x.getPixelForValue(endTime));\n            if (startPx < endPx) {\n              ctx.save();\n              ctx.fillStyle = color;\n              ctx.fillRect(startPx, top, endPx - startPx, bottom - top);\n              ctx.restore();\n            }\n          }\n          ctx.save();\n          ctx.fillStyle = 'rgba(0, 0, 0, 0.06)'; \/\/ The \"Night\" tint\n          ctx.fillRect(left, top, right - left, bottom - top);\n          const yesterdaySunrise = sunrise - 86400000;\n          const yesterdaySunset = sunset - 86400000;\n          drawSection(yesterdaySunrise, yesterdaySunset, 'rgba(255, 255, 255, 0.5)'); \/\/ clears the Night tint for yesterday\ndrawSection(sunrise, sunset, 'rgba(255, 255, 255, 0.5)'); \/\/ clears the Night tint for today\n          ctx.restore();\n        }\n      };\n\n   \/\/ --- SPEED CHART ---\n      if (speedChart) speedChart.destroy();\n      speedChart = new Chart(document.getElementById('sc-speed-chart'), {\n        type: 'line',\n        data: {\n          datasets: [\n            { label: 'Gust',\ndata: windowData.reduce((acc, d, i, arr) => {\n\/\/ If gap is detected (> 10 mins), push null point to break the line\n  if (i > 0 && (d.time - arr[i-1].time) > 600000) {\n    acc.push({ x: arr[i-1].time + 1, y: null });\n  }\n\n\/\/ only plot valid numeric values, prevent \"zero-snapping\"\n  const val = d.windGust?.kph;\n  if (typeof val === 'number') {\n    acc.push({ x: d.time, y: val });\n  }\n\n  return acc;\n}, []),\n\/\/ gap detect no line connection when no data exists\n borderColor: 'rgba(255, 165, 0, 0.45)', backgroundColor: 'rgba(255, 165, 0, 0.05)', fill: true, pointRadius: 0, borderWidth: 1.5, tension: 0.3,\nspanGaps: false \/\/ Don't connect across null non existent values\n },\n            { label: 'Speed', data: windowData.reduce((acc, d, i, arr) => {\n  if (i > 0 && (d.time - arr[i-1].time) > 600000) {\n\n\/\/ only plot valid numeric values prevent \"zero-snapping\"\n    acc.push({ x: arr[i-1].time + 1, y: null });\n  }\n  const val = d.windSpeed?.kph;\n  if (typeof val === 'number') { acc.push({ x: d.time, y: val }); }\n  return acc;\n}, []), \/\/ gap detect no line connection when no data exists\nborderColor: 'rgba(24, 94, 150, 0.8)', backgroundColor: 'rgba(24, 94, 150, 0.1)', fill: true, pointRadius: 0, borderWidth: 2, tension: 0.3,\nspanGaps: false \/\/ Don't connect across null non existent values\n }\n          ]\n        },\n        options: {\n          responsive: true, maintainAspectRatio: false,\n          interaction: { mode: 'index', intersect: false },\n          plugins: { \n            legend: { display: false }, \n            tooltip: { \n              enabled: true,\n              callbacks: { \n                title: (items) => timeFormatter(items[0].parsed.x),\n                label: (ctx) => `${ctx.dataset.label}: ${ctx.parsed.y.toFixed(1)} km\/h` \n              } \n            } \n          },\n          scales: {\n            x: { \n              type: 'linear', min: twoHoursAgo, max: now,\n              ticks: { stepSize: 15 * 60 * 1000, callback: (val) => timeFormatter(val), font: { size: 10 }, maxRotation: 0, minRotation: 0, autoSkip: false },\n              grid: { display: true, color: 'rgba(0,0,0,0.08)' }\n            },\n            y: { \n              beginAtZero: true, \n              ticks: { font: { size: 10 } } \n            },\n  \n\/\/ --- MIRRORED RIGHT AXIS ---\n yRight: { \n  position: 'right', \n  beginAtZero: true,\ndisplay: true, \/\/ false to hide\ngrid: { display: false }, \nticks: { font: { size: 10 } },\n\n\/\/ function tells the right axis to copy left axis exactly\n afterDataLimits: (axis) => {\n  axis.max = axis.chart.scales.y.max;\n  axis.min = axis.chart.scales.y.min;\n              }\n            }\n          }\n        },\n\n plugins: [solarBackgroundPlugin] \/\/ Change speed bg color based on day\/night\n      });\n\n\/\/ --- RAW DIRECTION CHART ---\n\/\/ map d.winddir directly without any averaging\n      const rawDirData = windowData.map(d => ({ x: d.time, y: d.winddir }));\n\n      if (dirChart) dirChart.destroy();\n      dirChart = new Chart(document.getElementById('sc-dir-chart'), {\n type: 'scatter', \/\/ points for better visual interpretation\n        data: { \n          datasets: [{ \n            data: rawDirData, \n            \n            backgroundColor: 'rgba(24, 94, 150, 0.6)', \n            pointRadius: 2.5,\n            pointHoverRadius: 5\n          }] \n        },\n        options: {\n          responsive: true, maintainAspectRatio: false,\n          interaction: { mode: 'nearest', axis: 'x', intersect: false },\n          plugins: { \n            legend: { display: false },\n            tooltip: { \n              callbacks: { \n                title: (items) => timeFormatter(items[0].raw.x),\n                label: (ctx) => `Dir: ${Math.round(ctx.raw.y)}\u00b0 (${getCardinal(ctx.raw.y)})` \n              } \n            }\n          },\n          scales: {\n            x: { \n              type: 'linear', min: twoHoursAgo, max: now,\n              ticks: { stepSize: 15 * 60 * 1000, callback: (val) => timeFormatter(val), font: { size: 10 }, maxRotation: 0, minRotation: 0, autoSkip: false },\n              grid: { display: true, color: 'rgba(0,0,0,0.08)' } \n            },\n            y: { min: 0, max: 360, ticks: { stepSize: 90, font: { size: 10 }, callback: v => v + '\u00b0' }, grid: { color: '#bbb', lineWidth: 1 } },\n            yRight: { position: 'right', min: 0, max: 360, grid: { display: false }, ticks: { stepSize: 90, font: { weight: 'bold', size: 11 }, callback: v => ({ 0: 'N', 90: 'E', 180: 'S', 270: 'W', 360: 'N' }[v] || '') } }\n          }\n        },\n    plugins: [solarBackgroundPlugin] \/\/ Change dir bg color based on day\/night\n\n      });    autoHideTooltip(speedChart);\nautoHideTooltip(dirChart);\n\n    } catch (e) { console.error(\"Update Error:\", e); }\n  }\n\n  updateLighthouse();\n  setInterval(updateLighthouse, 60000);\n})();\n<\/script>\n-->\n\n\n\n<!-- 2h wind widget -->\n<style>\n\n\/* To allow chart Zooming *\/\n#sc-speed-chart, #sc-dir-chart {\n  touch-action: pan-y;\n}\n\n\/* Button Styling *\/\n.nav-btn { \n  border: none; \n  background: #eee; \n  padding: 5px 12px; \n  border-radius: 20px; \n  font-size: 11px; \n  font-weight: 700; \n  cursor: pointer; \n  color: #666; \n  transition: 0.2s; \n}\n.nav-btn:focus { outline: none; box-shadow: none; }\n\n  .sc-pro-cockpit {\n    font-family: 'Segoe UI', Roboto, Arial, sans-serif;\n    max-width: 500px;\n    margin: 20px auto;\n    background: rgba(0,0,0,0.02);\n    border: 1px solid #eee;\n    border-radius: 12px;\n    padding: 15px;\n    color: #333;\n  }\n  .sc-graph-label {\n    font-size: 11px;\n    font-weight: 700;\n    color: #888;\n\/* text-transform: uppercase; *\/\n    margin-bottom: 8px;\n    letter-spacing: 1px;\n    display: flex;\n    justify-content: space-between;\n  }\n  .sc-chart-wrapper { margin-bottom: 25px; position: relative; height: 180px; }\n\n<\/style>\n\n<div class=\"sc-pro-cockpit\">\n  <div class=\"sc-graph-label\"><span>2h Wind Speed &#038; Gust evolution (km\/h)<\/span><\/div>\n  <div class=\"sc-chart-wrapper\">\n    <canvas id=\"sc-speed-chart\"><\/canvas>\n  <\/div>\n  <div class=\"sc-graph-label\"><span>2h Wind Direction<\/span><\/div>\n  <div class=\"sc-chart-wrapper\" style=\"height: 160px;\">\n    <canvas id=\"sc-dir-chart\"><\/canvas>\n  <\/div>\n<\/div>\n\n<div class=\"sc-controls\" style=\"display: flex; justify-content: center; gap: 20px; margin-top: -10px; padding-bottom: 15px;\">\n  <button id=\"sc-toggle-mode\" class=\"nav-btn\" style=\"background: #e8f4ff; border: 1px solid #185e96;\">READING Mode ACTIVE<\/button>\n  <button id=\"sc-reset-view\" class=\"nav-btn\" style=\"background: none; border: 1px solid #ccc;\">RESET View<\/button>\n<\/div>\n\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js\"><\/script>\n\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/hammerjs@2.0.8\"><\/script>\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chartjs-plugin-zoom@2.2.0\"><\/script>\n\n<script>\n(function() {\n  let speedChart, dirChart;\n\n\/\/ Zoom master switch\n  let isReadingMode = true; \n\n  const baseDir = 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/santacruz\/';\n\n  const getCardinal = (angle) => {\n    const directions = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'];\n    return directions[Math.round(angle \/ 22.5) % 16];\n  };\n\n  \/\/ Auto hide Tooltip function\n  function autoHideTooltip(chart, delay = 5000) {\n    let hideTimer;\n    const canvas = chart.canvas;\n\n    const clearActiveElements = () => {\n      if (chart) {\n\/\/ Removes highlight dots from the lines\/points\n        chart.setActiveElements([]);\n\/\/ Hides the tooltip box\n        chart.tooltip.setActiveElements([], { x: 0, y: 0 });\n        chart.update();\n      }\n    };\n\n    \/\/ Listen for touch end or mouse out to start the timer\n    const startTimer = () => {\n      clearTimeout(hideTimer);\n      hideTimer = setTimeout(clearActiveElements, delay);\n    };\n\n    \/\/ Reset timer if the user interacts again before it hides\n    const resetTimer = () => {\n      clearTimeout(hideTimer);\n    };\n\n    canvas.addEventListener('touchend', startTimer);\n    canvas.addEventListener('touchstart', resetTimer); \n  }\n\n  async function updateLighthouse() {\n    try {\n      const now = Date.now();\n      const cacheBust = '?t=' + now; \/\/ Defines the cache cleaner variable\n      const twoHoursAgo = now - (120 * 60 * 1000);\n      \n  \/\/ 1. Fetch Solar Data (local json instead of external API)\n      const solunarRes = await fetch('https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/moon-time\/solunar-data.json');\n      const solunarData = await solunarRes.json();\n\n\/\/ Turn HH:mm into a real timestamp for today\n      const parseTime = (timeStr) => {\n        const [h, m] = timeStr.split(':');\n        const d = new Date();\n        d.setHours(h, m, 0, 0);\n        return d.getTime();\n      };\n\n      const sunrise = parseTime(solunarData.today.sunrise);\n      const sunset = parseTime(solunarData.today.sunset);\n\n      const timeFormatter = (val) => {\n        const d = new Date(val);\n        return `${d.getHours()}:${String(d.getMinutes()).padStart(2, '0')}`;\n      };\n\n      const getFileUrl = (date) => {\n        const madeiraDate = date.toLocaleString(\"en-CA\", {timeZone: \"Atlantic\/Madeira\", hour12: false}).split(',')[0];\n        return baseDir + `${madeiraDate}.json`;\n      };\n      \n\/\/ Get Today with Cache Busting\n      const resToday = await fetch(getFileUrl(new Date()) + cacheBust);\n      let combinedData = await resToday.json();\n\n\/\/ New check added\nif (!Array.isArray(combinedData)) {\n  console.warn(\"Data malformed. Holding previous state.\");\n  return; \n}\n\n\/\/ Handle Midnight Cross-over\n\n      if (combinedData.length === 0 || (combinedData.length > 0 && combinedData[0].time > twoHoursAgo)) {\n        try {\n          const yesterday = new Date();\n          yesterday.setDate(yesterday.getDate() - 1);\n          const resYesterday = await fetch(getFileUrl(yesterday) + cacheBust);\n          const yesterdayData = await resYesterday.json();\n          combinedData = [...yesterdayData, ...combinedData];\n        } catch(e) {}\n      }\n      \n      const windowData = combinedData.filter(d => d.time > (twoHoursAgo - 10000));\n\n\/\/ 2. THE SOLAR PLUGIN (Defined once, used for both charts)\n      const solarBackgroundPlugin = {\n        id: 'solarBackground',\n        beforeDraw: (chart) => {\n          const { ctx, chartArea: { top, bottom, left, right }, scales: { x } } = chart;\n          function drawSection(startTime, endTime, color) {\n            const startPx = Math.max(left, x.getPixelForValue(startTime));\n            const endPx = Math.min(right, x.getPixelForValue(endTime));\n            if (startPx < endPx) {\n              ctx.save();\n              ctx.fillStyle = color;\n              ctx.fillRect(startPx, top, endPx - startPx, bottom - top);\n              ctx.restore();\n            }\n          }\n          ctx.save();\n          ctx.fillStyle = 'rgba(0, 0, 0, 0.06)'; \/\/ The \"Night\" tint\n          ctx.fillRect(left, top, right - left, bottom - top);\n          const yesterdaySunrise = sunrise - 86400000;\n          const yesterdaySunset = sunset - 86400000;\n          drawSection(yesterdaySunrise, yesterdaySunset, 'rgba(255, 255, 255, 0.5)'); \/\/ clears the Night tint for yesterday\n          drawSection(sunrise, sunset, 'rgba(255, 255, 255, 0.5)'); \/\/ clears the Night tint for today\n          ctx.restore();\n        }\n      };\n\n\/\/ --- SPEED CHART ---\n      if (speedChart) speedChart.destroy();\n      speedChart = new Chart(document.getElementById('sc-speed-chart'), {\n        type: 'line',\n        data: {\n          datasets: [\n            { label: 'Gust',\ndata: windowData.reduce((acc, d, i, arr) => {\n\/\/ If gap is detected (> 10 mins), push null point to break the line\n  if (i > 0 && (d.time - arr[i-1].time) > 600000) acc.push({ x: d.time - 1, y: null });\n\n\/\/ Guard check if windGust exist before reading kph\n  acc.push({ x: d.time, y: d.windGust?.kph ?? null });\n\n  return acc;\n}, []), \/\/ gap detect no line connection when no data exists\n borderColor: 'rgba(255, 165, 0, 0.45)', backgroundColor: 'rgba(255, 165, 0, 0.05)', fill: true, pointRadius: 0, borderWidth: 1.5, tension: 0.3,\nspanGaps: false \/\/ Don't connect across null non existent values\n },\n            { label: 'Speed', data: windowData.reduce((acc, d, i, arr) => {\n  if (i > 0 && (d.time - arr[i-1].time) > 600000) acc.push({ x: d.time - 1, y: null });\n\n\/\/ Guard check if windSpeed exists before reading kph\n  acc.push({ x: d.time, y: d.windSpeed?.kph ?? null });\n\n  return acc;\n}, []), \/\/ gap detect no line connection when no data exists\nborderColor: 'rgba(24, 94, 150, 0.8)', backgroundColor: 'rgba(24, 94, 150, 0.1)', fill: true, pointRadius: 0, borderWidth: 2, tension: 0.3,\nspanGaps: false \/\/ Don't connect across null non existent values\n }\n          ]\n        },\n        options: {\n          responsive: true, maintainAspectRatio: false,\n          interaction: { mode: 'index', intersect: false, axis: 'x' },\n          plugins: { \n            legend: { display: false }, \n            tooltip: { \n\/\/Only show when button clicked\n              enabled: isReadingMode,\n              callbacks: { \n                title: (items) => timeFormatter(items[0].parsed.x),\n                label: (ctx) => `${ctx.dataset.label}: ${ctx.parsed.y.toFixed(1)} km\/h` \n              } \n            },\n\/\/ --- Graph Zoom Start ---           \n            zoom: {\n              limits: { x: { min: twoHoursAgo, max: now } },\n\/\/ 2. Pan only works when NOT in reading mode\n              pan: { enabled: !isReadingMode, mode: 'x' }, \n              zoom: {\n                wheel: { enabled: true },\n                pinch: { enabled: true },\n                mode: 'x'\n              }\n            }\n\/\/ --- Graph Zoom End --- \n          },\n          scales: {\n            x: { \n              type: 'linear', min: twoHoursAgo, max: now,\n              bounds: 'ticks', \n              ticks: { stepSize: 1 * 60 * 1000, callback: (val) => timeFormatter(val), font: { size: 10 }, maxRotation: 0, minRotation: 0, includeBounds: true, maxTicksLimit: 15, autoSkip: true },\n              grid: { display: true, color: 'rgba(0,0,0,0.08)' }\n            },\n            y: { \n              beginAtZero: true, \n              ticks: { font: { size: 10 } } \n            },\n  \n\/\/ --- MIRRORED RIGHT AXIS ---\n            yRight: { \n              position: 'right', \n              beginAtZero: true,\n              display: true, \/\/ false to hide\n              grid: { display: false }, \n              ticks: { font: { size: 10 } },\n\n\/\/ function tells the right axis to copy left axis exactly\n              afterDataLimits: (axis) => {\n                axis.max = axis.chart.scales.y.max;\n                axis.min = axis.chart.scales.y.min;\n              }\n            }\n          }\n        },\n       \n        plugins: [solarBackgroundPlugin]\n\/\/ Change speed bg color based on day\/night\n      });\n\n\/\/ --- RAW DIRECTION CHART ---\n\/\/ map d.winddir directly without any averaging\n      const rawDirData = windowData.map(d => ({ x: d.time, y: d.winddir }));\n\n      if (dirChart) dirChart.destroy();\n      dirChart = new Chart(document.getElementById('sc-dir-chart'), {\n        type: 'scatter', \/\/ points for better visual interpretation\n        data: { \n          datasets: [{ \n            data: rawDirData, \n            backgroundColor: 'rgba(24, 94, 150, 0.6)', \n            pointRadius: 2.5,\n            pointHoverRadius: 5\n          }] \n        },\n        options: {\n          responsive: true, maintainAspectRatio: false,\n          interaction: { mode: 'nearest', axis: 'x', intersect: false },\n          plugins: { \n            legend: { display: false },\n            tooltip: { \n              enabled: isReadingMode,\n              callbacks: { \n                title: (items) => timeFormatter(items[0].raw.x),\n                label: (ctx) => `Dir: ${Math.round(ctx.raw.y)}\u00b0 (${getCardinal(ctx.raw.y)})` \n              } \n            },\n            zoom: {\n              limits: { x: { min: twoHoursAgo, max: now } },\n              pan: { enabled: !isReadingMode, mode: 'x' }, \n              zoom: { wheel: { enabled: true }, pinch: { enabled: true }, mode: 'x' }\n            }\n          },\n          scales: {\n            x: { \n              type: 'linear', min: twoHoursAgo, max: now,\n              bounds: 'ticks', \n              ticks: { stepSize: 1 * 60 * 1000, callback: (val) => timeFormatter(val), font: { size: 10 }, maxRotation: 0, minRotation: 0, includeBounds: true, maxTicksLimit: 15, autoSkip: true },\n              grid: { display: true, color: 'rgba(0,0,0,0.08)' } \n            },\n            y: { min: 0, max: 360, ticks: { stepSize: 90, font: { size: 10 }, callback: v => v + '\u00b0' }, grid: { color: '#bbb', lineWidth: 1 } },\n            yRight: { position: 'right', min: 0, max: 360, grid: { display: false }, ticks: { stepSize: 90, font: { weight: 'bold', size: 11 }, callback: v => ({ 0: 'N', 90: 'E', 180: 'S', 270: 'W', 360: 'N' }[v] || '') } }\n          }\n        },\n        \n        plugins: [solarBackgroundPlugin]\n\/\/ Change dir bg color based on day\/night\n      });    \n\n      autoHideTooltip(speedChart);\n      autoHideTooltip(dirChart);\n\n    } catch (e) { console.error(\"Update Error:\", e); }\n  }\n\n  updateLighthouse();\n  setInterval(updateLighthouse, 60000);\n\n  \/\/ Handle Mode Toggle\n  document.getElementById('sc-toggle-mode').addEventListener('click', (e) => {\n    isReadingMode = !isReadingMode;\n    \n\/\/ Update button appearance to stay \"Active\" (Blue) both modes\n    e.target.textContent = isReadingMode ? \"READING Mode ACTIVE\" : \"ZOOM\/PAN Mode ACTIVE\";\n    \n\/\/ Same blue styling for both\n    e.target.style.background = \"#e8f4ff\";\n    e.target.style.borderColor = \"#185e96\";\n\n    \/\/ Update both charts\n    [speedChart, dirChart].forEach(chart => {\n      chart.options.plugins.tooltip.enabled = isReadingMode;\n      chart.options.plugins.zoom.pan.enabled = !isReadingMode;\n      chart.update('none'); \/\/ Update without animation for a \"solid\" feel\n    });\n  });\n\n  \/\/ Handle Reset View\n  document.getElementById('sc-reset-view').addEventListener('click', () => {\n    [speedChart, dirChart].forEach(chart => chart.resetZoom());\n  });\n\n})();\n<\/script>\n\n\n\n<!-- 24h temp and hum graph -->\n<style>\n  \/* To allow chart Zooming *\/\n  #sc-8h-story-chart {\n    touch-action: pan-y;\n  }\n\n  \/* Zoom Button Styling (Include if this widget operates independently from the Solar one) *\/\n  .nav-btn { \n    border: none; \n    background: #eee; \n    padding: 5px 12px; \n    border-radius: 20px; \n    font-size: 11px; \n    font-weight: 700; \n    cursor: pointer; \n    color: #666; \n    transition: 0.2s; \n  }\n  .nav-btn:focus { outline: none; box-shadow: none; }\n<\/style>\n\n<div class=\"sc-pro-cockpit\" style=\"margin-top: 10px;\">\n <div class=\"sc-legend\" style=\"display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; font-size: 11px; font-weight: 600; color: #888;\">\n  \n  <div style=\"display: flex; gap: 11px;\">\n    <div id=\"leg-temp\" style=\"display: flex; align-items: center; gap: 4px; cursor: pointer; transition: opacity 0.3s;\">\n     24h &nbsp;<span style=\"width: 12px; height: 3px; background:  #e67e22; border-radius: 2px;\"><\/span> Temp \u00baC\n    <\/div>\n    <div id=\"leg-hum\" style=\"display: flex; align-items: center; gap: 4px; cursor: pointer; transition: opacity 0.3s;\">\n      <span style=\"width: 12px; height: 3px; background: #3498db; border-radius: 2px;\"><\/span> Hum %\n    <\/div>\n    <div id=\"leg-clouds\" style=\"display: flex; align-items: center; gap: 4px; cursor: pointer; transition: opacity 0.3s;\">\n      <span style=\"width: 12px; height: 9px; background: rgba(200, 200, 200, 0.6); border-radius: 2px;\"><\/span> Cloudbase\n    <\/div>\n  <\/div>\n\n  <div id=\"leg-all\" style=\"cursor: pointer; color: #333; border-bottom: 2px solid #333; padding-bottom: 2px; font-size: 11px; margin-right: 5px;\">ALL<\/div>\n<\/div>\n\n  <div class=\"sc-chart-wrapper\" style=\"height: 200px; position: relative;\">\n    <canvas id=\"sc-8h-story-chart\"><\/canvas>\n  <\/div>\n<\/div>\n\n<div class=\"sc-controls\" style=\"display: flex; justify-content: center; gap: 20px; margin-top: 10px; padding-bottom: 15px;\">\n  <button id=\"th-toggle-mode\" class=\"nav-btn\" style=\"background: #e8f4ff; border: 1px solid #185e96;\">READING Mode ACTIVE<\/button>\n  <button id=\"th-reset-view\" class=\"nav-btn\" style=\"background: none; border: 1px solid #ccc;\">RESET View<\/button>\n<\/div>\n\n<!-- Ensure Chart.js, Hammer.js, and chartjs-plugin-zoom are loaded on the page -->\n<script>\n(function() {\n  let storyChart;\n  let isReadingMode = true; \/\/ Added Zoom master switch\n\n\/\/ The base directory\n  const baseDir = 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/santacruz\/';\n\n\/\/ Auto hide Tooltip function\n function autoHideTooltip(chart, delay = 5000) {\n    let hideTimer;\n    const clearTooltip = () => {\n      if (chart) {\n\/\/ Clear line highlight point  \n        chart.setActiveElements([]); \n\/\/ hide the tooltip box\n        if (chart.tooltip) {\n          chart.tooltip.setActiveElements([], { x: 0, y: 0 });\n        }\n        chart.update('none');\n      }\n    };\n   chart.canvas.addEventListener('touchstart', () => {\n      clearTimeout(hideTimer);\n    });\n  chart.canvas.addEventListener('touchend', () => {\n      clearTimeout(hideTimer);\n      hideTimer = setTimeout(clearTooltip, delay);\n    });\n  }\n\n\/\/ For human-readable time\n  const timeFormatter = (val) => {\n    const d = new Date(val);\n    return `${d.getHours()}:${String(d.getMinutes()).padStart(2, '0')}`;\n  };\n\n\/\/ Function to make identical twin scales and \"Secret Sauce\"\n  function updateScales(chart) {\n    const humVisible = chart.isDatasetVisible(0);\n    const tempVisible = chart.isDatasetVisible(1);\n    \n    const yTemp = chart.options.scales.yTemp;\n    const yHum = chart.options.scales.yHum;\n\n    if (tempVisible && !humVisible) {\n\/\/ TEMP ONLY: identical steps for degree alignment\n      const tempDataset = chart.data.datasets[1].data.map(d => d.y).filter(v => v !== null);\n      const min = Math.floor(Math.min(...tempDataset)) - 1;\n      const max = Math.ceil(Math.max(...tempDataset)) + 1;\n\n      yTemp.min = yHum.min = min;\n      yTemp.max = yHum.max = max;\n      yTemp.ticks.stepSize = yHum.ticks.stepSize = 1; \n      yTemp.ticks.callback = yHum.ticks.callback = (v) => v.toFixed(0) + '\u00b0';\n      yTemp.grace = yHum.grace = 0;\n    } \n    else if (humVisible && !tempVisible) {\n\/\/ HUM ONLY:identical 10% steps\n      yTemp.min = yHum.min = 0;\n      yTemp.max = yHum.max = 100;\n      yTemp.ticks.stepSize = yHum.ticks.stepSize = 10; \n      yTemp.ticks.callback = yHum.ticks.callback = (v) => v.toFixed(0) + '%';\n      yTemp.grace = yHum.grace = 0;\n    } \n    else {\n\/\/ ALL VIEW: \"Secret Sauce\" logic\n      yTemp.min = undefined; yTemp.max = undefined;\n      yTemp.ticks.stepSize = undefined; \n      yHum.min = 0; yHum.max = 100;\n      yHum.ticks.stepSize = 10;\n      yTemp.ticks.callback = (v) => v.toFixed(0) + '\u00b0';\n      yHum.ticks.callback = (v) => v + '%';\n      yTemp.grace = '14%';\n    }\n  }\n\n  function syncLegendUI(chart) {\n    const humVisible = chart.isDatasetVisible(0);\n    const tempVisible = chart.isDatasetVisible(1);\n    document.getElementById('leg-hum').style.opacity = humVisible ? '1' : '0.3';\n    document.getElementById('leg-temp').style.opacity = tempVisible ? '1' : '0.3';\n    document.getElementById('leg-clouds').style.opacity = chart.isDatasetVisible(2) ? '1' : '0.3';\n    document.getElementById('leg-all').style.opacity = (humVisible && tempVisible) ? '1' : '0.5';\n    document.getElementById('leg-all').style.borderBottomColor = (humVisible && tempVisible) ? '#333' : 'transparent';\n  }\n\n  async function updateStory() {\n    try {\n\/\/ Ensure cache busting defined\n const now = Date.now();\n const cacheBust = '?t=' + now; \n \n\/\/ 24h graph window\n const twelveHoursAgo = now - (24 * 60 * 60 * 1000); \n\n\/\/ CONFIG TOGGLE - CHANGE THIS TO False to ALWAYS SHOW CLOUDS\n      const showCloudsOnlyDuringDay = true; \n\n\/\/ Sunrise & set from our json\n      const solunarRes = await fetch('https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/moon-time\/solunar-data.json');\n      const solunarData = await solunarRes.json();\n\n\/\/ Turn HH:mm into timestamp for today\n      const parseTime = (timeStr) => {\n        const [h, m] = timeStr.split(':');\n        const d = new Date();\n        d.setHours(h, m, 0, 0);\n        return d.getTime();\n      };\n\n      const sunriseToday = parseTime(solunarData.today.sunrise);\n      const sunsetToday = parseTime(solunarData.today.sunset);\n      \n      \/\/ Determine if a timestamp is \"Night\"\n      const getIsNight = (timestamp) => {\n        const d = new Date(timestamp);\n\/\/ Simplified check: is it between sunset and sunrise? Adjusts window cross midnight\n        if (timestamp > sunriseToday && timestamp < sunsetToday) return false;\n        if (timestamp < (sunriseToday - 86400000) || timestamp > (sunsetToday + 86400000)) return true; \n\/\/ Check yesterday's sun if timestamp is very old\n        const yesterdaySunset = sunsetToday - 86400000;\n        const yesterdaySunrise = sunriseToday - 86400000;\n        if (timestamp > yesterdaySunrise && timestamp < yesterdaySunset) return false;\n        return true; \n      };\n\n    const getFileUrl = (date) => {\n    const madeiraDate = date.toLocaleString(\"en-CA\", {timeZone: \"Atlantic\/Madeira\", hour12: false}).split(',')[0];\n    return baseDir + `${madeiraDate}.json`;\n  };\n\n\/\/ Get Today with Cache Busting\n      const resToday = await fetch(getFileUrl(new Date()) + cacheBust);\n      let combinedData = await resToday.json();\n\n      if (combinedData.length === 0 || (combinedData.length > 0 && combinedData[0].time > twelveHoursAgo)) {\n        try {\n          const yesterday = new Date();\n          yesterday.setDate(yesterday.getDate() - 1);\n          const resYesterday = await fetch(getFileUrl(yesterday) + cacheBust);\n          const yesterdayData = await resYesterday.json();\n          combinedData = [...yesterdayData, ...combinedData];\n        } catch(e) {}\n      }\n\n\/\/ Filter time window, keep only unique timestamps\nconst windowData = combinedData\n        .filter(d => d.time > (twelveHoursAgo - 10000))\n        .filter((value, index, self) => index === self.findIndex((t) => t.time === value.time));\n\n   \/\/ GAP DETECTOR Logic\n      const processedStoryData = [];\n      windowData.forEach((d, i) => {\n        if (i > 0 && (d.time - windowData[i - 1].time) > (10 * 60 * 1000)) {\n\/\/ 10 minutes\n\n\/\/ Place null for all 3 metrics to hide all lines\nprocessedStoryData.push({ time: d.time - 1, humidity: null, temp: null, dewpoint: null });\n        }\n        processedStoryData.push(d); \n      });\n\/\/ --- GAP DETECTOR END ---\n\n      if (storyChart) storyChart.destroy();\n      const ctx = document.getElementById('sc-8h-story-chart').getContext('2d');\n\n      const cloudLayerPlugin = {\n        id: 'cloudLayer',\n        beforeDatasetsDraw: (chart) => {\n          if (!chart.isDatasetVisible(2)) return;\n          const { ctx, chartArea: { top, bottom, left, right }, scales: { x } } = chart;\n          ctx.save(); \n\n\/\/ 1. Establish the clipping boundary so that the cloud stay inside the graph\nctx.beginPath();\nctx.rect(left, top, right - left, bottom - top);\nctx.clip();\n\n\/\/ 2. Start a fresh path exclusively for the cloud data\nctx.beginPath();\n\nlet first = true;\n\n   windowData.forEach(d => {\n            if (d.temp && d.dewpoint) {\n              const xPx = x.getPixelForValue(d.time);\n              const cbMeters = ((d.temp.c - d.dewpoint.c) * 125) + 125; \n\/\/ +station height for accuracy\n\n\/\/ Apply dynamic night check\n     const isNight = getIsNight(d.time);\n              const daytimeCloudY = bottom - (Math.min(cbMeters, 2000) \/ 2000) * (bottom - top);\n              const cloudY = (showCloudsOnlyDuringDay && getIsNight(d.time)) ? bottom : daytimeCloudY;\n              if (first) { ctx.moveTo(xPx, cloudY); first = false; }\n              else { ctx.lineTo(xPx, cloudY); }\n            }\n          });\n                    ctx.lineTo(right, top);\n          ctx.lineTo(left, top);\n          ctx.closePath();\n          const gradient = ctx.createLinearGradient(0, top, 0, bottom);\n          gradient.addColorStop(0, 'rgba(100, 100, 100, 0.16)');\n          gradient.addColorStop(1, 'rgba(100, 100, 100, 0.08)');\n          ctx.fillStyle = gradient;\n          ctx.fill();\n          ctx.restore();\n        }\n      };\n\n      const solarBackgroundPlugin = {\n        id: 'solarBackground',\n        beforeDraw: (chart) => {\n          const { ctx, chartArea: { top, bottom, left, right }, scales: { x } } = chart;\n                   function drawSection(startTime, endTime, color) {\n            const startPx = Math.max(left, x.getPixelForValue(startTime));\n            const endPx = Math.min(right, x.getPixelForValue(endTime));\n            if (startPx < endPx) {\n              ctx.save(); ctx.fillStyle = color;\n              ctx.fillRect(startPx, top, endPx - startPx, bottom - top);\n              ctx.restore();\n            }\n          }\n          ctx.save();\n          ctx.fillStyle = 'rgba(0, 0, 0, 0.06)'; \n          ctx.fillRect(left, top, right - left, bottom - top);\n          drawSection(sunriseToday - 86400000, sunsetToday - 86400000, 'rgba(255, 255, 255, 0.5)');\n          drawSection(sunriseToday, sunsetToday, 'rgba(255, 255, 255, 0.5)');\n          ctx.restore();\n        }\n      };\n\n      storyChart = new Chart(ctx, {\n        type: 'line',\n        data: {\n          datasets: [\n            { label: 'Humidity', data: processedStoryData.map(d => ({x: d.time, y: d.humidity})), borderColor: '#3498db', backgroundColor: 'rgba(52, 152, 219, 0.05)', yAxisID: 'yHum', pointRadius: 0, borderWidth: 2, tension: 0.4, fill: true, spanGaps: false },\n            { label: 'Temp', data: processedStoryData.map(d => ({x: d.time, y: d.temp ? d.temp.c : null})), borderColor: '#e67e22', backgroundColor: 'rgba(230, 126, 34, 0.1)', yAxisID: 'yTemp', pointRadius: 0, borderWidth: 2, tension: 0.4, spanGaps: false },\n            { label: 'DewPoint', data: processedStoryData.map(d => ({x: d.time, y: d.dewpoint ? d.dewpoint.c : null})), borderColor: 'transparent', pointRadius: 0, borderWidth: 0, yAxisID: 'yTemp', tension: 0.1, spanGaps: false }\n          ]\n        },\n        options: {\n\nanimations: {\n  y: {\n    type: 'number',\n    duration: 1500,\n    from: (ctx) => {\n\/\/ Automatically selects yTemp or yHum based on the dataset\n      const scale = ctx.chart.scales[ctx.dataset.yAxisID];\n      if (!scale || ctx.raw === undefined || ctx.raw.y === null) return ctx.target;\n      \n\/\/ Start at 85% of the target value for gentle 15% \"stretch\"\n      return scale.getPixelForValue(ctx.raw.y * 0.85);\n    }\n  }\n},\n          responsive: true,\n          maintainAspectRatio: false,\n          interaction: { mode: 'index', intersect: false },\n          plugins: { \n            legend: { display: false },\n            tooltip: {\n              enabled: isReadingMode, \/\/ Controlled by toggle\n              callbacks: {\n                title: (items) => timeFormatter(items[0].parsed.x),\n                label: (ctx) => {\n                  const val = ctx.parsed.y;\n                  if (val === null) return '';\n                  if (ctx.dataset.label === 'Humidity') return `Humidity: ${Math.round(val)}%`;\n                  if (ctx.dataset.label === 'Temp') return `Temp: ${val.toFixed(1)}\u00b0C`;\n                  if (ctx.dataset.label === 'DewPoint') {\n                    const d = windowData[ctx.dataIndex];\n                    if (!d || !d.temp) return `Dew Point: ${val.toFixed(1)}\u00b0C`;\n                    const cb = (d.temp.c - d.dewpoint.c) * 125 + 125; \n                    return [`Dew Point: ${val.toFixed(1)}\u00b0C`, `Cloudbase: ${Math.max(0, Math.round(cb))}m` ];\n                  }\n                  return '';\n                }\n              }\n            },\n            zoom: {\n              limits: { x: { min: twelveHoursAgo, max: now } },\n              pan: { enabled: !isReadingMode, mode: 'x' },\n              zoom: { wheel: { enabled: true }, pinch: { enabled: true }, mode: 'x' }\n            }\n          },\n          scales: {\n            x: { type: 'linear', min: twelveHoursAgo, max: now, ticks: { \nstepSize: 10 * 60 * 1000, \n      autoSkip: true, \n      includeBounds: true,\ncallback: (v) => timeFormatter(v), font: { size: 10 }, maxRotation: 0 }, grid: { color: 'rgba(0,0,0,0.09)' } },\n            yTemp: { \n              type: 'linear', position: 'left', grace: '14%', \n              ticks: { font: { size: 10, weight: '600' }, callback: (v) => v.toFixed(0) + '\u00b0' }, \n              grid: { display: true, color: 'rgba(34, 34, 34, 0.12)', borderDash: [3, 3] }, \n              afterDataLimits: (axis) => {\n                const humVis = axis.chart.isDatasetVisible(0);\n                const tempVis = axis.chart.isDatasetVisible(1);\n                if (tempVis && humVis) {\n                  const vals = axis.chart.data.datasets[1].data.map(d => d.y).filter(v => v !== null);\n                  const min = Math.min(...vals); const max = Math.max(...vals);\n                  axis.min = min - 1;\n                  axis.max = max + ((max - min) * 1.1 + 1.1); \n                }\n              }\n            },\n            yHum: { \n              type: 'linear', position: 'right', min: 0, max: 100, \n              ticks: { font: { size: 10 }, callback: (v) => v + '%' }, \n              grid: { display: false } \n            }\n          }\n        },\n        plugins: [solarBackgroundPlugin, cloudLayerPlugin]\n      });\n\n      const refreshUI = () => {\n        updateScales(storyChart);\n        syncLegendUI(storyChart);\n        storyChart.update();\n      };\n\n\/\/ - SET DEFAULT VIEW ON LOAD -\n\/\/ 0 = Humidity, 1 = Temp, 2 = DewPoint\/Clouds\n      storyChart.setDatasetVisibility(0, false); \/\/ Hide Humidity\n      storyChart.setDatasetVisibility(1, true);  \/\/ Show Temp\n      storyChart.setDatasetVisibility(2, false);  \/\/ Hide Cloudbase\n      \n\/\/ Make scales and legend sync to this view immediately\n      refreshUI();\n     document.getElementById('leg-all').onclick = () => {\n        storyChart.setDatasetVisibility(0, true);\n        storyChart.setDatasetVisibility(1, true);\n        storyChart.setDatasetVisibility(2, true);\n        refreshUI();\n      };\n\ndocument.getElementById('leg-hum').onclick = () => {\n        storyChart.setDatasetVisibility(0, true);\n        storyChart.setDatasetVisibility(1, false);\n        storyChart.setDatasetVisibility(2, false);\n        refreshUI();\n      };\n   document.getElementById('leg-temp').onclick = () => {\n        storyChart.setDatasetVisibility(0, false);\n        storyChart.setDatasetVisibility(1, true);\n        storyChart.setDatasetVisibility(2, false);\n        refreshUI();\n      };\n\ndocument.getElementById('leg-clouds').onclick = () => {\n        storyChart.setDatasetVisibility(2, !storyChart.isDatasetVisible(2));\n        syncLegendUI(storyChart);\n        storyChart.update();\n      };\n\nautoHideTooltip(storyChart);\n    } catch (e) { console.error(\"Story Chart Error:\", e); }\n  }\n\n   \/\/ Handle Mode Toggle\n  document.getElementById('th-toggle-mode').addEventListener('click', (e) => {\n    isReadingMode = !isReadingMode;\n    e.target.textContent = isReadingMode ? \"READING Mode ACTIVE\" : \"ZOOM\/PAN Mode ACTIVE\";\n    if (storyChart) {\n      storyChart.options.plugins.tooltip.enabled = isReadingMode;\n      storyChart.options.plugins.zoom.pan.enabled = !isReadingMode;\n      storyChart.update('none');\n    }\n  });\n\n  \/\/ Handle Reset View\n  document.getElementById('th-reset-view').addEventListener('click', () => {\n    if (storyChart) storyChart.resetZoom();\n  });\n\n  updateStory();\n\/\/ 5 minute refresh\nsetInterval(updateStory, 300000); \n})();\n<\/script>\n\n\n\n<!-- 24h Solar evolution graph with UV and Total Yield Harvest - added zoom 20-05-26 -->\n<style>\n\n  \/* To allow chart Zooming *\/\n  #sc-solar-canvas {\n    touch-action: pan-y;\n  }\n\n  \/* Zoom Button Styling *\/\n  .nav-btn { \n    border: none; \n    background: #eee; \n    padding: 5px 12px; \n    border-radius: 20px; \n    font-size: 11px; \n    font-weight: 700; \n    cursor: pointer; \n    color: #666; \n    transition: 0.2s; \n  }\n  .nav-btn:focus { outline: none; box-shadow: none; }\n\n  .sc-solar-cockpit {\n    font-family: 'Segoe UI', Roboto, Arial, sans-serif;\n    max-width: 500px;\n    margin: 20px auto;\n    background: rgba(0,0,0,0.02);\n    border: 1px solid #eee;\n    border-radius: 12px;\n    padding: 15px;\n    color: #333;\n  }\n  .sc-solar-label {\n    font-size: 11px;\n    font-weight: 800;\n    color: #888;\n    margin-bottom: 8px;\n    letter-spacing: 1px;\n    display: flex;\n    white-space: nowrap;\n    justify-content: space-between;\n    align-items: center;\n  }\n  .sc-solar-badge {\n    color: #96752d;\n    font-weight: 800;\n    font-size: 11px;\n    background: transparent;\n    padding: 3px 8px;\n    border-radius: 5px;\n  }\n  .sc-solar-wrapper { position: relative; height: 200px; }\n\n  \/* Legend specific styles *\/\n  .sc-solar-legend-item {\n    cursor: pointer;\n    transition: opacity 0.3s;\n    display: flex;\n    align-items: center;\n    gap: 4px;\n  }\n<\/style>\n\n<div class=\"sc-solar-cockpit\">\n  <div class=\"sc-solar-label\">\n    <div style=\"display: flex; gap: 10px; align-items: center;\">\n      <div id=\"leg-solar\" class=\"sc-solar-legend-item\">\n        24h <span style=\"width: 10px; height: 3px; background: #b39917; border-radius: 2px;\"><\/span> Solar\n      <\/div>\n      <div id=\"leg-uv\" class=\"sc-solar-legend-item\">\n        <span style=\"width: 10px; height: 3px; background: #888cbd; border-radius: 2px;\"><\/span> UV\n      <\/div>\n      <div id=\"leg-all-solar\" style=\"cursor: pointer; color: #333; border-bottom: 2px solid #333; opacity: 0.5; padding-bottom: 1px; font-size: 10px; margin-left: 5px;\">ALL<\/div>\n    <\/div>\n    <div style=\"display: flex; flex-direction: column; align-items: flex-end;\">\n      <span id=\"sc-solar-peak-badge\" class=\"sc-solar-badge\">Peak: 0 W\/m\u00b2<\/span>\n      <span id=\"sc-solar-total-badge\" class=\"sc-solar-badge\" style=\"margin-top: -5px;\">Yield: 0 Wh\/m\u00b2<\/span>\n    <\/div>\n  <\/div>\n  <div class=\"sc-solar-wrapper\">\n    <canvas id=\"sc-solar-canvas\"><\/canvas>\n  <\/div>\n<\/div>\n\n<div class=\"sc-controls\" style=\"display: flex; justify-content: center; gap: 20px; margin-top: -10px; padding-bottom: 15px;\">\n  <button id=\"sl-toggle-mode\" class=\"nav-btn\" style=\"background: #e8f4ff; border: 1px solid #185e96;\">READING Mode ACTIVE<\/button>\n  <button id=\"sl-reset-view\" class=\"nav-btn\" style=\"background: none; border: 1px solid #ccc;\">RESET View<\/button>\n<\/div>\n\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js\"><\/script>\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/hammerjs@2.0.8\"><\/script>\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chartjs-plugin-zoom@2.2.0\"><\/script>\n\n<script>\n(function() {\n  let solarChart;\n  let solarTimeout;\n  let isReadingMode = true; \/\/ Added Zoom master switch\n  const stationPath = 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/santacruz\/';\n  let lastSuccessfulPoints = []; \n\n  \/\/ 1. Helper: Format Time Strings\n  const formatTime = (ts) => {\n    const d = new Date(ts);\n    return d.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', hour12: false });\n  };\n\n\/\/ Sync Legend UI logic\n  function syncLegendUI(chart) {\n    const solarVisible = chart.isDatasetVisible(0);\n    const uvVisible = chart.isDatasetVisible(1);\n    document.getElementById('leg-solar').style.opacity = solarVisible ? '1' : '0.3';\n    document.getElementById('leg-uv').style.opacity = uvVisible ? '1' : '0.3';\n    document.getElementById('leg-all-solar').style.opacity = (solarVisible && uvVisible) ? '1' : '0.5';\n    document.getElementById('leg-all-solar').style.borderBottomColor = (solarVisible && uvVisible) ? '#333' : 'transparent';\n  }\n\n\/\/ Make scales mirrored dynamically\nfunction updateScales(chart) {\n  const solarVisible = chart.isDatasetVisible(0);\n  const uvVisible = chart.isDatasetVisible(1);\n  const ySolar = chart.options.scales.ySolar;\n  const yUV = chart.options.scales.yUV;\n\n  \/\/ Clear previous limits and grace to take manual control\n  ySolar.ticks.maxTicksLimit = undefined;\n  yUV.ticks.maxTicksLimit = undefined;\n  ySolar.grace = yUV.grace = 0; \n\n  \/\/ 1. Get the absolute highest value currently on the chart\n  const sData = chart.data.datasets[0].data.map(d => d.y).filter(v => v !== null);\n  const uData = chart.data.datasets[1].data.map(d => d.y).filter(v => v !== null);\n  \n  const peakSolar = Math.max(...sData, 0);\n  const peakUV = Math.max(...uData, 0) * 100; \/\/ Multiply by 100 to align scales\n  const absolutePeak = Math.max(peakSolar, peakUV);\n\n  \/\/ 2. Determine Scale and Steps\n  let scaleMax, solarStep, uvStep;\n\n  if (absolutePeak <= 1000) {\n    \/\/ Normal\/Low conditions: Lock to 1000 Max, 100W \/ 1.0UV steps\n    scaleMax = 1000;\n    solarStep = 100;\n    uvStep = 1;\n  } else {\n    \/\/ Extreme conditions (>1000W): Expand the chart dynamically\n    \/\/ Adds a 50W buffer, then rounds up to the nearest 200 (e.g., 1080 -> 1200)\n    scaleMax = Math.ceil((absolutePeak + 50) \/ 200) * 200; \n    solarStep = 200;\n    uvStep = 2;\n  }\n\n  \/\/ 3. Apply to Views\n  if (solarVisible && !uvVisible) {\n    \/\/ SOLAR ONLY\n    ySolar.min = yUV.min = 0;\n    ySolar.max = yUV.max = scaleMax;\n    ySolar.ticks.stepSize = yUV.ticks.stepSize = solarStep;\n    \n    ySolar.ticks.callback = yUV.ticks.callback = (v) => v + ' W';\n    ySolar.ticks.color = yUV.ticks.color = '#888';\n    ySolar.ticks.font.weight = yUV.ticks.font.weight = '600';\n  } \n  else if (uvVisible && !solarVisible) {\n    \/\/ UV ONLY\n    ySolar.min = yUV.min = 0;\n    ySolar.max = yUV.max = scaleMax \/ 100;\n    ySolar.ticks.stepSize = yUV.ticks.stepSize = uvStep;\n    \n    ySolar.ticks.callback = yUV.ticks.callback = (v) => v.toFixed(1);\nySolar.ticks.color = yUV.ticks.color = '#673ab7';\n \n  } \n  else {\n    \/\/ ALL VIEW: Perfect Mirror\n    ySolar.min = yUV.min = 0;\n    ySolar.max = scaleMax;\n    yUV.max = scaleMax \/ 100;\n    \n    ySolar.ticks.stepSize = solarStep;\n    yUV.ticks.stepSize = uvStep;\n    \n    ySolar.ticks.callback = (v) => v + ' W';\n    yUV.ticks.callback = (v) => v.toFixed(1);\n    \n    ySolar.ticks.color = '#888';\n    yUV.ticks.color = '#673ab7';\n\/\/ These Lines Sync solar & uv\n    ySolar.ticks.font.size = yUV.ticks.font.size = 9;\n    ySolar.ticks.font.weight = yUV.ticks.font.weight = '600';\n  }\n}\n\n\/\/ Standalone Sun Data fetch\n  async function getSunData() {\n    try {\n      const res = await fetch('https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/moon-time\/solunar-data.json');\n      if (!res.ok) throw new Error();\n      const data = await res.json();\n      const parseTime = (timeStr) => {\n        const [h, m] = timeStr.split(':');\n        const d = new Date();\n        d.setHours(h, m, 0, 0);\n        return d.getTime();\n      };\n      return { sunrise: parseTime(data.today.sunrise), sunset: parseTime(data.today.sunset) };\n    } catch (e) {\n      return { sunrise: null, sunset: null };\n    }\n  }\n\n  \/\/ 2. Main Update Function\n  async function updateSolar() {\n    const canvasEl = document.getElementById('sc-solar-canvas');\n    if (!canvasEl) return;\n\n    try {\n      \/\/ B. Setup Time Variables\n      const now = Date.now();\n      const rolling24 = now - (24 * 60 * 60 * 1000);\n      const todayStr = new Date().toLocaleString(\"en-CA\", {timeZone: \"Atlantic\/Madeira\", hour12: false}).split(',')[0];\n      const yesterdayStr = new Date(now - 86400000).toLocaleString(\"en-CA\", {timeZone: \"Atlantic\/Madeira\", hour12: false}).split(',')[0];\n\n      \/\/ 1. Precise Solar Data Fetch & 2. Fetch Data today and yesterday\n      const [sunData, resToday, resYesterday] = await Promise.all([\n        getSunData(),\n        fetch(`${stationPath}${todayStr}.json?t=${now}`).catch(() => ({ ok: false })),\n        fetch(`${stationPath}${yesterdayStr}.json?t=${now}`).catch(() => ({ ok: false }))\n      ]);\n\n      const dataToday = resToday.ok ? await resToday.json() : [];\n      const dataYest = resYesterday.ok ? await resYesterday.json() : [];\n      const rawData = [...dataYest, ...dataToday];\n      \n      let combined = rawData\n        .filter(d => d.time >= (rolling24 - 60000))\n        .filter((value, index, self) => index === self.findIndex((t) => t.time === value.time))\n        .sort((a, b) => a.time - b.time);\n\n      \/\/ PROTECTION: Prevent wipeout if fetch fails but we have history\n      if (combined.length === 0 && lastSuccessfulPoints.length > 0) return;\n\n      \/\/ Dummy point if empty\n      if (combined.length === 0) {\n        combined.push({ time: rolling24, solarRadiation: 0, uv: 0 });\n      }\n\n      const lastPoint = combined[combined.length - 1];\n      const isOffline = lastPoint && (now - lastPoint.time) > (10 * 60 * 1000);\n\n      if (isOffline) {\n        document.getElementById('sc-solar-peak-badge').innerText = `Station Offline`;\n      }\n\n      \/\/ Find Peak for the Badge\n      const peakSolar = Math.max(...combined.map(d => d.solarRadiation || 0));\n      document.getElementById('sc-solar-peak-badge').innerText = `Peak: ${Math.round(peakSolar)} W\/m\u00b2`;\n\n      \/\/ 3. CALCULATE ENERGY YIELD (Wh\/m2) with Gap Detection\n      let totalWh = 0;\n      const plotData = [];\n      \n      combined.forEach((curr, i) => {\n        const prev = combined[i - 1];\n        if (prev) {\n          const gapMs = curr.time - prev.time;\n          if (gapMs > 10 * 60 * 1000) {\n            plotData.push({ x: prev.time + 1, y: null, uv: null });\n          } else {\n            const hours = gapMs \/ 3600000;\n            const avgRad = ((curr.solarRadiation || 0) + (prev.solarRadiation || 0)) \/ 2;\n            totalWh += avgRad * hours;\n          }\n        }\n        \n        plotData.push({\n          x: curr.time,\n          y: curr.solarRadiation || 0,\n          uv: curr.uv || 0,\n          cumulative: totalWh\n        });\n      });\n\n      lastSuccessfulPoints = plotData;\n      document.getElementById('sc-solar-total-badge').innerText = `Total: ${Math.round(totalWh)} Wh\/m\u00b2`;\n\n      \/\/ 4. CHART PLUGINS\n      const nightMaskPlugin = {\n        id: 'nightMask',\n        beforeDraw: (chart) => {\n          if (!sunData.sunrise || !sunData.sunset) return; \n          const { ctx, chartArea: { top, bottom, left, right }, scales: { x } } = chart;\n          ctx.save();\n          ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';\n          const drawNight = (s, e) => {\n            const sPx = Math.max(left, x.getPixelForValue(s));\n            const ePx = Math.min(right, x.getPixelForValue(e));\n            if (sPx < ePx) ctx.fillRect(sPx, top, ePx - sPx, bottom - top);\n          };\n          drawNight(rolling24, sunData.sunrise - 86400000); \n          drawNight(sunData.sunset - 86400000, sunData.sunrise);\n          drawNight(sunData.sunset, now);\n          ctx.restore();\n        }\n      };\n\n      const ctx = canvasEl.getContext('2d');\n      if (solarChart) solarChart.destroy();\n\n      solarChart = new Chart(ctx, {\n        type: 'line',\n        data: {\n          datasets: [\n            {\n              label: 'Solar Radiation',\n              data: plotData.map(d => ({ x: d.x, y: d.y })),\n              borderColor: '#b39917',\n              backgroundColor: 'rgba(255, 193, 7, 0.09)',\n              fill: true,\n              borderWidth: 1.5,\n              pointRadius: 0,\n              yAxisID: 'ySolar',\n              tension: 0.2\n            },\n            {\n              label: 'UV Index',\n              data: plotData.map(d => ({ x: d.x, y: d.uv })),\n              borderColor: '#888cbd',\n              borderWidth: 1.5,\n              pointRadius: 0,\n              yAxisID: 'yUV',\n              zIndex: 1\n            }\n          ]\n        },\n        options: {\n\nanimations: {\n  y: {\n    type: 'number',\n    duration: 1500,\n    from: (ctx) => {\n\/\/ Dynamically pick correct scale for dataset (ySolar\/ yUV)\n      const scale = ctx.chart.scales[ctx.dataset.yAxisID];\n      if (!scale || ctx.raw === undefined) return ctx.target;\n      \n\/\/ Start at 80% of the target value for gentle 20% \"stretch\"\n      return scale.getPixelForValue(ctx.raw.y * 0.8);\n    }\n  }\n},\n\n   responsive: true,\n   maintainAspectRatio: false,\n          interaction: { mode: 'index', intersect: false },\n          plugins: {\n            legend: { display: false },\n            tooltip: {\n  enabled: isReadingMode, \/\/ Controlled by toggle\n              backgroundColor: 'rgba(44, 62, 80, 0.9)',\n              callbacks: {\n                title: (items) => formatTime(items[0].parsed.x),\n                label: (ctx) => {\n                  const d = plotData[ctx.dataIndex];\n                  if (!d) return '';\n                  if (ctx.dataset.label === 'UV Index') return `UV Index: ${d.uv.toFixed(1)}`;\n                  return [\n                    `Radiation: ${Math.round(d.y)} W\/m\u00b2`,\n                    `Energy so far: ${Math.round(d.cumulative)} Wh\/m\u00b2`\n                  ];\n                }\n              }\n            },\n            zoom: {\n              limits: { x: { min: rolling24, max: now } },\n              pan: { enabled: !isReadingMode, mode: 'x' },\n              zoom: { wheel: { enabled: true }, pinch: { enabled: true }, mode: 'x' }\n\n            }\n          },\n          scales: {\n            x: {\n              type: 'linear',\n              min: rolling24,\n              max: now,\n              ticks: { stepSize: 10 * 60 * 1000, autoSkip: true, includeBounds: true, maxTicksLimit: 10, callback: (v) => formatTime(v), font: { size: 9 } },\n              grid: { display: false }\n            },\n            ySolar: {\n              position: 'left',\n              beginAtZero: true,\n              suggestedMax: 1000,\n              ticks: { font: { size: 9, weight: '600'  }, callback: (v) => v + ' W' },\n              grid: { color: 'rgba(0,0,0,0.03)' }\n            },\n            yUV: {\n              position: 'right',\n              min: 0,\n              suggestedMax: 10,\n              ticks: { font: { size: 9, weight: '600' }, color: '#673ab7' },\n              grid: { display: false }\n            }\n          }\n        },\n        plugins: [nightMaskPlugin]\n      });\n\n\/\/ --- REFINED LEGEND LOGIC ---\n            const refreshUI = () => {\n        updateScales(solarChart);   syncLegendUI(solarChart);\n        solarChart.update();\n      };\n\n\/\/ DEFAULT VIEW: Solar only (index 0)\n      solarChart.setDatasetVisibility(0, true);\n      solarChart.setDatasetVisibility(1, false);\n      refreshUI();\n\n \/\/ 1. ALL Click: Show both\n      document.getElementById('leg-all-solar').onclick = () => {\n        solarChart.setDatasetVisibility(0, true);\n        solarChart.setDatasetVisibility(1, true);\n        refreshUI();\n      };\n\n\/\/ 2. Solar Click: Show Solar, Hide UV\n      document.getElementById('leg-solar').onclick = () => {\n        solarChart.setDatasetVisibility(0, true);\n        solarChart.setDatasetVisibility(1, false);\n        refreshUI();\n      };\n\n\/\/ 3. UV Click: Show UV, Hide Solar\n      document.getElementById('leg-uv').onclick = () => {\n        solarChart.setDatasetVisibility(0, false);\n        solarChart.setDatasetVisibility(1, true);\n        refreshUI();\n      };\n\n      autoHideTooltip(solarChart);\n\n    } catch (e) { console.error(\"Solar Error:\", e); }\n  }\n\n \/\/ Auto hide Tooltip function\n  function autoHideTooltip(chart, delay = 5000) {\n    let hideTimer;\n    const clearTooltip = () => {\n      if (chart) {\n        chart.setActiveElements([]);\n        if (chart.tooltip) {\n          chart.tooltip.setActiveElements([], { x: 0, y: 0 });\n        }\n        chart.update();\n      }\n    };\n    chart.canvas.addEventListener('touchstart', () => clearTimeout(hideTimer));\n    chart.canvas.addEventListener('touchend', () => {\n      clearTimeout(hideTimer);\n      hideTimer = setTimeout(clearTooltip, delay);\n    });\n  }\n  \/\/ Handle Mode Toggle\n  document.getElementById('sl-toggle-mode').addEventListener('click', (e) => {\n    isReadingMode = !isReadingMode;\n    e.target.textContent = isReadingMode ? \"READING Mode ACTIVE\" : \"ZOOM\/PAN Mode ACTIVE\";\n    if (solarChart) {\n      solarChart.options.plugins.tooltip.enabled = isReadingMode;\n      solarChart.options.plugins.zoom.pan.enabled = !isReadingMode;\n      solarChart.update('none');\n    }\n  });\n\n  \/\/ Handle Reset View\n  document.getElementById('sl-reset-view').addEventListener('click', () => {\n    if (solarChart) solarChart.resetZoom();\n  });\n\n  updateSolar();\n  setInterval(updateSolar, 300000);\n})();\n<\/script>\n\n\n\n<!-- 24h rain evolution graph with daily totals - added zoom 20-05-26 -->\n\n<style>\n  \/* To allow chart Zooming *\/\n  #sc-rain-canvas {\n    touch-action: pan-y;\n  }\n\n  \/* Zoom Button Styling *\/\n  .nav-btn { \n    border: none; \n    background: #eee; \n    padding: 5px 12px; \n    border-radius: 20px; \n    font-size: 11px; \n    font-weight: 700; \n    cursor: pointer; \n    color: #666; \n    transition: 0.2s; \n  }\n  .nav-btn:focus { outline: none; box-shadow: none; }\n\n  .sc-rain-cockpit {\n    font-family: 'Segoe UI', Roboto, Arial, sans-serif;\n    max-width: 500px;\n    margin: 20px auto;\n    background: rgba(0,0,0,0.02);\n    border: 1px solid #eee;\n    border-radius: 12px;\n    padding: 15px;\n    color: #333;\n  }\n  .sc-rain-label {\n    font-size: 11px;\n    font-weight: 700;\n    color: #888;\n    margin-bottom: 8px;\n    letter-spacing: 1px;\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n  }\n  .sc-rain-badge-clean {\n    color: #185e96;\n    font-weight: 800;\n    font-size: 11px;\n  }\n  .sc-rain-wrapper { \n    position: relative; \n    height: 200px; \n    margin-top: 15px; \/* Space for the top labels *\/\n  }\n  .sc-axis-label {\n    position: absolute;\n    top: -12px;\n    font-size: 10px;\n    font-weight: 800;\n    z-index: 10;\n  }\n  .label-left { left: 0; margin-top: 0; color: #4caf50; }\n \/* .label-right { right: 0; color: #185e96; } *\/\n\n<\/style>\n\n<div class=\"sc-rain-cockpit\">\n  <div class=\"sc-rain-label\">\n    <span id=\"sc-rain-title-toggle\" style=\"cursor: pointer; user-select: none;\">24h Rain Evolution<\/span>\n\n    <span id=\"sc-rain-total-badge\" class=\"sc-rain-badge-clean\">Total Today: 0.0 mm<\/span>\n  <\/div>\n  <div class=\"sc-rain-wrapper\">\n    <div class=\"sc-axis-label label-left\">mm\/h<\/div>\n    <canvas id=\"sc-rain-canvas\"><\/canvas>\n  <\/div>\n<\/div>\n\n<div class=\"sc-controls\" style=\"display: flex; justify-content: center; gap: 20px; margin-top: -10px; padding-bottom: 15px;\">\n  <button id=\"rn-toggle-mode\" class=\"nav-btn\" style=\"background: #e8f4ff; border: 1px solid #185e96;\">READING Mode ACTIVE<\/button>\n  <button id=\"rn-reset-view\" class=\"nav-btn\" style=\"background: none; border: 1px solid #ccc;\">RESET View<\/button>\n<\/div>\n\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js\"><\/script>\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/hammerjs@2.0.8\"><\/script>\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chartjs-plugin-zoom@2.2.0\"><\/script>\n\n<script>\n(function() {\n  let rainChart;\n  let isReadingMode = true; \/\/ Added Zoom master switch\n  const stationPath = 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/santacruz\/';\n\n  let lastSuccessfulPoints = []; \/\/ THIS SAVES the HISTORY\n\n  const formatTime24h = (ts) => {\n    const d = new Date(ts);\n    return d.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', hour12: false });\n  };\n\n  \/\/ Auto hide Tooltip function\n  function autoHideTooltip(chart, delay = 5000) {\n    let hideTimer;\n    const clearTooltip = () => {\n      if (chart) {\n        \/\/ Clears the highlight dot from the line\n        chart.setActiveElements([]);\n        if (chart.tooltip) {\n          \/\/ Hides the tooltip box\n          chart.tooltip.setActiveElements([], { x: 0, y: 0 });\n        }\n        chart.update();\n      }\n    };\n    chart.canvas.addEventListener('touchstart', () => {\n      clearTimeout(hideTimer);\n    });\n    chart.canvas.addEventListener('touchend', () => {\n      clearTimeout(hideTimer);\n      hideTimer = setTimeout(clearTooltip, delay);\n    });\n  }\n\n  \/\/ Swapped external API for local Solunar JSON\n  async function getSunData() {\n    const res = await fetch('https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/moon-time\/solunar-data.json');\n    const data = await res.json();\n    \n    \/\/ Helper to turn \"08:05\" into  timestamp for today\n    const parseTime = (timeStr) => {\n      const [h, m] = timeStr.split(':');\n      const d = new Date();\n      d.setHours(h, m, 0, 0);\n      return d.getTime();\n    };\n\n    return {\n      sunrise: parseTime(data.today.sunrise),\n      sunset: parseTime(data.today.sunset)\n    };\n  }\n\n  async function initRainWidget() {\n    try {\n      const now = Date.now();\n      const cacheBust = `?t=${now}`;\n      const twentyFourHoursAgo = now - 86400000;\n      const d = new Date(now);\n      const todayStr = d.toLocaleDateString(\"en-CA\", {timeZone: \"Atlantic\/Madeira\"});\n      d.setDate(d.getDate() - 1);\n      const yesterdayStr = d.toLocaleDateString(\"en-CA\", {timeZone: \"Atlantic\/Madeira\"});\n\n      \/\/ 1. FETCH DATA (Summary for Ladder, Raw for Rate +Sun Data)\n      const [sunResults, resSumToday, resSumYest, resRawToday, resRawYest] = await Promise.all([\n        getSunData(),\n        fetch(`${stationPath}${todayStr}.summary.json${cacheBust}`).catch(() => ({ ok: false })),\n        fetch(`${stationPath}${yesterdayStr}.summary.json${cacheBust}`).catch(() => ({ ok: false })),\n        fetch(`${stationPath}${todayStr}.json${cacheBust}`).catch(() => ({ ok: false })),\n        fetch(`${stationPath}${yesterdayStr}.json${cacheBust}`).catch(() => ({ ok: false }))\n      ]);\n\n      \/\/ Handle data or defaults without early return\n      const sToday = resSumToday.ok ? await resSumToday.json() : { stats: { rain_total: 0 } };\n      const sYesterday = resSumYest.ok ? await resSumYest.json() : { stats: { rain_total: 0 } };\n      const rawToday = resRawToday.ok ? await resRawToday.json() : [];\n      const rawYest = resRawYest.ok ? await resRawYest.json() : [];\n\n      \/\/ PROTECTION: Only blocks updates if we have a history to protect (prevents wipeout)\n      if (rawToday.length === 0 && lastSuccessfulPoints.length > 0) return;\n\n      \/\/ sunResults from our server\n      const sunrise = sunResults.sunrise;\n      const sunset = sunResults.sunset;\n\n      \/\/ 2. MAP CHART POINTS (THE LADDER LOGIC)\n      const initialRaw = [...rawYest, ...rawToday]\n        .filter(d => d.time >= (twentyFourHoursAgo - 60000))\n        .sort((a, b) => a.time - b.time);  \/\/ Ensure chronological order\n\n      \/\/ --- GAP DETECTOR START ---\n      const combinedRaw = [];\n      const GAP_THRESHOLD = 10 * 60 * 1000; \/\/ 10 minutes\n\n      initialRaw.forEach((d, i) => {\n        if (i > 0 && (d.time - initialRaw[i - 1].time) > GAP_THRESHOLD) {\n          \/\/ Place null when no data\n          combinedRaw.push({ \n            time: d.time - 1, \n            precipRate: null, \n            precipLastHour: null \n          });\n        }\n        combinedRaw.push(d);\n      });\n      \/\/ --- GAP DETECTOR END ---\n\n      \/\/ Safe update in case no new data exists on the json, Ensure we have at least one point so the map function works\n      if (combinedRaw.length === 0) {\n        combinedRaw.push({ time: twentyFourHoursAgo, precipRate: { mm: 0 }, precipLastHour: { mm: 0 } });\n      }\n\n      const lastPoint = combinedRaw[combinedRaw.length - 1];\n      \/\/ If the station is silent, bridge the gap to \"Now\"\n      if (lastPoint && lastPoint.precipRate !== null && (now - lastPoint.time) > (1 * 60 * 1000)) {\n        combinedRaw.push({ ...lastPoint, time: now, precipRate: { mm: 0 } });\n      }\n\n      \/\/ 3. Calculate Sum of Rates to create distribution weights\n      \/\/ Check to prevent division by zero during data gaps\n      const sumRatesToday = rawToday.reduce((acc, d) => acc + (d.precipRate.mm || 0), 0);\n      const sumRatesYest = rawYest.reduce((acc, d) => acc + (d.precipRate.mm || 0), 0);\n\n      let runningTotalToday = 0;\n      let runningTotalYest = 0;\n\n      const chartPoints = combinedRaw.map(d => {\n        const pointDateStr = new Date(d.time).toLocaleDateString(\"en-CA\", {timeZone: \"Atlantic\/Madeira\"});\n        const isToday = pointDateStr === todayStr;\n        let displayY = 0;\n\n        \/\/ If it's a gap point (null), return null for the chart\n        if (d.precipRate === null) {\n          return { x: d.time, y: null, rate: null, lastHour: null };\n        }\n\n        if (isToday) {\n          \/\/ If it rained today, add the summary total based on this point's rate\n          if (sumRatesToday > 0) {\n            const contribution = (d.precipRate.mm \/ sumRatesToday) * sToday.stats.rain_total;\n            runningTotalToday += contribution;\n          } else {\n            \/\/ If no rates are recorded yet, we just show the total known rain as a flat line\n            runningTotalToday = sToday.stats.rain_total;\n          }\n          displayY = runningTotalToday;\n        } else {\n          \/\/ Same logic for Yesterday portion of the 24h graph\n          if (sumRatesYest > 0) {\n            const contribution = (d.precipRate.mm \/ sumRatesYest) * sYesterday.stats.rain_total;\n            runningTotalYest += contribution;\n          } else {\n            runningTotalYest = sYesterday.stats.rain_total;\n          }\n          displayY = runningTotalYest;\n        }\n\n        return {\n          x: d.time,\n          y: displayY, \n          rate: d.precipRate.mm,\n          lastHour: d.precipLastHour ? d.precipLastHour.mm : 0\n        };\n      });\n\n      \/\/ LOCK IN HISTORY\n      lastSuccessfulPoints = chartPoints;\n\n      \/\/ 4. UPDATE BADGE\n      document.getElementById('sc-rain-total-badge').innerText = `Total Today: ${sToday.stats.rain_total.toFixed(1)} mm`;\n\n      \/\/ 5. CHART RENDER\n      const ctx = document.getElementById('sc-rain-canvas').getContext('2d');\n      if (rainChart) rainChart.destroy();\n\n      rainChart = new Chart(ctx, {\n        type: 'line',\n        data: {\n          datasets: [\n            {\n              label: 'Rain Rate',\n              data: chartPoints.map(d => ({ x: d.x, y: d.rate })),\n              borderColor: 'rgba(76, 175, 80, 0.4)',\n              backgroundColor: 'rgba(76, 175, 80, 0.08)',\n              fill: true,\n              pointRadius: 0,\n              borderWidth: 1,\n              stepped: true,\n              spanGaps: false,\n              yAxisID: 'yRate'\n            },\n            {\n              label: 'Daily Accumulation',\n              data: chartPoints.map(d => ({ x: d.x, y: d.y })),\n              borderColor: '#185e96',\n              backgroundColor: 'rgba(24, 94, 150, 0.1)',\n              fill: true,\n              pointRadius: 0,\n              borderWidth: 2,\n              stepped: 'before', \/\/ THIS IS THE KEY FOR THE LADDER graph print\n              tension: 0.1, \/\/ Slight tension for smooth climb\n              spanGaps: false,\n              yAxisID: 'y'\n            }\n          ]\n        },\n        options: {\n          responsive: true,\n          maintainAspectRatio: false,\n          interaction: { mode: 'index', intersect: false },\n          plugins: {\n            legend: { display: false },\n            tooltip: {\n              enabled: isReadingMode, \/\/ Controlled by toggle\n              backgroundColor: 'rgba(44, 62, 80, 0.9)',\n              callbacks: {\n                title: (items) => `${formatTime24h(items[0].parsed.x)}`,\n                label: (context) => {\n                  if (context.dataset.label === 'Rain Rate' || context.parsed.y === null) return null;\n                  const rawPoint = chartPoints[context.dataIndex];\n                  return [\n                    `Daily Total: ${context.parsed.y.toFixed(1)} mm`,\n                    `Last Hour: ${rawPoint.lastHour.toFixed(1)} mm`,\n                    `Current Rate: ${rawPoint.rate.toFixed(1)} mm\/h`\n                  ];\n                }\n              }\n            },\n            zoom: {\n              limits: { x: { min: twentyFourHoursAgo, max: now } },\n              pan: { enabled: !isReadingMode, mode: 'x' },\n              zoom: { wheel: { enabled: true }, pinch: { enabled: true }, mode: 'x' }\n            }\n          },\n          scales: {\n            x: { \n              type: 'linear', \n              min: twentyFourHoursAgo, \n              max: now, \n              ticks: { font: { size: 9 }, callback: (v) => formatTime24h(v), autoSkip: true, includeBounds: true, maxTicksLimit: 10, stepSize: 10 * 60 * 1000 },\n              grid: { display: false } \n            },\n            \/\/ Graph legends rate and total\n            yRate: { \n              type: 'linear',\n              position: 'left',\n              beginAtZero: true, \n              suggestedMax: 7, \/\/ For better visual consistency \n              display: true,\n              ticks: { \n                font: { size: 10, weight: 600 }, \n                color: '#4caf50', \/\/ Matching line color\n                autoSkip: false\n              }, \n              grid: { display: false } \n            },\n            y: { \n              type: 'linear',\n              position: 'right',\n              beginAtZero: true, \n              grace: '33%',\n              suggestedMax: 3, \/\/ Keeps low totals from filling the whole height\n              ticks: { \n                font: { size: 10, weight: 600 }, \n                color: '#185e96', \/\/ Matching line color \n                callback: (v) => v.toFixed(1) \/\/ Just the number\n              }, \n              grid: { color: 'rgba(0,0,0,0.05)' } \n            }\n          }\n        },\n        plugins: [{\n          id: 'solarBackground',\n          beforeDraw: (chart) => {\n            const { ctx, chartArea: { top, bottom, left, right }, scales: { x } } = chart;\n            ctx.save();\n            ctx.fillStyle = 'rgba(0, 0, 0, 0.08)';\n            ctx.fillRect(left, top, right - left, bottom - top);\n            const drawSun = (s, e) => {\n              const sPx = Math.max(left, x.getPixelForValue(s));\n              const ePx = Math.min(right, x.getPixelForValue(e));\n              if (sPx < ePx) { ctx.fillStyle = 'rgba(255, 255, 255, 0.6)'; ctx.fillRect(sPx, top, ePx - sPx, bottom - top); }\n            };\n            drawSun(sunrise - 86400000, sunset - 86400000);\n            drawSun(sunrise, sunset);\n            ctx.restore();\n          }\n        }]\n      });\n\n      autoHideTooltip(rainChart);\n\n    } catch (e) { console.error(\"Rain Sync Error:\", e); }\n  }\n\n  initRainWidget();\n  setInterval(initRainWidget, 60000);\n\n  \/\/ --- NEW ZOOM CONTROLS (MOVED OUTSIDE) ---\n  \/\/ Placing these outside ensures they are attached only once, preventing toggle duplication.\n  document.getElementById('rn-toggle-mode').addEventListener('click', (e) => {\n    isReadingMode = !isReadingMode;\n    e.target.textContent = isReadingMode ? \"READING Mode ACTIVE\" : \"ZOOM\/PAN Mode ACTIVE\";\n    if (rainChart) {\n      rainChart.options.plugins.tooltip.enabled = isReadingMode;\n      rainChart.options.plugins.zoom.pan.enabled = !isReadingMode;\n      rainChart.update('none');\n    }\n  });\n\n  document.getElementById('rn-reset-view').addEventListener('click', () => {\n    if (rainChart) rainChart.resetZoom();\n  });\n\n  \/\/ --- MINIMAL TRANSPARENCY TOGGLE ---\n  document.getElementById('sc-rain-title-toggle').addEventListener('click', () => {\n    if (!rainChart) return;\n\n    const accumulationDataset = rainChart.data.datasets[1];\n    \n    \/\/ Check if it is currently visible by looking at the border color\n    const isHidden = accumulationDataset.borderColor === 'transparent';\n\n    if (isHidden) {\n      \/\/ Restore original colors\n      accumulationDataset.borderColor = '#185e96';\n      accumulationDataset.backgroundColor = 'rgba(24, 94, 150, 0.1)';\n    } else {\n      \/\/ Make the line and fill invisible, but keep data \"alive\"\n      accumulationDataset.borderColor = 'transparent';\n      accumulationDataset.backgroundColor = 'transparent';\n    }\n\n    rainChart.update('none'); \/\/ 'none' ensures the change is instant and stable\n  });\n\n})();\n<\/script>\n\n\n\n<!-- Daily rain totals, comparing our Filtered calculation with Wunderground -->\n<div style=\"text-align: center;\">\n<div id=\"rain-compare\" style=\"font-size: 0.80em; font-family: monospace; padding: 8px; border: 1px solid #ccc; max-width: 420px;\">\n  <div style=\"font-size: 0.85em;\"><strong>&#x1f327; Rain comparison (today)<\/strong><\/div>\n  <div id=\"ourRain\">Our calc. total: \u2026<\/div>\n  <div id=\"wgRain\">Wunderground total: \u2026<\/div>\n  <div id=\"rainTime\" style=\"font-size: 0.70em; color: #666; margin-top: 6px;\"><\/div>\n<\/div>\n<\/div>\n\n<script>\nasync function updateRainComparison() {\n  try {\n    \/\/ 1. Get today's date in YYYY-MM-DD format (Madeira\/Portugal time)\n    const today = new Date().toLocaleDateString('sv-SE', { timeZone: 'Atlantic\/Madeira', hour12: false });\n    \n    \/\/ 2. Build dynamic URLs\n    const baseUrl = \"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/santacruz\/\";\n    const summaryUrl = `${baseUrl}${today}.summary.json`;\n    const rawUrl     = `${baseUrl}${today}.json`;\n\n    const [summaryRes, rawRes] = await Promise.all([\n      fetch(summaryUrl),\n      fetch(rawUrl)\n    ]);\n\n    if (!summaryRes.ok || !rawRes.ok) throw new Error(\"Files not found for today\");\n\n    const summaryData = await summaryRes.json();\n    const rawData     = await rawRes.json();\n\n    \/\/ 3. Extracting the specific fields based on your JSON format\n    \/\/ Summary uses stats.rain_total\n    const ourTotal = summaryData.stats?.rain_total ?? \"--\";\n    \n    \/\/ Wunderground raw file is an array; we get precipSinceMidnight.mm from the last entry\n    const lastEntry = Array.isArray(rawData) ? rawData[rawData.length - 1] : rawData;\n    const wgTotal = lastEntry.precipSinceMidnight?.mm ?? \"--\";\n\n    \/\/ 4. Update UI\n    document.getElementById(\"ourRain\").textContent =\n      `Our calc. total: ${ourTotal} mm`;\n\n    document.getElementById(\"wgRain\").textContent =\n      `Wunderground total: ${wgTotal} mm`;\n\n    document.getElementById(\"rainTime\").textContent =\n  \"Last update: \" + new Date().toLocaleTimeString('en-GB', { \n    hour: '2-digit', \n    minute: '2-digit', \n    hour12: false \n  });\n\n  } catch (err) {\n    document.getElementById(\"rainTime\").textContent =\n      \"Rain data not yet available for today\";\n    console.error(\"Rain comparison error:\", err);\n  }\n}\n\n\/\/ Initial load and 5-minute refresh\nupdateRainComparison();\nsetInterval(updateRainComparison, 5 * 60 * 1000);\n\n<\/script>\n\n\n\n<!-- 24h pressure evolution graph - changed from 12h 23\/05\/26 -->\n<div class=\"sc-pro-cockpit\" style=\"margin-top: 20px; font-family: 'Inter', sans-serif;\">\n  <div class=\"sc-legend\" style=\"display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;\">\n    <div style=\"display: flex; gap: 11px; font-size: 11px; font-weight: 600; color: #555;\">\n      <div style=\"display: flex; align-items: center; gap: 4px;\">\n        24h &nbsp;<span style=\"width: 12px; height: 3px; background: #2c3e50; border-radius: 2px;\"><\/span> Pressure trend (hPa)\n      <\/div>\n    <\/div>\n    <div id=\"pressure-trend-val\" style=\"font-size: 11px; font-weight: 600; color: #2c3e50;\">&#8212;<\/div>\n  <\/div>\n\n  <div class=\"sc-chart-wrapper\" style=\"height: 180px; position: relative;\">\n    <canvas id=\"sc-12h-pressure-chart\"><\/canvas>\n  <\/div>\n<\/div>\n\n<script>\n(function() {\n  let pressureChart;\n \/\/ The base directory\n  const baseDir = 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/santacruz\/';\n\n\/\/ Auto hide Tooltip function\nfunction autoHideTooltip(chart, delay = 5000) {\n  let hideTimer;\n  const clearTooltip = () => {\n    if (chart) {\n\/\/ Clear highlight dot from line\n      chart.setActiveElements([]);\n      if (chart.tooltip) {\n  \/\/ Hides the tooltip box\n        chart.tooltip.setActiveElements([], { x: 0, y: 0 });\n      }\n      chart.update();\n    }\n  };\n\nchart.canvas.addEventListener('touchstart', () => {\n    clearTimeout(hideTimer);\n  });\n  chart.canvas.addEventListener('touchend', () => {\n    clearTimeout(hideTimer);\n    hideTimer = setTimeout(clearTooltip, delay);\n  });\n}\n\n \/\/ For human-readable time\n  const timeFormatter = (val) => {\n  return new Date(val).toLocaleTimeString(\"en-GB\", {\n    timeZone: \"Atlantic\/Madeira\",\n    hour: \"2-digit\",\n    minute: \"2-digit\"\n  });\n};\n\n  async function updatePressure() {\n    try {\n      const now = Date.now();\n      const cacheBust = '?t=' + now; \/\/ Ensuring cache busting is defined\n      const twelveHoursAgo = now - (24 * 60 * 60 * 1000);\n\n\/\/ --- Solunar Sync from our json to paint bg day\/night ---\n      const solunarRes = await fetch('https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/moon-time\/solunar-data.json');\n      const solunarData = await solunarRes.json();\n\n      const parseTime = (timeStr) => {\n        const [h, m] = timeStr.split(':');\n        const d = new Date();\n        d.setHours(h, m, 0, 0);\n        return d.getTime();\n      };\n\n      const sunrise = parseTime(solunarData.today.sunrise);\n      const sunset = parseTime(solunarData.today.sunset);\n      \nconst getFileUrl = (date) => {\n  const dStr = date.toLocaleDateString(\"en-CA\", {timeZone: \"Atlantic\/Madeira\"});\n  return baseDir + `${dStr}.json`;\n};\n\n      const resToday = await fetch(getFileUrl(new Date()) + cacheBust);\n      let combinedData = await resToday.json();\n      \n      try {\n        const yesterday = new Date();\n        yesterday.setDate(yesterday.getDate() - 1);\n        const resYesterday = await fetch(getFileUrl(yesterday) + cacheBust);\n        const yesterdayData = await resYesterday.json();\n        combinedData = [...yesterdayData, ...combinedData];\n      } catch(e) {}\n\n      const windowData = combinedData\n  .filter(d => d.time > (twelveHoursAgo - 10000))\n  .filter((value, index, self) => \n    index === self.findIndex((t) => t.time === value.time)\n  );\n\n      if (windowData.length === 0) return;\n\n\/\/ --- GAP DETECTOR START ---\n      const processedPressureData = [];\n      const GAP_THRESHOLD = 10 * 60 * 1000; \/\/ 10 minutes\n\n windowData.forEach((d, i) => {\n        if (i > 0 && (d.time - windowData[i - 1].time) > GAP_THRESHOLD) {\n\/\/ Place null to stop the line\n          processedPressureData.push({ \n            time: d.time - 1, \n            pressure: null \n          });\n        }\n        processedPressureData.push(d); \n      });\n\/\/ --- GAP DETECTOR END ---\n\n      const latestP = windowData[windowData.length - 1].pressure?.hPa;\n      const twelveHrsAgo = now - (24 * 60 * 60 * 1000);\n      const oldP = windowData.find(d => d.time > twelveHrsAgo)?.pressure?.hPa;\n      \n      if (latestP && oldP) {\n        const diff = (latestP - oldP).toFixed(1);\n        let sign = '\u25ba '; \/\/ Default to no arrow or front facing one\n\n        if (diff > 0) {\n          sign = '\u25b2 + ';\n        } else if (diff < 0) {\n          sign = '\u25bc ';\n        }\n\/\/ If diff is \"0.0\", sign shows front facing arrow or no arrow\n        document.getElementById('pressure-trend-val').innerText = `${sign}${diff} hPa `;\n      }\n\n      const ctx = document.getElementById('sc-12h-pressure-chart').getContext('2d');\n      if (pressureChart) pressureChart.destroy();\n\n      const values = windowData.map(d => d.pressure?.hPa).filter(v => v != null);\n      const minP = Math.min(...values);\n      const maxP = Math.max(...values);\n\n   \/\/ THE SOLAR PLUGIN\n      const solarBackgroundPlugin = {\n        id: 'solarBackground',\n        beforeDraw: (chart) => {\n          const { ctx, chartArea: { top, bottom, left, right }, scales: { x } } = chart;\n          function drawSection(startTime, endTime, color) {\n            const startPx = Math.max(left, x.getPixelForValue(startTime));\n            const endPx = Math.min(right, x.getPixelForValue(endTime));\n            if (startPx < endPx) {\n              ctx.save();\n              ctx.fillStyle = color;\n              ctx.fillRect(startPx, top, endPx - startPx, bottom - top);\n              ctx.restore();\n            }\n          }\n          ctx.save();\n          ctx.fillStyle = 'rgba(0, 0, 0, 0.06)'; \n          ctx.fillRect(left, top, right - left, bottom - top);\n          const yesterdaySunrise = sunrise - 86400000;\n          const yesterdaySunset = sunset - 86400000;\n          drawSection(yesterdaySunrise, yesterdaySunset, 'rgba(255, 255, 255, 0.5)');\n          drawSection(sunrise, sunset, 'rgba(255, 255, 255, 0.5)');\n          ctx.restore();\n        }\n      };\n\n      pressureChart = new Chart(ctx, {\n        type: 'line',\n        data: {\n          datasets: [{\n            data: processedPressureData.map(d => ({x: d.time, y: d.pressure ? d.pressure.hPa : null})),\n            borderColor: '#2c3e50',\n            backgroundColor: 'rgba(44, 62, 80, 0.04)',\n            borderWidth: 2,\n            pointRadius: 0,\n \/\/ Creates a softer print        cubicInterpolationMode: 'monotone', \n            tension: 0.4,\n            fill: true,\n            spanGaps: false\n          }]\n        },\n        options: {\n          responsive: true,\n          maintainAspectRatio: false,\n          interaction: { mode: 'index', intersect: false },\n          plugins: { \n            legend: { display: false },\n            tooltip: {\n                enabled: true,\n                backgroundColor: 'rgba(44, 62, 80, 0.9)',\n                titleFont: { size: 11 },\n                bodyFont: { size: 12, weight: '600' },\n                padding: 10,\n                displayColors: false,\n           callbacks: {\n       title: (items) => timeFormatter(items[0].parsed.x),\n       label: (ctx) => `${ctx.parsed.y.toFixed(1)} hPa`\n                }\n            }\n          },\n          scales: {\n            x: {\n          type: 'linear',\n          min: twelveHoursAgo,\n              max: now,\n              ticks: { \n stepSize: 2 * 60 * 60 * 1000, \n                callback: (v) => {\n      return new Date(v).toLocaleTimeString(\"en-GB\", {\n                    timeZone: \"Atlantic\/Madeira\",\n                    hour: \"2-digit\",\n                    minute: \"2-digit\",\n                    hour12: false\n                  });\n                },\n                font: { size: 10 },\n                maxRotation: 0,\n autoSkip: true,\n                padding: 10 \/\/ Added space for readability\n              },\n              grid: { color: 'rgba(0,0,0,0.03)', drawBorder: false }\n            },\n            y: {\n              type: 'linear',\n     suggestedMin: minP - 1,\n     suggestedMax: maxP + 1,\n          ticks: { \nfont: { size: 10, weight: '600' },\nprecision: 0, \nstepSize: 1,\n callback: (v) => v.toFixed(0),\n                padding: 8\n              },\n              grid: { color: 'rgba(0,0,0,0.06)', borderDash: [4, 4], drawBorder: false }\n            }\n          }\n        },\n        plugins: [solarBackgroundPlugin]\n      });\n      autoHideTooltip(pressureChart);\n    } catch (e) { console.error(\"Pressure Error:\", e); }\n  }\n\n  updatePressure();\n  setInterval(updatePressure, 60000);\n})();\n<\/script>\n\n\n\n<!-- Daily Summary card max, min values. Day picker option, Year & Month views -->\n<style>\n  :root {\n    --bg-smoke: rgba(0, 0, 0, 0.03);\n    --border-soft: #eaeaea;\n    --text-deep: #333;\n    --text-muted: #888;\n    --accent-glow: #185e96;\n    --accent-heat: #e67e22;\n    --accent-cold: #3498db;\n  }\n\n  .sc-history-card {\n    font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n    background: var(--bg-smoke);\n    border: 1px solid var(--border-soft);\n    border-radius: 8px;\n    padding: 15px 20px;\n    max-width: 400px;\n    margin: 20px auto;\n    color: var(--text-deep);\n    box-shadow: 0 2px 10px rgba(0,0,0,0.03);\n  }\n\n  .sc-nav { display: flex; gap: 8px; margin-bottom: 15px; justify-content: center; }\n  .sc-btn { border: none; background: #e8e8e8; padding: 4px 12px; border-radius: 20px; font-size: 10px; font-weight: 700; cursor: pointer; color: #777; transition: 0.2s; outline:none; text-transform: uppercase; }\n  .sc-btn.active { background: #3b5875; color: #fff; }\n\n  .sc-history-header {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    padding: 0 0px 8px 0px;\n    margin-bottom: 12px;\n    border-bottom: 1px solid #f0f0f0;\n  }\n\n  \/* New Date Navigation Styles *\/\n  .sc-date-nav { display: flex; align-items: center; gap: 5px; }\n  .date-arrow { \n    background: none; border: none; color: #aaa; cursor: pointer; \n    font-size: 14px; padding: 0 5px; transition: 0.2s; \n  }\n  .date-arrow:hover { color: var(--accent-glow); }\n  #h-date { cursor: pointer; position: relative; }\n  #date-picker-input { \n    position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;\n  }\n\n  .sc-history-grid {\n    display: grid;\n    grid-template-columns: 1fr 1fr;\n    gap: 10px;\n  }\n\n  .sc-h-box {\n    background: rgba(255, 255, 255, 0.5);\n    border-radius: 8px;\n    padding: 12px;\n    display: flex;\n    flex-direction: column;\n    border: 1px solid rgba(0,0,0,0.01);\n    transition: all 0.2s ease;\n  }\n\n  .sc-h-label {\n    font-size: 11px;\n    font-weight: 600;\n    text-transform: uppercase;\n    color: var(--text-muted);\n    margin-bottom: 6px;\n  }\n\n  .sc-h-main {\n    font-size: 18px;\n    font-weight: 700;\n    color: var(--text-deep);\n    display: flex;\n    flex-wrap: nowrap;\n    align-items: baseline;\n  }\n\n  .sc-h-footer {\n    margin-top: auto;\n    font-size: 11px;\n    color: var(--text-muted);\n    display: flex;\n    justify-content: space-between;\n    padding-top: 5px;\n    border-top: 1px solid rgba(0,0,0,0.03);\n  }\n\n  .sc-h-wide { grid-column: span 2; }\n  .v-sep { color: rgba(171, 171, 171,0.5); font-weight: 400; margin: 0 8px; }\n  .sc-h-unit { font-size: 10px; font-weight: 400; color: #666; margin-left: 2px; }\n  .hi-val { color: var(--accent-heat); }\n  .lo-val { color: var(--accent-cold); }\n  .trend-arrow { display: flex; align-items: center; justify-content: center; width: 16px; height: 16px; color: #888; margin: 0 8px; }\n\n  #solar-estimator {\n    display: none;\n    grid-column: span 2;\n    background: #fff;\n    padding: 12px;\n    border-radius: 8px;\n    font-size: 11px;\n    border: 1px dashed #d1d1d1;\n    margin-top: 5px;\n    box-shadow: inset 0 2px 4px rgba(0,0,0,0.02);\n  }\n  .est-row { display: flex; justify-content: space-between; margin-bottom: 6px; border-bottom: 1px dotted #eee; padding-bottom: 4px;}\n\n  #solar-insight-box {\n    margin-top: 10px;\n    padding: 12px;\n    background: #fffdf5;\n    border-radius: 6px;\n    border: 1px solid #f9f3db;\n    color: #7f8c8d;\n    font-style: italic;\n    line-height: 1.4;\n    cursor: pointer;\n    user-select: none;\n    position: relative;\n  }\n  #solar-insight-box:hover { background: #fff9e6; }\n  #solar-insight-box::after { content: \"\u203a\"; position: absolute; right: 10px; top: 50%; transform: translateY(-50%); font-size: 18px; color: #d4af37; font-weight: bold; font-style: normal;}\n\n  .val-pair { display: inline-flex; align-items: baseline; white-space: nowrap; }\n<\/style>\n\n<div class=\"sc-history-card\">\n  <div class=\"sc-nav\">\n    <button onclick=\"changeView('day')\" id=\"btn-day\" class=\"sc-btn active\">Today<\/button>\n    <button onclick=\"changeView('month')\" id=\"btn-month\" class=\"sc-btn\">Month<\/button>\n    <button onclick=\"changeView('year')\" id=\"btn-year\" class=\"sc-btn\">Year<\/button>\n  <\/div>\n\n  <div class=\"sc-history-header\">\n    <span id=\"h-title\" style=\"font-size: 14px; font-weight: 600; color: #555;\">Santa Cruz DAILY Summary<\/span>\n    <div class=\"sc-date-nav\">\n      <button onclick=\"shiftDate(-1)\" class=\"date-arrow\" id=\"prev-date\">\u276e<\/button>\n      <input type=\"date\" id=\"date-picker-input\" onchange=\"setDate(this.value)\">\n      <span id=\"h-date\" onclick=\"document.getElementById('date-picker-input').showPicker()\" style=\"font-size: 13px; font-weight: 600; color: #333;\">&#8211;\/&#8211;\/&#8212;-<\/span>\n      <button onclick=\"shiftDate(1)\" class=\"date-arrow\" id=\"next-date\">\u276f<\/button>\n    <\/div>\n  <\/div>\n\n  <div class=\"sc-history-grid\">\n    <div class=\"sc-h-box sc-h-wide\">\n      <span class=\"sc-h-label\">Max Windspeed and Gust<\/span>\n      <div class=\"sc-h-main\">\n        <span id=\"h-wind-max\">&#8212;<\/span><span class=\"sc-h-unit\">km\/h<\/span>\n        <span class=\"v-sep\">\/<\/span>\n        <span class=\"hi-val\" id=\"h-gust-max\">&#8212;<\/span><span class=\"sc-h-unit\">km\/h<\/span>\n      <\/div>\n      <div class=\"sc-h-footer\">\n        <span id=\"avg-wind-label\">Avg <b id=\"h-wind-avg\">&#8212;<\/b><span class=\"sc-h-unit\">km\/h<\/span><\/span>\n        <span>Daytime Dominant Dir <b id=\"h-domin-wind\" style=\"color:var(--accent-glow)\">&#8212;<\/b><\/span>\n      <\/div>\n    <\/div>\n\n    <div class=\"sc-h-box\">\n      <span class=\"sc-h-label\">Temperature<\/span>\n      <div class=\"sc-h-main\">\n        <span class=\"val-pair\">\n          <span class=\"lo-val\" id=\"h-temp-min\">&#8212;<\/span><span class=\"sc-h-unit\">\u00b0C<\/span>\n        <\/span>\n        <span class=\"v-sep\">\/<\/span>\n        <span class=\"val-pair\">\n          <span class=\"hi-val\" id=\"h-temp-max\">&#8212;<\/span><span class=\"sc-h-unit\">\u00b0C<\/span>\n        <\/span>\n      <\/div>\n      <div class=\"sc-h-footer\"><span>Avg<\/span> <span id=\"h-temp-avg\">&#8212;<\/span><\/div>\n    <\/div>\n\n    <div class=\"sc-h-box\">\n      <span class=\"sc-h-label\">Humidity<\/span>\n      <div class=\"sc-h-main\">\n        <span class=\"lo-val\" id=\"h-hum-min\">&#8212;<\/span><span class=\"sc-h-unit\">%<\/span>\n        <span class=\"v-sep\">&#8211;<\/span>\n        <span class=\"hi-val\" id=\"h-hum-max\">&#8212;<\/span><span class=\"sc-h-unit\">%<\/span>\n      <\/div>\n      <div class=\"sc-h-footer\"><span>Avg<\/span> <span id=\"h-hum-avg\">&#8212;<\/span><\/div>\n    <\/div>\n\n    <div class=\"sc-h-box\" id=\"solar-trigger\" style=\"cursor: pointer; background: rgba(255, 243, 200, 0.3);\">\n      <span class=\"sc-h-label\">Solar Yield \u24d8<\/span>\n      <div class=\"sc-h-main\">\n        <span id=\"h-solar-wh\">&#8212;<\/span><span class=\"sc-h-unit\" id=\"h-sol-unit\">Wh\/m\u00b2<\/span>\n      <\/div>\n      <div style=\"font-size: 10px; font-weight: 700; color: #9b59b6;\" id=\"h-uv-peak\">UV &#8212;<\/div>\n      <div class=\"sc-h-footer\"><span>Peak<\/span> <span id=\"h-solar-peak\">&#8212;<\/span><\/div>\n    <\/div>\n\n    <div class=\"sc-h-box\">\n      <span class=\"sc-h-label\">Rain Total<\/span>\n      <div class=\"sc-h-main\">\n        <span id=\"h-rain-total\">&#8212;<\/span><span class=\"sc-h-unit\">mm<\/span>\n      <\/div>\n      <div class=\"sc-h-footer\"><span id=\"rain-sub-label\">Max Rate<\/span> <span id=\"h-rain-rate\">&#8212;<\/span><\/div>\n    <\/div>\n\n    <div id=\"solar-estimator\">\n      <div id=\"est-title\" style=\"font-weight: 800; margin-bottom: 8px; color: #96752d; letter-spacing: 0.5px;\">EST. GENERATION TODAY (Wh)<\/div>\n\n<!-- Added new Vevor solar panel estimation -->\n<div class=\"est-row\" ><span>1.4W Vevor station panel:<\/span> <b id=\"est-station\">&#8212;<\/b><\/div>\n\n      <div class=\"est-row\"><span>25W portable solar panel:<\/span> <b id=\"est-25\">&#8212;<\/b><\/div>\n      <div class=\"est-row\"><span>100W e-bike solar panel:<\/span> <b id=\"est-100\">&#8212;<\/b><\/div>\n      <div class=\"est-row\"><span>450W home solar panel:<\/span> <b id=\"est-450\">&#8212;<\/b><\/div>\n      \n      <div id=\"solar-insight-box\">\n        <span id=\"insight-text\">Tap to cycle through real-life examples&#8230;<\/span>\n      <\/div>\n      <div style=\"font-size: 9px; color: #bbb; margin-top: 8px;\">*Calculated at 75% solar panel efficiency.<\/div>\n    <\/div>\n\n    <div class=\"sc-h-box sc-h-wide\" id=\"pres-trend-box\">\n      <span class=\"sc-h-label\" id=\"pres-label\">Pressure Trend<\/span>\n      <div class=\"sc-h-main\" style=\"display:flex; align-items:center;\">\n        <span style=\"font-weight:400; color:#888;\" id=\"h-pres-start\">&#8212;<\/span>\n        <span class=\"trend-arrow\" id=\"h-pres-icon\"><\/span>\n        <span id=\"h-pres-end\">&#8212;<\/span>\n        <span class=\"sc-h-unit\" style=\"margin-left:8px;\">hPa<\/span>\n      <\/div>\n      <div class=\"sc-h-footer\">\n        <span id=\"h-pres-status\">Steady<\/span>\n        <span id=\"h-pres-delta\">&#8212;<\/span>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script>\n(function() {\n  const svgArrows = {\n    up: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"7\" y1=\"17\" x2=\"17\" y2=\"7\"><\/line><polyline points=\"7 7 17 7 17 17\"><\/polyline><\/svg>`,\n    down: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"7\" y1=\"7\" x2=\"17\" y2=\"17\"><\/line><polyline points=\"17 7 17 17 7 17\"><\/polyline><\/svg>`,\n    right: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"><\/line><polyline points=\"12 5 19 12 12 19\"><\/polyline><\/svg>`\n  };\n\n  let currentView = 'day';\n  let currentIdx = 0;\n\/\/ New Vevor Weather station solar panel added to the calculated estimations\n let yields = { yStation: 0, y25: 0, y100: 0, y450: 0 };\n  \n\/\/ Date Tracking & Boundaries Santa Cruz CHANGE THE DATE FOR EVERY WEATHER STATION\n  let selectedDate = new Date();\n\n\/\/ THIS IS THE FIRST DAY ONLINE WITH JSON FILES\n  const stationStartDate = new Date(\"2026-01-03\");\n\nconst getISO = (d) => d.toLocaleString(\"en-CA\", {timeZone: \"Atlantic\/Madeira\", hour12: false}).split(',')[0];\n\n  window.changeView = function(v) {\n    currentView = v;\n    document.querySelectorAll('.sc-btn').forEach(b => b.classList.toggle('active', b.id === 'btn-'+v));\n    \n    \/\/ UI: Only show date navigation in 'day' mode\n    const navWrapper = document.querySelector('.sc-date-nav');\n    if (navWrapper) navWrapper.style.display = (v === 'day') ? 'flex' : 'none';\n    \n    updateHistory();\n  };\n\n  window.shiftDate = function(offset) {\n    const targetDate = new Date(selectedDate);\n    targetDate.setDate(targetDate.getDate() + offset);\n    \n    const todayStr = getISO(new Date());\n    const startStr = getISO(stationStartDate);\n    const targetStr = getISO(targetDate);\n\n    \/\/ Block navigation outside bounds\n    if (targetStr > todayStr || targetStr < startStr) return; \n    \n    selectedDate = targetDate;\n    updateHistory();\n  };\n\n  window.setDate = function(dateString) {\n    if(!dateString) return;\n    const selected = new Date(dateString);\n    \n    const todayStr = getISO(new Date());\n    const startStr = getISO(stationStartDate);\n    const selectedStr = getISO(selected);\n\n    if (selectedStr > todayStr || selectedStr < startStr) return;\n    \n    selectedDate = selected;\n    updateHistory();\n  };\n\n  \/\/ --- Insight and Solar logic (kept intact) ---\n  function cycleInsight() {\n\n\/\/ Added Vevor factory 3AA NI-MH 2000mah battery and my mod 18650 bat 3600mah\n    const modBatteryWh = 13.32; \/\/ 3600mAh @ 3.7V\n    const factoryWh = 7.4;      \/\/ 2000mAh @ 3.7V\n      \n\/\/ Calculations\n    const pctMod = Math.min(((yields.yStation \/ modBatteryWh) * 100), 100).toFixed(0);\n    const mahGained = ((yields.yStation \/ 3.7) * 1000).toFixed(0);\n\n    const list = [\n\n   `&#x1f4a1; <b>Weather station:<\/b> Today adds <b>${mahGained}mAh<\/b>, <b>${pctMod}%<\/b> of a 3600mAh bat, <b>${((yields.yStation \/ factoryWh) * 100).toFixed(0)}%<\/b> the original 2Ah AA pack.`,\n \n      `&#x1f4a1; <b>Portability:<\/b> A 25W panel charges a 20,000mAh powerbank <b>${(yields.y25 \/ (3.7 * 20)).toFixed(1)} times<\/b>.`,\n      `&#x1f4a1; <b>48V 17.5Ah E-Bike:<\/b> A 100W panel could give <b>${Math.round(yields.y100 \/ 15)} km<\/b> extra range.`,\n      `&#x1f4a1; <b>Appliances:<\/b> One 450W panel could power a fridge for <b>${Math.round(yields.y450 \/ 50)} hours<\/b>.`,\n      `&#x1f4a1; <b>Autonomy:<\/b> Ten 450W panels could fill <b>${Math.round(((yields.y450 * 10) \/ 13500) * 100)}%<\/b> of a 13.5kWh Powerwall.`,\n      `&#x1f4a1; <b>Computer:<\/b> A 100W panel could fully charge a laptop <b>${Math.round(yields.y100 \/ 70)} times<\/b>.`\n    ];\n    document.getElementById('insight-text').innerHTML = list[currentIdx];\n    currentIdx = (currentIdx + 1) % list.length;\n  }\n\n  document.getElementById('solar-trigger').onclick = function() {\n    const est = document.getElementById('solar-estimator');\n    const opening = est.style.display !== 'block';\n    est.style.display = opening ? 'block' : 'none';\n    if (opening) {\n        if (currentView === 'day') cycleInsight();\n        else document.getElementById('insight-text').innerHTML = `&#x2600; Total solar energy captured per m\u00b2`;\n    }\n  };\n\n  document.getElementById('solar-insight-box').onclick = function(e) {\n    e.stopPropagation();\n    if (currentView === 'day') cycleInsight();\n  };\n\n  function getCardinal(angle) {\n    const directions = ['N','NNE','NE','ENE','E','ESE','SE','SSE','S','SSW','SW','WSW','W','WNW','NW','NNW'];\n    return directions[Math.round(angle \/ 22.5) % 16];\n  }\n\n  async function updateHistory() {\n    const baseUrl = `https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/santacruz\/`;\n    const todayStr = getISO(selectedDate);\n    const startStr = getISO(stationStartDate);\n    const realToday = getISO(new Date());\n    const currentYear = selectedDate.getFullYear();\n    \n    \/\/ Update Arrow Visibility\n    const prevBtn = document.getElementById('prev-date');\n    const nextBtn = document.getElementById('next-date');\n    const pickerInput = document.getElementById('date-picker-input');\n\n    if (currentView === 'day') {\n        pickerInput.min = startStr;\n        pickerInput.max = realToday;\n        \n        if (prevBtn) prevBtn.style.visibility = (todayStr === startStr) ? 'hidden' : 'visible';\n        if (nextBtn) nextBtn.style.visibility = (todayStr === realToday) ? 'hidden' : 'visible';\n    }\n\/\/ Always use .summary.json for 'day' to get rain totals\n    let url = (currentView === 'day') ? `${baseUrl}${todayStr}.summary.json` : (currentView === 'month' ? `${baseUrl}month.summary.json` : `${baseUrl}${currentYear}.summary.json`);\n    url += `?t=${Date.now()}`;\n\n    try {\n      const res = await fetch(url);\n      if (!res.ok) throw new Error(\"File not found\");\n      const json = await res.json();\n      \n      if (currentView === 'day') {\n\n \/\/ Point 'data' to the hourly samples inside the summary\n\/\/ Added optional chaining (?.) and fallback values (|| 0) to keep widget working\nconst data = (json.hourly?.temperature || []).map((t, idx) => ({\n    time: t.t,\n    temp: { c: t.c },\n    humidity: json.hourly?.humidity?.[idx]?.v || null, \n    solarRadiation: json.hourly?.solar?.[idx]?.sol || 0,\n    pressure: { hPa: json.hourly?.pressure?.[idx]?.hPa || 0 },\n    uv: json.stats?.uv_max || 0\n}));\n\/\/ Removed the early return so the rest of the stats still load\n\n   \/\/ --- SOLAR CALCULATIONS ---\n        let totalWh = json.stats.solar_yield || 0;  \/\/ Use PHP calculated total\n        let maxSolar = json.stats.solar_max || 0;\n        \n        const eff = 0.75;\n        yields.yStation = totalWh * 0.0014 * eff; \/\/ 1.4W Vevor Panel Factor\n        yields.y25 = totalWh * 0.025 * eff;\n        yields.y100 = totalWh * 0.100 * eff;\n        yields.y450 = totalWh * 0.450 * eff;\n\n        \/\/ --- WIND ---\n      \/\/ Direct pull from daily json stats for the 24h peaks with safe checks\n        document.getElementById('h-wind-max').innerText = json.stats.peak_24h_wind != null ? json.stats.peak_24h_wind.toFixed(1) : '--';\n        document.getElementById('h-gust-max').innerText = json.stats.peak_24h_gust != null ? json.stats.peak_24h_gust.toFixed(1) : '--';\n        document.getElementById('h-wind-avg').innerText = json.stats.wind_avg_24h != null ? json.stats.wind_avg_24h.toFixed(1) : '--';\n\n\/\/ --- DOMINANT DIRECTION ---\n        document.getElementById('h-domin-wind').innerText = json.stats.dom_dir || \"---\";\n\n   \/\/ --- TEMPERATURE & HUMIDITY ---\n        document.getElementById('h-temp-min').innerText = json.stats.temp_min != null ? json.stats.temp_min.toFixed(1) : '--';\n        document.getElementById('h-temp-max').innerText = json.stats.temp_max != null ? json.stats.temp_max.toFixed(1) : '--';\n        document.getElementById('h-temp-avg').innerText = json.stats.temp_avg != null ? json.stats.temp_avg.toFixed(1) + '\u00b0' : '--';\n        \n        document.getElementById('h-hum-min').innerText = json.stats.hum_min != null ? json.stats.hum_min : '--';\n        document.getElementById('h-hum-max').innerText = json.stats.hum_max != null ? json.stats.hum_max : '--';\n        document.getElementById('h-hum-avg').innerText = json.stats.hum_avg != null ? json.stats.hum_avg + '%' : '--';\n \n\/\/ --- RAIN (The totals we are calculating with Leo script last hour) ---\n        document.getElementById('h-rain-total').innerText = json.stats.rain_total != null ? json.stats.rain_total.toFixed(1) : '--';\n        document.getElementById('h-rain-rate').innerText = json.stats.rain_max_rate != null ? json.stats.rain_max_rate.toFixed(1) + ' mm\/h' : '--';\n\n \/\/ --- PRESSURE TREND ---\n        const presData = (json.hourly?.pressure || []).filter(p => p && p.hPa != null);\n        if (presData.length > 0) {\n            const startP = presData[0].hPa;\n            const endP = presData[presData.length-1].hPa;\n            const diffP = endP - startP;\n            document.getElementById('h-pres-icon').innerHTML = diffP > 0.6 ? svgArrows.up : (diffP < -0.6 ? svgArrows.down : svgArrows.right);\n            document.getElementById('h-pres-start').innerText = startP.toFixed(0);\n            document.getElementById('h-pres-end').innerText = endP.toFixed(0);\n            document.getElementById('h-pres-delta').innerText = (diffP > 0 ? '+' : '') + diffP.toFixed(1) + ' hPa';\n            document.getElementById('h-pres-status').innerText = diffP > 0.6 ? 'Rising' : (diffP < -0.6 ? 'Reducing' : 'Steady');\n        } else {\n            document.getElementById('h-pres-icon').innerHTML = '';\n            document.getElementById('h-pres-start').innerText = '--';\n            document.getElementById('h-pres-end').innerText = '--';\n            document.getElementById('h-pres-delta').innerText = '--';\n            document.getElementById('h-pres-status').innerText = '--';\n        }\n\n        document.getElementById('h-title').innerText = \"Santa Cruz DAILY Summary\";\n        document.getElementById('h-date').innerText = selectedDate.toLocaleDateString('pt-PT', { timeZone: 'Atlantic\/Madeira' }).replace(\/\\\/\/g, '-');\n        document.getElementById('h-solar-wh').innerText = Math.round(totalWh);\n        document.getElementById('h-sol-unit').innerText = \"Wh\/m\u00b2\";\n        document.getElementById('h-solar-peak').innerText = Math.round(maxSolar) + ' W\/m\u00b2';\n        document.getElementById('h-uv-peak').innerText = \"UV \" + (json.stats.uv_max || 0).toFixed(1);\n        document.getElementById('est-title').innerText = \"EST. GENERATION TODAY (Wh)\";\n\n\/\/ New Vevor solar panel estimation added\ndocument.getElementById('est-station').innerText = (currentView === 'day') ? \n    yields.yStation.toFixed(1) + ' Wh' : \n    (yields.yStation \/ 1000).toFixed(2) + ' kWh';\n\n        document.getElementById('est-25').innerText = Math.round(yields.y25) + ' Wh';\n        document.getElementById('est-100').innerText = Math.round(yields.y100) + ' Wh';\n        document.getElementById('est-450').innerText = Math.round(yields.y450) + ' Wh';\n        document.getElementById('pres-label').innerText = \"Pressure Trend\";\n        document.getElementById('pres-trend-box').style.display = \"flex\";\n      }\n      \n      else {\n \/\/ --- MONTHLY \/ YEARLY LOGIC (Direct from PHP Stats) ---\n        const stats = json.stats || {};\n        document.getElementById('h-title').innerText = currentView === 'month' ? \"MONTHLY TOTALS\" : \"YEARLY TOTALS\";\n        document.getElementById('h-date').innerText = currentView === 'month' ? selectedDate.toLocaleString('en-US', { month: 'long', year: 'numeric', timeZone: 'Atlantic\/Madeira' }) : \"Year \" + currentYear;\n        \n        const displayYield = stats.solar_yield || 0;\n        const eff = 0.75;\n        yields.yStation = displayYield * 0.0014 * eff; \/\/ Vevor solar panel 1.4W\n        yields.y25 = displayYield * 0.025 * eff;\n        yields.y100 = displayYield * 0.100 * eff;\n        yields.y450 = displayYield * 0.450 * eff;\n\n\/\/ Add Vevor solar panel total to month and year\nconst estStationEl = document.getElementById('est-station');\nif (estStationEl) {\n    estStationEl.innerText = (currentView === 'day') ? \n        yields.yStation.toFixed(1) + ' Wh' : \n        (yields.yStation \/ 1000).toFixed(2) + ' kWh';\n}\n\n \/\/ 1. SOLAR &#038; YIELDS (Updating the global yields object so examples work)\n        document.getElementById('h-solar-wh').innerText = displayYield > 1500 ? (displayYield\/1000).toFixed(1) : displayYield;\n        document.getElementById('h-sol-unit').innerText = displayYield > 1500 ? \"kWh\/m\u00b2\" : \"Wh\/m\u00b2\";\n        document.getElementById('h-solar-peak').innerText = (stats.solar_max || 0).toFixed(0) + ' W\/m\u00b2';\n        document.getElementById('h-uv-peak').innerText = \"UV \" + (stats.uv_max || 0).toFixed(1);\n\n        \/\/ 2. ESTIMATOR BOX (Simple conversion to kWh for the totals view)\n        document.getElementById('est-title').innerText = \"EST. GENERATION (TOTAL)\";\n        document.getElementById('est-25').innerText = (yields.y25 \/ 1000).toFixed(1) + ' kWh';\n        document.getElementById('est-100').innerText = (yields.y100 \/ 1000).toFixed(1) + ' kWh';\n        document.getElementById('est-450').innerText = (yields.y450 \/ 1000).toFixed(1) + ' kWh';\n\n   \/\/ 3. WIND & TEMP (Using the 24h peak\/avg PHP fields)\n        document.getElementById('h-wind-max').innerText = stats.peak_24h_wind != null ? stats.peak_24h_wind.toFixed(1) : '--';\n        document.getElementById('h-gust-max').innerText = stats.peak_24h_gust != null ? stats.peak_24h_gust.toFixed(1) : '--';\n        document.getElementById('h-wind-avg').innerText = stats.wind_avg_24h != null ? stats.wind_avg_24h.toFixed(1) : '--';\n        document.getElementById('h-domin-wind').innerText = stats.dom_dir || \"---\";\n\n      \/\/ Temperature\n        document.getElementById('h-temp-min').innerText = stats.temp_min != null ? stats.temp_min.toFixed(1) : '--';\n        document.getElementById('h-temp-max').innerText = stats.temp_max != null ? stats.temp_max.toFixed(1) : '--';\n        document.getElementById('h-temp-avg').innerText = stats.temp_avg != null ? stats.temp_avg.toFixed(1) + '\u00b0' : '--';\n        \n    \/\/ 4. HUMIDITY & RAIN\n        document.getElementById('h-hum-min').innerText = stats.hum_min != null ? stats.hum_min : \"--\";\n        document.getElementById('h-hum-max').innerText = stats.hum_max != null ? stats.hum_max : \"--\";\n        document.getElementById('h-hum-avg').innerText = stats.hum_avg != null ? stats.hum_avg + '%' : \"--\";\n        \n    \/\/ Rain\n        document.getElementById('h-rain-total').innerText = stats.rain_total != null ? stats.rain_total.toFixed(1) : '--';\n        document.getElementById('h-rain-rate').innerText = stats.rain_max_rate != null ? stats.rain_max_rate.toFixed(1) + ' mm\/h' : '--';\n        \n     \/\/ 5. PRESSURE\n        document.getElementById('pres-label').innerText = \"Pressure Range\";\n        document.getElementById('h-pres-status').innerText = \"Low \/ High\";\n        document.getElementById('h-pres-start').innerText = stats.pressure_min != null ? Math.round(stats.pressure_min) : \"--\";\n        document.getElementById('h-pres-icon').innerHTML = `<span style=\"color:rgba(171,171,171,0.5); margin: 0 4px;\">\/<\/span>`; \n        document.getElementById('h-pres-end').innerText = stats.pressure_max != null ? Math.round(stats.pressure_max) : \"--\";\n        document.getElementById('h-pres-delta').innerText = (stats.pressure_max != null && stats.pressure_min != null) ? (stats.pressure_max - stats.pressure_min).toFixed(0) + ' hPa diff' : '--';\n      }\n    } catch (e) { \n      console.warn(\"Error processing or no data found for this date:\", todayStr, e);\n      document.getElementById('h-date').innerText = \"No Data Available\";\n    }\n  }\n\n  updateHistory();\n  setInterval(() => { if(currentView === 'day') updateHistory(); }, 300000);\n})();\n<\/script>\n\n\n\n<!-- Day plus Precious selection, 7 days, 30 days and year Wind 9h to 21h -->\n\n<div class=\"sc-pro-cockpit wind-vault\" style=\"font-family: 'Inter', sans-serif; background: rgba(250, 250, 250,0.4); padding: 15px; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.05); margin-top: 20px;\">\n  \n  <div class=\"sc-header-flex\" style=\"display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px;\">\n    <div style=\"display: flex; flex-direction: column; gap: 8px;\">\n      <div id=\"sc-wind-time-nav\" style=\"display: flex; gap: 8px; align-items: center;\">\n        <button onclick=\"loadWindData('day')\" id=\"w-btn-day\" class=\"nav-btn active\">Today<\/button>\n        <button onclick=\"loadWindData('week')\" id=\"w-btn-week\" class=\"nav-btn\">7 Days<\/button>\n        <button onclick=\"loadWindData('month')\" id=\"w-btn-month\" class=\"nav-btn\">30 Days<\/button>\n        <button onclick=\"loadWindData('year')\" id=\"w-btn-year\" class=\"nav-btn\">Year<\/button>\n\n      <\/div>\n      \n      <div id=\"sc-wind-date-nav\" style=\"display: flex; align-items: center; gap: 10px; padding-left: 5px;\">\n        <button id=\"sc-wind-prev-btn\" onclick=\"shiftWindDate(-1)\" class=\"date-arrow\">\u276e<\/button>\n        <span id=\"sc-wind-display-date\" onclick=\"document.getElementById('sc-wind-date-picker').showPicker()\" style=\"font-size: 11px; font-weight: 700; color: #3b5875; cursor: pointer; min-width: 50px; text-align: center;\">&#8211;\/&#8211;<\/span>\n        <input type=\"date\" id=\"sc-wind-date-picker\" style=\"position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;\" onchange=\"setWindDate(this.value)\">\n        <button id=\"sc-wind-next-btn\" onclick=\"shiftWindDate(1)\" class=\"date-arrow\">\u276f<\/button>\n<button id=\"sc-24h-toggle\" onclick=\"toggle24h()\" class=\"nav-btn\" style=\"display: none; margin-left: 10px; border: 1px solid #3b5875; background: #fff; color: #3b5875;\">Show 24h<\/button>\n      <\/div>\n    <\/div>\n    \n    <div style=\"font-size: 11px; color: #999; font-weight: 600; letter-spacing: 0.5px; text-align: right; padding-top: 5px;\" id=\"sc-station-label\">SANTA CRUZ wind 9h to 21h<\/div>\n  <\/div>\n\n  <div class=\"sc-legend\" style=\"display: flex; gap: 12px; font-size: 11px; font-weight: 700; color: #444; margin-bottom: 15px; background: #f9f9f9; padding: 6px 10px; border-radius: 6px; width: fit-content;\">\n    <div style=\"display: flex; align-items: center; gap: 4px;\"><span style=\"width: 8px; height: 3px; background: rgba(41, 128, 185, 0.7);\"><\/span> Wind<\/div>\n    <div style=\"display: flex; align-items: center; gap: 4px;\"><span style=\"width: 8px; height: 3px; background: rgba(230, 126, 34, 0.4);\"><\/span> Gusts<\/div>\n    <div style=\"display: flex; align-items: center; gap: 4px;\"><span style=\"width: 6px; height: 6px; border-radius: 50%; background: #3b5875;\"><\/span> Direction<\/div>\n    <span id=\"sc-wind-status\" style=\"color: #e67e22; margin-left: 10px; font-style: italic; font-size: 10px;\"><\/span>\n  <\/div>\n\n  <div style=\"height: 320px; position: relative;\">\n    <canvas id=\"sc-wind-master-chart\"><\/canvas>\n  <\/div>\n<\/div>\n\n<div style=\"display: flex; justify-content: center; gap: 20px; margin-top: -10px; padding-bottom: 15px;\">\n  <button id=\"w-toggle-mode\" class=\"nav-btn\" style=\"background: #e8f4ff; border: 1px solid #185e96;\">READING Mode ACTIVE<\/button>\n  <button id=\"w-reset-view\" class=\"nav-btn\" style=\"background: none; border: 1px solid #ccc;\">RESET View<\/button>\n<\/div>\n\n<style>\n  \/* Added touch-action for panning *\/\n  #sc-wind-master-chart { touch-action: pan-y; }\n  .nav-btn { border: none; background: #eee; padding: 5px 12px; border-radius: 20px; font-size: 11px; font-weight: 700; cursor: pointer; color: #666; transition: 0.2s; }\n  .nav-btn.active { background: #3b5875; color: #fff; }\n  .date-arrow { background: none; border: none; color: #bbb; cursor: pointer; font-size: 20px; display: flex; align-items: center; transition: 0.2s; }\n  .date-arrow:hover { color: #3b5875; }\n  .date-arrow:disabled { color: #eee; cursor: not-allowed; }\n\n  @media (max-width: 600px) {\n    .sc-header-flex { flex-direction: column; gap: 10px; }\n    #sc-station-label { text-align: left; }\n  }\n<\/style>\n\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/hammerjs@2.0.8\"><\/script>\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chartjs-plugin-zoom@2.2.0\"><\/script>\n\n<script>\n(function() {\n  let windChart;\nlet isReadingMode = true; \/\/ Added state for mode toggle\n  let currentTimeframe = 'day';\n  let activeDate = new Date();\n\/\/ New 24h wind button\nlet is24hMode = false;\n\nwindow.toggle24h = function() {\n  is24hMode = !is24hMode;\n  const btn = document.getElementById('sc-24h-toggle');\n  btn.innerText = is24hMode ? \"Back to 9-21h\" : \"Show 24h\";\n  loadWindData('day');\n};\n\n  const stationPath = 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/santacruz\/';\n  const stationStartDate = new Date(\"2026-01-03\"); \/\/ Santa Cruz - Change this date based on weather station deployment\n\n  \/\/ Initial Logic: If it's before 9am, default to yesterday for the \"Day\" view\n  const getISO = (d) => d.toLocaleString(\"en-CA\", {timeZone: \"Atlantic\/Madeira\", hour12: false}).split(',')[0];\n\n  \/\/ Initial Logic: Madeira-aware hour check\n  const now = new Date();\n  const currentHr = parseInt(now.toLocaleString('en-US', {hour: 'numeric', hour12: false, timeZone: 'Atlantic\/Madeira'}), 10);\n  if (currentHr < 9) {\n    activeDate.setDate(activeDate.getDate() - 1);\n  }\n\n  const getCompass = (deg) => {\n    const s = [\"N\", \"NNE\", \"NE\", \"ENE\", \"E\", \"ESE\", \"SE\", \"SSE\", \"S\", \"SSW\", \"SW\", \"WSW\", \"W\", \"WNW\", \"NW\", \"NNW\"];\n    return s[Math.round(deg \/ 22.5) % 16];\n  };\n\n   window.shiftWindDate = function(offset) {\n    const target = new Date(activeDate);\n    target.setDate(target.getDate() + offset);\n    if (getISO(target) > getISO(new Date()) || getISO(target) < getISO(stationStartDate)) return;\n    activeDate = target;\n    loadWindData('day');\n  };\n\n  window.setWindDate = function(val) {\n    if (!val) return;\n    activeDate = new Date(val);\n    loadWindData('day');\n  };\n\n  async function loadWindData(timeframe) {\n    currentTimeframe = timeframe;\n    const status = document.getElementById('sc-wind-status');\n    if (status) status.innerText = \"Loading...\";\n\n    \/\/ UI Updates\n    document.querySelectorAll('#sc-wind-time-nav .nav-btn').forEach(b => b.classList.toggle('active', b.id === 'w-btn-'+timeframe));\n\n   \/\/ 24h wind logic for day view only\n    const dateNav = document.getElementById('sc-wind-date-nav');\n    const toggleBtn = document.getElementById('sc-24h-toggle');\n    const stationLabel = document.getElementById('sc-station-label');\n\n    \/\/ Only show date nav and 24h toggle if we are in 'day' view\n    if (timeframe === 'day') {\n      dateNav.style.display = 'flex';\n      toggleBtn.style.display = 'inline-block';\n      stationLabel.innerText = is24hMode ? \"SANTA CRUZ wind 24h\" : \"SANTA CRUZ wind 9h to 21h\";\n    } else {\n      dateNav.style.display = 'none';\n      toggleBtn.style.display = 'none';\n      is24hMode = false; \/\/ Reset mode when leaving Day view\n      if (toggleBtn) toggleBtn.innerText = \"Show 24h\";\n    }\n\n    if (timeframe === 'day') {\n      const picker = document.getElementById('sc-wind-date-picker');\n      picker.max = getISO(new Date());\n      picker.min = getISO(stationStartDate);\n      document.getElementById('sc-wind-display-date').innerText = activeDate.toLocaleDateString('pt-PT', {day:'2-digit', month:'2-digit', timeZone: 'Atlantic\/Madeira'});\n      \n      document.getElementById('sc-wind-prev-btn').disabled = getISO(activeDate) === getISO(stationStartDate);\n      document.getElementById('sc-wind-next-btn').disabled = getISO(activeDate) === getISO(new Date());\n    }\n\n    try {\n      let processedData = [];\n\n       \/\/ OPTIMIZED CACHE LOGIC:\n      const now = new Date();\n      const isCurrentMonth = activeDate.getMonth() === now.getMonth() && activeDate.getFullYear() === now.getFullYear();\n      \n      \/\/ 'day' stays live to the millisecond.\n      \/\/ 'month' (current) stays stable for 1 hour to allow fast caching.\n      \/\/ everything else (past months\/years) caches forever.\n      const hourlyTimestamp = new Date().setMinutes(0, 0, 0); \n\n      const cacheBust = (timeframe === 'day') \n        ? `?t=${Date.now()}` \n        : (timeframe === 'month' && isCurrentMonth) \n          ? `?t=${hourlyTimestamp}` \n          : '';\n\n    if (timeframe === 'day') {\n        const file = getISO(activeDate) + '.json';\n        const res = await fetch(stationPath + file + cacheBust);\n        const json = await res.json();\n       \n\/\/ Guard check for valid array structure\n        if (!Array.isArray(json)) {\n          console.warn(\"Data malformed. Holding previous state.\");\n          if (status) status.innerText = \"Data Structure Error\";\n          return;\n        }\n\n \/\/ 9h to 21h FILTER\n          const filtered = json.filter(d => {\n          if (is24hMode) return true; \/\/ Show everything for 24h view\n          const hr = parseInt(new Date(d.time).toLocaleString('en-US', {hour: 'numeric', hour12: false, timeZone: 'Atlantic\/Madeira'}), 10);\n          return hr >= 9 && hr < 21; \/\/ Standard 9-21h window\n        });\n\n        processedData = [];\n        const GAP_THRESHOLD = 10 * 60 * 1000; \/\/ 10 minutes in milliseconds\n\n        filtered.forEach((d, i) => {\n          const currentTime = new Date(d.time).getTime();\n          \n          \/\/ If there's a previous point, check if the gap is too large\n          if (i > 0) {\n            const prevTime = new Date(filtered[i - 1].time).getTime();\n            if (currentTime - prevTime > GAP_THRESHOLD) {\n    \/\/ Insert two null points for clean line segments during zoom\/pan operations\n              processedData.push({ t: prevTime + 1000, s: null, g: null, d: null });\n              processedData.push({ t: currentTime - 1000, s: null, g: null, d: null });\n            }\n          }\n\n          processedData.push({\n            t: currentTime,\n            s: d.windSpeed?.kph ?? 0,\n            g: d.windGust?.kph ?? 0,\n         d: d.winddir ?? null\n\n          });\n        });\n      }\n     \nelse if (timeframe === 'month') {\n        const res = await fetch(`${stationPath}${activeDate.getFullYear()}-${String(activeDate.getMonth() + 1).padStart(2, '0')}.summary.json${cacheBust}`);\n        const json = await res.json();\n        let daysBuffer = json.days || [];\n        \n        if (daysBuffer.length < 25) {\n          const prev = new Date(activeDate.getFullYear(), activeDate.getMonth() - 1, 1);\n          const res2 = await fetch(`${stationPath}${prev.getFullYear()}-${String(prev.getMonth() + 1).padStart(2, '0')}.summary.json`);\n          if (res2.ok) {\n            const json2 = await res2.json();\n            daysBuffer = [...(json2.days || []), ...daysBuffer];\n          }\n        }\n        daysBuffer.slice(-30).forEach(day => {\n          const samples = day.wind?.samples || [];\n          samples.forEach(s => processedData.push({ t: s.t, s: s.s, g: s.g, d: s.d }));\n          \n          \/\/ Add a break after each day to prevent connecting lines\n          if (samples.length > 0) {\n            processedData.push({ t: samples[samples.length - 1].t + 1, s: null, g: null, d: null });\n          }\n        });\n      } \n      else {\n        const file = (timeframe === 'year') ? activeDate.getFullYear() + '.summary.json' : 'week.summary.json';\n        const res = await fetch(stationPath + file + cacheBust);\n        const json = await res.json();\n        const days = json.days || [];\n        \n        if (timeframe === 'year') {\n          processedData = days.map(d => ({ \n            t: new Date(d.t.replace(\/-\/g, \"\/\")).getTime(), \n            s: d.wind, g: d.gust, d: d.dir \n          }));\n        } else {\n          days.forEach(day => {\n            const samples = day.wind?.samples || [];\n            samples.forEach(s => processedData.push({ t: s.t, s: s.s, g: s.g, d: s.d }));\n            \n            \/\/ Add a break after each day to prevent connecting lines\n            if (samples.length > 0) {\n                processedData.push({ t: samples[samples.length - 1].t + 1, s: null, g: null, d: null });\n            }\n          });\n        }\n      }\n      if (status) status.innerText = \"\";\n      renderChart(processedData);\n    } catch (e) {\n      console.error(e);\n      if (status) status.innerText = \"Data Pending...\";\n    }\n  }\n\n  function renderChart(data) {\n    const ctx = document.getElementById('sc-wind-master-chart').getContext('2d');\n    if (windChart) windChart.destroy();\n\n    const isYear = currentTimeframe === 'year';\n\n    windChart = new Chart(ctx, {\n      type: 'line',\n      data: {\n        datasets: [\n          {\n            label: 'Direction',\n            data: data.map(d => ({x: d.t, y: d.d})),\n            borderColor: 'transparent',\n            pointBackgroundColor: '#3b5875',\n            pointRadius: { 'day': 2.0, 'week': 1.6, 'month': 1.2, 'year': 2.0 }[currentTimeframe],\n            yAxisID: 'yDir',\n            showLine: false\n          },\n          {\n            label: 'Gusts',\n            data: data.map(d => ({x: d.t, y: d.g})),\n            borderColor: 'rgba(230, 126, 34, 0.25)', \/\/ Faded orange line\n            backgroundColor: 'rgba(230, 126, 34, 0.04)', \/\/ Gentle orange fill\n            fill: true,\nborderWidth: { 'day': 1.2, 'week': 0.7, 'month': 0.2, 'year': 1.1 }[currentTimeframe],\npointRadius: { 'day': 0,   'week': 0,   'month': 0,   'year': 0   }[currentTimeframe],\n            yAxisID: 'ySpeed',\n            tension: 0.3, \/\/ Smoother curves\n            spanGaps: false\n          },\n          {\n            label: 'Wind',\n            data: data.map(d => ({x: d.t, y: d.s})),\n            borderColor: 'rgba(41, 128, 185, 0.4)', \/\/ Sophisticated, lighter blue\nborderWidth: { 'day': 1.2, 'week': 0.6, 'month': 1.2, 'year': 1.0 }[currentTimeframe],\npointRadius: { 'day': 0,   'week': 0,   'month': 0,   'year': 0   }[currentTimeframe],\n            yAxisID: 'ySpeed',\n            tension: 0.3,\n            spanGaps: false\n          }\n        ]\n      },\n      options: {\n        responsive: true,\n        maintainAspectRatio: false,\n        interaction: { mode: 'index', intersect: false },\n        scales: {\n          x: {\n            type: 'linear',\n\/\/ This makes the graph go edge to edge 9h to 21h \n            min: currentTimeframe === 'day' ? new Date(activeDate).setHours(is24hMode ? 0 : 9, 0, 0, 0) : undefined,\n            max: currentTimeframe === 'day' ? new Date(activeDate).setHours(is24hMode ? 24 : 21, 0, 0, 0) : undefined,\n                        ticks: {\n              \/\/ 1 hour for day, ~30 days (2,592,000,000ms) for year, auto for others\n              stepSize: currentTimeframe === 'day' ? (60 * 60 * 1000) : (currentTimeframe === 'year' ? 2592000000 : undefined),\n              \n              \/\/ Force higher limit for year to ensure all months render\n        autoSkip: true,\n        maxTicksLimit: 12,\n        includeBounds: true,\n\n              font: { size: 9, weight: '600' },\n              callback: (v) => {\n                const d = new Date(v);\n                if (currentTimeframe === 'day') return d.toLocaleString('en-US', {hour: 'numeric', hour12: false, timeZone: 'Atlantic\/Madeira'}) + ':00';\n                if (isYear) return d.toLocaleString('en-GB', {month: 'short', timeZone: 'Atlantic\/Madeira'});\n                return d.toLocaleString('en-GB', {day: 'numeric', month: 'numeric', timeZone: 'Atlantic\/Madeira'});\n              }\n            },\n\n            grid: { display: false }\n          },\n          ySpeed: {\n            position: 'left',\n            beginAtZero: true,\n            grace: '10%',\n            ticks: { font: { size: 10 }, color: '#888' },\n            grid: { color: 'rgba(0,0,0,0.03)' }\n          },\n          yDir: {\n            position: 'right',\n            min: 0, max: 360,\n            ticks: {\n              stepSize: 90,\n              callback: v => v === 0 || v === 360 ? 'N' : v === 90 ? 'E' : v === 180 ? 'S' : v === 270 ? 'W' : '',\n              font: { size: 11, weight: '700' }\n            },\n            grid: { display: false }\n          }\n        },\n        plugins: {\n          legend: { display: false },\n                    \/\/ Added zoom plugin configuration\n          zoom: {\n            limits: { \n              x: { \n                min: data.length > 0 ? data[0].t : undefined, \n                max: data.length > 0 ? data[data.length - 1].t : undefined \n              } \n            },\n            pan: { enabled: !isReadingMode, mode: 'x' },\n            zoom: {\n              wheel: { enabled: true },\n              pinch: { enabled: true },\n              mode: 'x'\n            }\n          },\n      tooltip: {\n            enabled: isReadingMode, \/\/ Conditioned tooltip on reading mode\n            backgroundColor: 'rgba(44, 62, 80, 0.9)',\n            callbacks: {\n              title: (items) => {\n                const d = new Date(items[0].parsed.x);\n \/\/ Added hour12: false for 24h format\n                return d.toLocaleString([], { weekday:'short', day:'numeric', month:'short', hour:'2-digit', minute:'2-digit', hour12: false, timeZone: 'Atlantic\/Madeira' });\n              },\n              label: (ctx) => {\n                if (ctx.datasetIndex === 0) return `Dir: ${ctx.parsed.y}\u00b0 ${getCompass(ctx.parsed.y)}`;\n                return `${ctx.dataset.label}: ${ctx.parsed.y.toFixed(1)} km\/h`;\n              }\n            }\n          }\n        }\n      }\n    });\n\n    \/\/ Auto-hide tooltip on mobile\n    let touchTimer;\n    const canvas = document.getElementById('sc-wind-master-chart');\n    canvas.addEventListener('touchend', () => {\n      clearTimeout(touchTimer);\n      touchTimer = setTimeout(() => {\n        if (windChart?.tooltip) {\n          windChart.setActiveElements([]);\n          windChart.tooltip.setActiveElements([], {x:0, y:0});\n          windChart.update();\n        }\n      }, 5000);\n    });\n  }\n\n  loadWindData('day');\n  window.loadWindData = loadWindData;\n\/\/ Added Logic for Toggle Mode and Reset View\n  document.getElementById('w-toggle-mode').addEventListener('click', (e) => {\n    isReadingMode = !isReadingMode;\n    e.target.textContent = isReadingMode ? \"READING Mode ACTIVE\" : \"ZOOM\/PAN Mode ACTIVE\";\n    \n    if (windChart) {\n      windChart.options.plugins.tooltip.enabled = isReadingMode;\n      windChart.options.plugins.zoom.pan.enabled = !isReadingMode;\n      windChart.update('none');\n    }\n  });\n\n  document.getElementById('w-reset-view').addEventListener('click', () => {\n    if (windChart) windChart.resetZoom();\n  });\n\n})();\n<\/script>\n\n\n\n<!-- Brilliant Climate day picker, 7 days, 30 days and year, temp, hum, rain, solar and pressure evolution -->\n<!-- added cloud base, uv and zoom below\n\n<div class=\"sc-pro-cockpit climate-vault\" style=\"font-family: 'Inter', sans-serif; background: rgba(250, 250, 250,0.4); padding: 15px; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.05); margin-top: 20px;\">\n  <div class=\"sc-header-flex\" style=\"display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px;\">\n    <div style=\"display: flex; flex-direction: column; gap: 8px;\">\n      <div id=\"sc-climate-time-nav\" style=\"display: flex; gap: 8px; align-items: center;\">\n        <button onclick=\"loadClimateData('day')\" id=\"c-btn-day\" class=\"nav-btn\">Today<\/button>\n        <button onclick=\"loadClimateData('week')\" id=\"c-btn-week\" class=\"nav-btn active\">7 Days<\/button>\n        <button onclick=\"loadClimateData('month')\" id=\"c-btn-month\" class=\"nav-btn\">30 Days<\/button>\n        <button onclick=\"loadClimateData('year')\" id=\"c-btn-year\" class=\"nav-btn\">Year<\/button>\n      <\/div>\n      \n      <div id=\"sc-date-nav-wrapper\" style=\"display: none; align-items: center; gap: 10px; padding-left: 5px;\">\n        <button id=\"sc-prev-day-btn\" onclick=\"shiftClimateDate(-1)\" class=\"date-arrow\">\u276e<\/button>\n        <span id=\"sc-display-date\" onclick=\"document.getElementById('sc-date-picker').showPicker()\" style=\"font-size: 11px; font-weight: 700; color: #3b5875; cursor: pointer; min-width: 50px; text-align: center;\">--\/--<\/span>\n        <input type=\"date\" id=\"sc-date-picker\" style=\"position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;\" onchange=\"setClimateDate(this.value)\">\n        <button id=\"sc-next-day-btn\" onclick=\"shiftClimateDate(1)\" class=\"date-arrow\">\u276f<\/button>\n      <\/div>\n    <\/div>\n    \n    <div style=\"font-size: 11px; color: #999; font-weight: 600; letter-spacing: 0.5px; text-align: right; padding-top: 5px;\">SANTA CRUZ CLIMATE<\/div>\n  <\/div>\n\n  <div class=\"sc-sub-nav\" style=\"display: flex; gap: 15px; margin-bottom: 15px; border-bottom: 1px solid #eee; padding-bottom: 10px; overflow-x: auto;\">\n    <button onclick=\"switchClimateType('temp')\" id=\"tab-temp\" class=\"sub-tab active\">Temperature<\/button>\n    <button onclick=\"switchClimateType('hum')\" id=\"tab-hum\" class=\"sub-tab\">Humidity<\/button>\n    <button onclick=\"switchClimateType('rain')\" id=\"tab-rain\" class=\"sub-tab\">Rain<\/button>\n    <button onclick=\"switchClimateType('solar')\" id=\"tab-solar\" class=\"sub-tab\">Sun<\/button>\n    <button onclick=\"switchClimateType('pres')\" id=\"tab-pres\" class=\"sub-tab\">Pressure<\/button>\n  <\/div>\n\n  <div style=\"height: 300px; position: relative;\">\n    <canvas id=\"sc-climate-chart\"><\/canvas>\n  <\/div>\n<\/div>\n\n<div style=\"display: flex; justify-content: center; gap: 20px; margin-top: -10px; padding-bottom: 15px;\">\n  <button id=\"cl-toggle-mode\" class=\"nav-btn\" style=\"background: #e8f4ff; border: 1px solid #185e96;\">READING Mode ACTIVE<\/button>\n  <button id=\"cl-reset-view\" class=\"nav-btn\" style=\"background: none; border: 1px solid #ccc;\">RESET View<\/button>\n<\/div>\n\n<style>\n#sc-climate-chart {\n  touch-action: pan-y;\n}\n  .sub-tab:focus, .nav-btn:focus { outline: none; box-shadow: none; }\n  .nav-btn { border: none; background: #eee; padding: 5px 12px; border-radius: 20px; font-size: 11px; font-weight: 700; cursor: pointer; color: #666; transition: 0.2s; }\n  .nav-btn.active { background: #3b5875; color: #fff; }\n  .sub-tab { background: none; border: none; padding: 4px 0; font-size: 11px; font-weight: 700; color: #aaa; cursor: pointer; transition: all 0.2s; border-bottom: 2px solid transparent; white-space: nowrap; }\n  .sub-tab.active { color: #3b5875; border-bottom: 2px solid #3b5875; }\n  .date-arrow { background: none; border: none; color: #bbb; cursor: pointer; font-size: 20px; padding: 0 1px; transition: 0.2s; display: flex; align-items: center; justify-content: center; }\n  .date-arrow:hover { color: #3b5875; }\n  .date-arrow:disabled { color: #eee; cursor: not-allowed; }\n<\/style>\n\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/hammerjs@2.0.8\"><\/script>\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chartjs-plugin-zoom@2.2.0\"><\/script>\n\n<script>\n(function() {\n  let climateChart;\n let isReadingMode = true;\n  let currentType = 'temp';\n  let currentTime = 'week';\n  let rawData = [];\n  let activeDate = new Date();\n  const stationPath = 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/santacruz\/';\n  \n \/\/ SANTA CRUZ Define Station Start Date - Fully synchronized with datePicker.min\n  const stationStartDate = new Date(\"2026-01-03\");\n\n  const typeConfig = {\n    temp:  { label: 'Temperature', color: '#e67e22', unit: '\u00b0C', hArray: 'temperature', hKey: 'c', yKey: 'temp_max', sKey: 'temp_max' },\n    hum:   { label: 'Humidity',    color: '#2980b9', unit: '%',  hArray: 'humidity',    hKey: 'v', yKey: 'hum_max', sKey: 'hum_max' },\n    rain:  { label: 'Total Rain',  color: '#3498db', unit: 'mm', hArray: null,           hKey: null,yKey: 'rain_total', sKey: 'rain_total' },\n    solar: { label: 'Solar Peak',  color: '#f1c40f', unit: ' W\/m\u00b2', uvUnit: ' Index', hArray: 'solar',    hKey: 'sol', yKey: 'solar_max',  sKey: 'solar_max', yieldKey: 'solar_yield' },\n    pres:  { label: 'Pressure',    color: '#16a085', unit: 'hPa', hArray: 'pressure',   hKey: 'hPa',yKey: 'pressure_max', sKey: 'pressure_max' }\n  };\n\n  \/\/ Helper to get YYYY-MM-DD string for clean comparison\n  const getISO = (d) => d.toLocaleString(\"en-CA\", {timeZone: \"Atlantic\/Madeira\", hour12: false}).split(',')[0];\n\n  window.shiftClimateDate = function(offset) {\n    const targetDate = new Date(activeDate);\n    targetDate.setDate(targetDate.getDate() + offset);\n    \n    const todayStr = getISO(new Date());\n    const startStr = getISO(stationStartDate);\n    const targetStr = getISO(targetDate);\n\n    \/\/ Strict boundary block\n    if (targetStr > todayStr || targetStr < startStr) return; \n    \n    activeDate = targetDate;\n    loadClimateData('day');\n  };\n\n  window.setClimateDate = function(dateString) {\n    if (!dateString) return;\n    const selected = new Date(dateString);\n    \n    const todayStr = getISO(new Date());\n    const startStr = getISO(stationStartDate);\n    const selectedStr = getISO(selected);\n\n    if (selectedStr > todayStr || selectedStr < startStr) return;\n    \n    activeDate = selected;\n    loadClimateData('day');\n  };\n\n  async function loadClimateData(timeframe) {\n    currentTime = timeframe;\n    document.querySelectorAll('#sc-climate-time-nav .nav-btn').forEach(b => b.classList.toggle('active', b.id === 'c-btn-'+timeframe));\n    \n   const dateNav = document.getElementById('sc-date-nav-wrapper');\nconst nextBtn = document.getElementById('sc-next-day-btn');\nconst prevBtn = document.getElementById('sc-prev-day-btn'); \/\/ Set ID here\nconst datePicker = document.getElementById('sc-date-picker');\n    \n    dateNav.style.display = (timeframe === 'day') ? 'flex' : 'none';\n\n    if (timeframe === 'day') {\n      const now = new Date();\n      const todayStr = getISO(now);\n      const startStr = getISO(stationStartDate);\n      const activeStr = getISO(activeDate);\n\n      \/\/ Set picker boundaries\n      datePicker.min = startStr; \n      datePicker.max = todayStr;\n      \n      \/\/ Toggle Arrow Visibility off beyond the last available day\n      const isAtStart = activeStr === startStr;\n      const isAtEnd = activeStr === todayStr;\n\n      if (prevBtn) {\n          prevBtn.style.visibility = isAtStart ? 'hidden' : 'visible';\n          prevBtn.disabled = isAtStart;\n      }\n      if (nextBtn) {\n          nextBtn.style.visibility = isAtEnd ? 'hidden' : 'visible';\n          nextBtn.disabled = isAtEnd;\n      } \n\n \/\/ Display date to Madeira Timezone\n      document.getElementById('sc-display-date').innerText = activeDate.toLocaleDateString('pt-PT', {\n          day: '2-digit', \n          month: '2-digit', \n          timeZone: 'Atlantic\/Madeira'\n      });\n    \n    } else {\n      activeDate = new Date(); \n    }\n\n    try {\n      let daysBuffer = [];\n      const cacheBust = `?t=${Date.now()}`;\n\n      if (timeframe === 'day') {\n        const dayFile = `${activeDate.getFullYear()}-${String(activeDate.getMonth() + 1).padStart(2, '0')}-${String(activeDate.getDate()).padStart(2, '0')}.json`;\n        \n        const res = await fetch(`${stationPath}${dayFile}${cacheBust}`);\n        if(!res.ok) throw new Error(\"Day file not found\");\n        const dayRaw = await res.json();\n        \nconst interval = 300000; \/\/ 5 minutes points for daily view \n        const buckets = {};\n\n        dayRaw.forEach(d => {\n          const bucketTime = Math.floor(d.time \/ interval) * interval;\n          let val;\n\n\/\/ Identify value to compare for the \"Max\" in this bucket\n          if (currentType === 'temp') val = d.temp?.c;\n          else if (currentType === 'hum') val = d.humidity;\n          else if (currentType === 'solar') val = d.solarRadiation;\n          else if (currentType === 'pres') val = d.pressure?.hPa;\n          else if (currentType === 'rain') val = d.precipRate?.mm;\n\n          if (val !== undefined && val !== null) {\n\n\/\/ If this value is higher than what we have for this time slot, keep it\nif (!buckets[bucketTime] || val > buckets[bucketTime]._maxVal) {\n              buckets[bucketTime] = { \n                ...d, \n                time: bucketTime, \n                _maxVal: val \n              };\n            }\n          }\n        });\n        rawData = Object.values(buckets).sort((a, b) => a.time - b.time);\n      } else if (timeframe === 'month') {\n        const res1 = await fetch(`${stationPath}${activeDate.getFullYear()}-${String(activeDate.getMonth() + 1).padStart(2, '0')}.summary.json${cacheBust}`);\n        const data1 = await res1.json();\n        daysBuffer = data1.days || [];\n        if (daysBuffer.length < 30) {\n          const prevDate = new Date(activeDate.getFullYear(), activeDate.getMonth() - 1, 1);\n          try {\n            const res2 = await fetch(`${stationPath}${prevDate.getFullYear()}-${String(prevDate.getMonth() + 1).padStart(2, '0')}.summary.json${cacheBust}`);\n            const data2 = await res2.json();\n            daysBuffer = [...(data2.days || []), ...daysBuffer];\n          } catch(e) { console.log(\"Prev month not found\"); }\n        }\n        rawData = daysBuffer.slice(-30);\n      } else {\n        let fileName = (timeframe === 'year') ? activeDate.getFullYear() + '.summary.json' : 'week.summary.json';\n        const res = await fetch(`${stationPath}${fileName}${cacheBust}`);\n        const json = await res.json();\n        rawData = json.days || [];\n      }\n      renderChart();\n    } catch (e) { \n        console.error(e);\n        if (climateChart) climateChart.destroy();\n    }\n  }\n\n  window.switchClimateType = function(type) {\n    currentType = type;\n    document.querySelectorAll('.sub-tab').forEach(t => t.classList.toggle('active', t.id === 'tab-'+type));\n    \n    \/\/ Ensure day view re-calculates peaks for the new sensor type\n    if (currentTime === 'day') {\n      loadClimateData('day');\n    } else {\n      renderChart();\n    }\n  };\n\n  \/\/ renderChart function \n  function renderChart() {\n    const ctx = document.getElementById('sc-climate-chart').getContext('2d');\n    if (climateChart) climateChart.destroy();\n\n    const cfg = typeConfig[currentType];\n            const isYear = currentTime === 'year' || currentTime === 'month' || (currentTime === 'week' && (currentType === 'temp' || currentType === 'hum' || currentType === 'rain'));\n    let datasets = [];\n\n    if (isYear) {\nlet maxData = [], minData = [], yieldData = [], rainRateData = [], uvMaxData = [];\n      const grouped = rawData.reduce((acc, curr) => {\n        const date = curr.t ? curr.t.split(' ')[0] : curr.date;\n        if (!acc[date]) acc[date] = [];\n        acc[date].push(curr);\n        return acc;\n      }, {});\n\n        let lastBaseTime = null;\n      Object.keys(grouped).sort().forEach(date => {\n        const dayBlocks = grouped[date];\n        const baseTime = new Date(date.replace(\/-\/g, \"\/\")).getTime();\n        \n\/\/ Define tiered threshold for the interruption of the graph based on gap time\n let bandThreshold = 90000000; \/\/ 25h for Week\n if (currentTime === 'month') bandThreshold = 176400000; \n\/\/ 49h for Month\n if (currentTime === 'year')  bandThreshold = 604800000; \/\/ 7 days for Year\n\n\/\/ If gap exceeds threshold, insert null break for both lines\n        if (lastBaseTime && (baseTime - lastBaseTime) > bandThreshold) {\n  const breakTime = baseTime - 3600000;\n  maxData.push({ x: breakTime, y: null });\n  minData.push({ x: breakTime, y: null });\n  rainRateData.push({ x: breakTime, y: null });\n  yieldData.push({ x: breakTime, y: null });\n  uvMaxData.push({ x: breakTime, y: null });\n}\n        lastBaseTime = baseTime;\n        const hiKey = cfg.yKey;\n        const loKey = cfg.yKey.replace('hum_max', 'hum_min').replace('pressure_max', 'pressure_min').replace('temp_max', 'temp_min');\n        \n        const highs = dayBlocks.map(d => {\n            let v = d[hiKey];\n            if ((v === undefined || v === 0) && d.stats) v = d.stats[cfg.sKey];\n                        \n\/\/ Allow both Rain and Temp to record 0, but treat 0 as null for everything else\nreturn (v === 0 && !['rain', 'temp'].includes(currentType)) ? null : v;\n}).filter(v => v !== null); \n\n        if (currentType === 'rain') {\n            const rates = dayBlocks.map(d => d.rain_max_rate || (d.stats ? d.stats.rain_max_rate : 0));\n            rainRateData.push({ x: baseTime, y: Math.max(...rates) || 0 });\n        }\n\n        const lows = dayBlocks.map(d => {\n            let v = d[loKey];\n            if ((v === undefined || v === 0) && d.stats) {\n                const sMinKey = cfg.sKey.replace('max', 'min').replace('total', 'min');\n                v = d.stats[sMinKey];\n            }\n            return v;\n                        }).filter(v => v !== undefined && v !== null && (currentType !== 'temp' || (v !== 99 && v !== -99)) && (currentType !== 'hum' || v > 0));\n\n        \n               if (highs.length > 0) {\n            maxData.push({ x: baseTime, y: Math.max(...highs) });\n            if (currentType === 'solar') {\n                const firstDay = dayBlocks[0];\n                const stats = firstDay.stats || {};\n                const yld = firstDay.solar_yield || stats.solar_yield || 0;\n                const uv = firstDay.uv_max || stats.uv_max || 0;\n                yieldData.push({ x: baseTime, y: yld });\n                \n\/\/ Apply UV x40 multiplier here for visual flow\n    uvMaxData.push({ x: baseTime, y: uv * 40 });\n            }\n            if (lows.length > 0 && !['rain', 'solar'].includes(currentType)) {\n                minData.push({ x: baseTime, y: Math.min(...lows) });\n            }\n        }\n      });\n\n     \/\/ 1. Optional background layers (Rain Rate \/ Solar Harvest)\n      if (currentType === 'rain') {\n        datasets.push({ label: 'Max Rain Rate', data: rainRateData.sort((a,b) => a.x - b.x), borderColor: '#2ecc71', borderWidth: 1.2, borderDash: [4, 4], pointRadius: 0, fill: false, tension: 0.4 });\n      }\n      if (currentType === 'solar') {\n        datasets.push({ label: 'Daily Harvest', data: yieldData.sort((a,b) => a.x - b.x), borderColor: '#f39c12', backgroundColor: '#f1c40f25', borderWidth: 1.5, fill: true, pointRadius: 0, tension: 0.4 });\n      }\n\n      \/\/ 2. The MAX Line (The ceiling of our band)\n      datasets.push({\n        label: currentType === 'rain' ? 'Total Rain' : cfg.label + ' Max',\n        data: maxData.sort((a,b) => a.x - b.x),\nspanGaps: false,\n        borderColor: cfg.color, \n        backgroundColor: cfg.color + '15', \n        borderWidth: 1.5, \n        pointRadius: 0, \n\/\/ Paint between Min and Max line ('+1'). Otherwise, fill to floor\n        fill: (minData.length > 0) ? '+1' : true, \n        tension: 0.3\n      });\n\n \/\/ 3. The MIN Line (The floor of our band) - MUST be immediately after Max\n      if (minData.length > 0) {\n        datasets.push({ \n          label: cfg.label + ' Min', \n          data: minData.sort((a,b) => a.x - b.x), \nspanGaps: false,\n          borderColor: cfg.color, \n          borderDash: [4, 4], \n          borderWidth: 1.2, \n          pointRadius: 0, \n          fill: false, \/\/ This stops it from painting to the floor from Min line\n          tension: 0.3 \n        });\n      }\n\n\/\/ 4. UV Line ONLY for the YEAR (No fill)\n      if (currentTime === 'year' && currentType === 'solar') {\n          datasets.push({ \n              label: 'UV Index Max', \n              data: uvMaxData.sort((a,b) => a.x - b.x), \n              borderColor: '#9e6ab8', \n              borderWidth: 0.8, \n              borderDash: [2, 2], \n              pointRadius: 0.1, \n              fill: false, \n              tension: 0.4 \n          });\n      }\n\n    \n    } else {\n      let chartData = [];\n      let harvestPoints = [];\n      let rainRatePoints = [];\n\n let lastX = null;\n      rawData.forEach((day, i) => { \/\/ Added 'i' for index\n        let xVal, yVal;\n        if (currentTime === 'day') {\n            xVal = day.time;\n\n \/\/ --- GAP DETECTOR START ---\n\/\/ If the gap between points is > 10 minutes, place null\n    if (i > 0 && (xVal - rawData[i-1].time) > 600000) {\n      chartData.push({ x: xVal - 1, y: null });\n    }\n \/\/ --- GAP DETECTOR END ---\n\n            if (currentType === 'temp') yVal = day.temp?.c;\n            else if (currentType === 'hum') yVal = day.humidity;\n            else if (currentType === 'solar') yVal = day.solarRadiation;\n            else if (currentType === 'pres') yVal = day.pressure?.hPa;\n            else if (currentType === 'rain') yVal = day.precipRate?.mm;\n        } else {\n            const timeKey = day.t || day.date;\n            if (!timeKey) return;\n            if (currentType === 'rain') {\n                const dayTime = timeKey.includes(' ') ? new Date(timeKey.split(' ')[0] + \"T12:00:00Z\").getTime() : new Date(timeKey + \"T12:00:00Z\").getTime();\n                rainRatePoints.push({ x: dayTime, y: day.rain_max_rate || (day.stats ? day.stats.rain_max_rate : 0) });\n            }\n           if (cfg.hArray && currentType !== 'solar' && day.hourly && day.hourly[cfg.hArray]?.length > 0) {\n                const points = day.hourly[cfg.hArray].sort((a, b) => a.t - b.t);\n\n\/\/ Define how much silence we allow before breaking the line\n let gapThreshold = 90000000; \n\/\/ 25 hours (for 7-Day view)\n\nif (currentTime === 'month') gapThreshold = 176400000; \n\/\/ 49 hours (for 30-Day view)\n                \nif (currentTime === 'year')  gapThreshold = 604800000; \n\/\/ 168 hours, 7 days- Year view\n\n points.forEach(p => {\nif (lastX && (p.t - lastX) > gapThreshold) {\n\/\/ Place null point 1 minute before the new data to break the line cleanly\n                        chartData.push({ x: p.t - 60000, y: null }); \n                    }\n                    chartData.push({ x: p.t, y: p[cfg.hKey] });\n                    lastX = p.t;\n                });\n                return;\n            }\n                                    xVal = timeKey.includes('T') ? new Date(timeKey).getTime() : new Date(timeKey + \"T12:00:00Z\").getTime();\n            \n            \/\/ Define threshold here as well to ensure Summary views (30d\/Year) follow the rules\n            let summaryThreshold = 90000000; \n            if (currentTime === 'month') summaryThreshold = 176400000; \n            if (currentTime === 'year')  summaryThreshold = 604800000;\n\n            if (lastX && (xVal - lastX) > summaryThreshold) {\n\/\/ Break cleanly 1 hour before the next summary point\n                  const breakTime = xVal - 3600000;\n    chartData.push({ x: breakTime, y: null });\n    rainRatePoints.push({ x: breakTime, y: null });\n    harvestPoints.push({ x: breakTime, y: null });\n}\n            lastX = xVal;\n            yVal = day.stats ? day.stats[cfg.sKey] : (day[cfg.sKey] || day[cfg.yKey]);\n            if (currentType === 'solar') {\n                const dayTime = timeKey.includes(' ') ? new Date(timeKey.split(' ')[0] + \"T12:00:00Z\").getTime() : new Date(timeKey + \"T12:00:00Z\").getTime();\n                harvestPoints.push({ x: dayTime, y: day.solar_yield || (day.stats ? day.stats.solar_yield : 0) });\n            }\n        }\n        if (yVal !== undefined && yVal !== null) chartData.push({ x: xVal, y: yVal });\n      });\n\n      if (currentType === 'rain' && currentTime !== 'day') {\n          datasets.push({ label: 'Max Rain Rate', data: rainRatePoints.sort((a,b) => a.x - b.x), borderColor: '#2ecc71', borderWidth: 1.2, borderDash: [4, 4], pointRadius: 0, fill: false, tension: 0.4 });\n      }\n      if (currentType === 'solar' && currentTime !== 'day') {\n          datasets.push({ label: 'Daily Harvest', data: harvestPoints.sort((a,b) => a.x - b.x), borderColor: '#f39c12', borderWidth: 1.5, backgroundColor: '#f1c40f25', fill: true, pointRadius: 0, tension: 0.4 });\n      }\n\ndatasets.push({\n  label: (currentType === 'rain' && currentTime === 'day') ? 'Rain Rate' : (currentType === 'rain' ? 'Total Rain' : cfg.label),\n  data: chartData.sort((a,b) => a.x - b.x),\nspanGaps: false,\n  \n\/\/ This line prints dashes only when viewing Rain Rate in Day view\n \/\/ borderDash: (currentType === 'rain' && currentTime === 'day') ? [4, 4] : [],\n\n  \/\/ Use green if it's the rain rate in day view; otherwise use the config color (Blue for Rain)\n  borderColor: (currentType === 'rain' && currentTime === 'day') ? '#2ecc71' : cfg.color,\n\/\/ We use a low opacity hex (15 or 10) for a gentle fill\n  backgroundColor: (currentType === 'rain' && currentTime === 'day') ? '#2ecc7115' : (currentType === 'solar' ? cfg.color + '20' : cfg.color + '15'),\n\n  borderWidth: 2,\n  pointRadius: 0,\n  \/\/ Ensure fill is true for solar even in day view\n  fill: true, \n  tension: 0.4\n});\n    }\n\n    climateChart = new Chart(ctx, {\n      type: 'line',\n      data: { datasets: datasets },\n      options: {\n        responsive: true, maintainAspectRatio: false,\n        interaction: { mode: 'index', intersect: false },\n        scales: {\n          x: { \n  type: 'linear', \n\/\/ 'currentTime' match script's variable name and stretch day graph edge to edge\n             min: (() => {\n  if (currentTime === 'day') return new Date(getISO(activeDate) + 'T00:00:00').getTime();\n  if (currentTime === 'week') return Date.now() - (7 * 24 * 60 * 60 * 1000);\n  if (currentTime === 'month') return Date.now() - (30 * 24 * 60 * 60 * 1000);\n  return undefined;\n})(),\nmax: (() => {\n  if (currentTime === 'day') return new Date(getISO(activeDate) + 'T24:00:00').getTime();\n  if (currentTime === 'week' || currentTime === 'month') return Date.now();\n  return undefined;\n})(),\n\n                                    ticks: { \n              autoSkip: true,\n              \/\/ Higher limits allow the \"super resolution\" look\n  maxTicksLimit: currentTime === 'day' ? 12 : (currentTime === 'week' ? 14 : 11),\n      \/\/ Setting a smaller stepSize forces the chart to consider more intervals\n   stepSize: currentTime === 'day' ? (60 * 60 * 1000) : (24 * 60 * 60 * 1000),\n              font: { size: 9, weight: '600' }, \n              callback: v => {\n                const d = new Date(v);\n                if (currentTime === 'day') {\n                    return d.toLocaleString('en-US', {hour: 'numeric', hour12: false, timeZone: 'Atlantic\/Madeira'}) + ':00';\n                }\n                if (currentTime === 'year') return (d.getMonth() + 1) + '\/' + String(d.getFullYear()).slice(-2);\n                return d.getDate() + '\/' + (d.getMonth() + 1);\n              }\n            }, \n            grid: { display: false } \n          },\n      y: { \n    beginAtZero: (currentType === 'rain' || currentType === 'solar'),\n    \/\/ Only use grace for Rain\/Solar\/Temp; Humidity and Pressure should be tight\n    grace: (currentType === 'rain' || currentType === 'solar' || currentType === 'temp') ? '2%' : 0,\n    suggestedMin: currentType === 'pres' ? 1000 : (currentType === 'temp' ? 10 : (currentType === 'hum' ? 10 : null)),\n    suggestedMax: currentType === 'pres' ? 1035 : (currentType === 'temp' ? 30 : \n(currentType === 'solar' ? 900 :\n(currentType === 'hum' ? 100 : (currentType === 'rain' ? 15 : null)))),\n    ticks: { \n        font: { size: 10 },\n        \/\/ Ensure humidity never shows labels above 100\n        callback: function(value) {\n            if (currentType === 'hum' && value > 100) return null;\n            return value;\n        }\n    }, \n    grid: { color: 'rgba(0,0,0,0.03)' } \n } \n    }, \n         plugins: {\n          legend: { display: false },\n          zoom: {\n            \/\/ Use limits to prevent scrolling into empty dates\n            limits: { x: { min: 'original', max: 'original' } },\n            pan: { enabled: !isReadingMode, mode: 'x' },\n            zoom: {\n              wheel: { enabled: true },\n              pinch: { enabled: true },\n              mode: 'x'\n            }\n          },\n          tooltip: {\n            enabled: isReadingMode,\n            callbacks: {\n              title: (items) => {\n                const d = new Date(items[0].parsed.x);\n                return (isYear || currentType === 'solar' || currentType === 'rain') && currentTime !== 'day'\n  ? d.toLocaleDateString('en-GB', { day:'numeric', month:'short', year: 'numeric', timeZone: 'Atlantic\/Madeira' })\n  : d.toLocaleString('en-GB', { day:'numeric', month:'short', hour:'2-digit', minute:'2-digit', hour12: false, timeZone: 'Atlantic\/Madeira' });\n\/\/ Added hour12: false for 24h format\n              },\n             label: (ctx) => {\n    let unit = cfg.unit;\n    let label = ctx.dataset.label;\n    let value = ctx.parsed.y; \/\/ Default value\n\n    if (label === 'Daily Harvest') unit = ' Wh\/m\u00b2';\n    if (label === 'Max Rain Rate' || (label === 'Rain Rate' && currentTime === 'day')) unit = ' mm\/h';\n    \n    \/\/ Handle UV Index: Divide by 40 to show real index and use uvUnit\n    if (label === 'UV Index Max') {\n        unit = cfg.uvUnit;\n        value = value \/ 40; \n    }\n    \n\/\/ Round to whole numbers for Humidity, and Solar\nconst formattedValue = (['hum', 'solar'].includes(currentType)) \n    ? Math.round(value) \n    : value.toFixed(1); \/\/ Keeps decimal for Temp, Rain, and Pressure\n\n    return `${label}: ${formattedValue}${unit}`;\n\n}\n            }\n          }\n        }\n      }\n    });\n      \/\/ Auto-hide tooltip 5s after the user finishes touching the chart\n    let climateTimer; \/\/ Unique name to avoid conflict with other widgets\n    const chartCanvas = document.getElementById('sc-climate-chart');\n    chartCanvas.addEventListener('touchend', () => {\n      clearTimeout(climateTimer);\n      climateTimer = setTimeout(() => {\n        if (climateChart?.tooltip) {\n    \n\/\/ Clear highlight points and hide tooltip\n          climateChart.setActiveElements([]); \n          climateChart.tooltip.setActiveElements([], { x: 0, y: 0 });\n          climateChart.update();\n        }\n      }, 5000);\n    });\n  }\n\n  loadClimateData('week');\n  window.loadClimateData = loadClimateData;\n\/\/ Toggle Mode\ndocument.getElementById('cl-toggle-mode').addEventListener('click', (e) => {\n  isReadingMode = !isReadingMode;\n  e.target.textContent = isReadingMode ? \"READING Mode ACTIVE\" : \"ZOOM\/PAN Mode ACTIVE\";\n  \n  if (climateChart) {\n    climateChart.options.plugins.tooltip.enabled = isReadingMode;\n    climateChart.options.plugins.zoom.pan.enabled = !isReadingMode;\n    climateChart.update('none');\n  }\n});\n\n\/\/ Reset Zoom\ndocument.getElementById('cl-reset-view').addEventListener('click', () => {\n  if (climateChart) climateChart.resetZoom();\n});\n\n})();\n<\/script>\n-->\n\n\n\n<!-- Brilliant Climate day picker, 7 days, 30 days and year, temp, hum, rain, solar and pressure evolution - NEW CloudBase, UV and zoom added 25-05-2026 -->\n\n<div class=\"sc-pro-cockpit climate-vault\" style=\"font-family: 'Inter', sans-serif; background: rgba(250, 250, 250,0.4); padding: 15px; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.05); margin-top: 20px;\">\n  <div class=\"sc-header-flex\" style=\"display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px;\">\n    <div style=\"display: flex; flex-direction: column; gap: 8px;\">\n      <div id=\"sc-climate-time-nav\" style=\"display: flex; gap: 8px; align-items: center;\">\n        <button onclick=\"loadClimateData('day')\" id=\"c-btn-day\" class=\"nav-btn\">Today<\/button>\n        <button onclick=\"loadClimateData('week')\" id=\"c-btn-week\" class=\"nav-btn active\">7 Days<\/button>\n        <button onclick=\"loadClimateData('month')\" id=\"c-btn-month\" class=\"nav-btn\">30 Days<\/button>\n        <button onclick=\"loadClimateData('year')\" id=\"c-btn-year\" class=\"nav-btn\">Year<\/button>\n      <\/div>\n      \n      <div id=\"sc-date-nav-wrapper\" style=\"display: none; align-items: center; gap: 10px; padding-left: 5px;\">\n        <button id=\"sc-prev-day-btn\" onclick=\"shiftClimateDate(-1)\" class=\"date-arrow\">\u276e<\/button>\n        <span id=\"sc-display-date\" onclick=\"document.getElementById('sc-date-picker').showPicker()\" style=\"font-size: 11px; font-weight: 700; color: #3b5875; cursor: pointer; min-width: 50px; text-align: center;\">&#8211;\/&#8211;<\/span>\n        <input type=\"date\" id=\"sc-date-picker\" style=\"position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;\" onchange=\"setClimateDate(this.value)\">\n        <button id=\"sc-next-day-btn\" onclick=\"shiftClimateDate(1)\" class=\"date-arrow\">\u276f<\/button>\n      <\/div>\n    <\/div>\n    \n    <div style=\"font-size: 11px; color: #999; font-weight: 600; letter-spacing: 0.5px; text-align: right; padding-top: 5px;\">SANTA CRUZ CLIMATE<\/div>\n  <\/div>\n\n  <div class=\"sc-sub-nav\" style=\"display: flex; gap: 15px; margin-bottom: 15px; border-bottom: 1px solid #eee; padding-bottom: 10px; overflow-x: auto;\">\n    <button onclick=\"switchClimateType('temp')\" id=\"tab-temp\" class=\"sub-tab active\">Temp<\/button>\n    <button onclick=\"switchClimateType('hum')\" id=\"tab-hum\" class=\"sub-tab\">Hum<\/button>\n    <button onclick=\"switchClimateType('cloud')\" id=\"tab-cloud\" class=\"sub-tab\">Cloud<\/button>\n    <button onclick=\"switchClimateType('rain')\" id=\"tab-rain\" class=\"sub-tab\">Rain<\/button>\n    <button onclick=\"switchClimateType('solar')\" id=\"tab-solar\" class=\"sub-tab\">Sun<\/button>\n    <button onclick=\"switchClimateType('uv')\" id=\"tab-uv\" class=\"sub-tab\">UV<\/button>\n    <button onclick=\"switchClimateType('pres')\" id=\"tab-pres\" class=\"sub-tab\">Pressure<\/button>\n  <\/div>\n\n  <div style=\"height: 300px; position: relative;\">\n    <canvas id=\"sc-climate-chart\"><\/canvas>\n  <\/div>\n<\/div>\n\n<div style=\"display: flex; justify-content: center; gap: 20px; margin-top: -10px; padding-bottom: 15px;\">\n  <button id=\"cl-toggle-mode\" class=\"nav-btn\" style=\"background: #e8f4ff; border: 1px solid #185e96;\">READING Mode ACTIVE<\/button>\n  <button id=\"cl-reset-view\" class=\"nav-btn\" style=\"background: none; border: 1px solid #ccc;\">RESET View<\/button>\n<\/div>\n\n<style>\n\n#sc-climate-chart {\n  touch-action: pan-y;\n}\n\n  .sub-tab:focus, .nav-btn:focus { outline: none; box-shadow: none; }\n  .nav-btn { border: none; background: #eee; padding: 5px 12px; border-radius: 20px; font-size: 11px; font-weight: 700; cursor: pointer; color: #666; transition: 0.2s; }\n  .nav-btn.active { background: #3b5875; color: #fff; }\n  .sub-tab { background: none; border: none; padding: 4px 0; font-size: 11px; font-weight: 700; color: #aaa; cursor: pointer; transition: all 0.2s; border-bottom: 2px solid transparent; white-space: nowrap; }\n  .sub-tab.active { color: #3b5875; border-bottom: 2px solid #3b5875; }\n  .date-arrow { background: none; border: none; color: #bbb; cursor: pointer; font-size: 20px; padding: 0 1px; transition: 0.2s; display: flex; align-items: center; justify-content: center; }\n  .date-arrow:hover { color: #3b5875; }\n  .date-arrow:disabled { color: #eee; cursor: not-allowed; }\n<\/style>\n\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/hammerjs@2.0.8\"><\/script>\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chartjs-plugin-zoom@2.2.0\"><\/script>\n\n<script>\n(function() {\n  let climateChart;\n  let isReadingMode = true;\n  let currentType = 'temp';\n  let currentTime = 'week';\n  let rawData = [];\n  let activeDate = new Date();\n  const stationPath = 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/santacruz\/';\n  \n  \/\/ SANTA CRUZ Define Station Start Date - Fully synchronized with datePicker.min\n  const stationStartDate = new Date(\"2026-01-03\");\n\n  const typeConfig = {\n    temp:  { label: 'Temperature', color: '#e67e22', unit: '\u00b0C', hArray: 'temperature', hKey: 'c', yKey: 'temp_max', sKey: 'temp_max' },\n    hum:   { label: 'Humidity',    color: '#2980b9', unit: '%',  hArray: 'humidity',    hKey: 'v', yKey: 'hum_max', sKey: 'hum_max' },\n    cloud: { \n        label: 'Cloudbase', \n        color: '#7f8c8d', \n        unit: 'm', \n        hArray: 'humidity', \/\/ We use this to trigger the hourly logic loop\n        hKey: 'v', \n        yKey: 'cloud_max',  \/\/ Key for Year\/Month\/Week summaries\n        sKey: 'cloud_max'   \/\/ Key for Daily stats\n    },\n    rain:  { label: 'Total Rain',  color: '#3498db', unit: 'mm', hArray: null,           hKey: null,yKey: 'rain_total', sKey: 'rain_total' },\n        solar: { label: 'Solar Peak',  color: '#f1c40f', unit: ' W\/m\u00b2', uvUnit: '', hArray: 'solar',    hKey: 'sol', yKey: 'solar_max',  sKey: 'solar_max', yieldKey: 'solar_yield' },\n    uv:    { label: 'UV Index',    color: '#9e6ab8', unit: '', hArray: null,       hKey: null, yKey: 'uv_max',    sKey: 'uv_max' },\n    pres:  { label: 'Pressure',    color: '#16a085', unit: 'hPa', hArray: 'pressure',   hKey: 'hPa',yKey: 'pressure_max', sKey: 'pressure_max' }\n  };\n\n  \/\/ Helper to get YYYY-MM-DD string for clean comparison\n  const getISO = (d) => d.toLocaleString(\"en-CA\", {timeZone: \"Atlantic\/Madeira\", hour12: false}).split(',')[0];\n\n  const getCloudbase = (t, dp) => {\n    if (t == null || dp == null) return null;\n    \/\/ Formula: (Temp - Dewpoint) * 125 + station_height\n    const base = ((t - dp) * 125) + 125;\n    return base > 0 ? base : 0; \n  };\n\n  window.shiftClimateDate = function(offset) {\n    const targetDate = new Date(activeDate);\n    targetDate.setDate(targetDate.getDate() + offset);\n    \n    const todayStr = getISO(new Date());\n    const startStr = getISO(stationStartDate);\n    const targetStr = getISO(targetDate);\n\n    \/\/ Strict boundary block\n    if (targetStr > todayStr || targetStr < startStr) return; \n    \n    activeDate = targetDate;\n    loadClimateData('day');\n  };\n\n  window.setClimateDate = function(dateString) {\n    if (!dateString) return;\n    const selected = new Date(dateString);\n    \n    const todayStr = getISO(new Date());\n    const startStr = getISO(stationStartDate);\n    const selectedStr = getISO(selected);\n\n    if (selectedStr > todayStr || selectedStr < startStr) return;\n    \n    activeDate = selected;\n    loadClimateData('day');\n  };\n\n  async function loadClimateData(timeframe) {\n    currentTime = timeframe;\n    document.querySelectorAll('#sc-climate-time-nav .nav-btn').forEach(b => b.classList.toggle('active', b.id === 'c-btn-'+timeframe));\n    \n    const dateNav = document.getElementById('sc-date-nav-wrapper');\n    const nextBtn = document.getElementById('sc-next-day-btn');\n    const prevBtn = document.getElementById('sc-prev-day-btn'); \/\/ Set ID here\n    const datePicker = document.getElementById('sc-date-picker');\n    \n    dateNav.style.display = (timeframe === 'day') ? 'flex' : 'none';\n\n    if (timeframe === 'day') {\n      const now = new Date();\n      const todayStr = getISO(now);\n      const startStr = getISO(stationStartDate);\n      const activeStr = getISO(activeDate);\n\n      \/\/ Set picker boundaries\n      datePicker.min = startStr; \n      datePicker.max = todayStr;\n      \n      \/\/ Toggle Arrow Visibility off beyond the last available day\n      const isAtStart = activeStr === startStr;\n      const isAtEnd = activeStr === todayStr;\n\n      if (prevBtn) {\n          prevBtn.style.visibility = isAtStart ? 'hidden' : 'visible';\n          prevBtn.disabled = isAtStart;\n      }\n      if (nextBtn) {\n          nextBtn.style.visibility = isAtEnd ? 'hidden' : 'visible';\n          nextBtn.disabled = isAtEnd;\n      }\n\n      \/\/ Display date Madeira Timezone\n      document.getElementById('sc-display-date').innerText = activeDate.toLocaleDateString('pt-PT', {\n          day: '2-digit', \n          month: '2-digit', \n          timeZone: 'Atlantic\/Madeira'\n      });\n    \n    } else {\n      activeDate = new Date(); \n    }\n\n    try {\n      let daysBuffer = [];\n      const cacheBust = `?t=${Date.now()}`;\n\n      if (timeframe === 'day') {\n        const dayFile = `${activeDate.getFullYear()}-${String(activeDate.getMonth() + 1).padStart(2, '0')}-${String(activeDate.getDate()).padStart(2, '0')}.json`;\n        \n        const res = await fetch(`${stationPath}${dayFile}${cacheBust}`);\n        if(!res.ok) throw new Error(\"Day file not found\");\n        const dayRaw = await res.json();\n        \n        \/\/ Define intervals: 10 mins for Cloud, 5 min for everything else\n        const interval = (currentType === 'cloud') ? 600000 : 300000;\n        const buckets = {};\n\n        dayRaw.forEach(d => {\n          const bucketTime = Math.floor(d.time \/ interval) * interval;\n          let val;\n          \n          \/\/ Identify the value to compare for the \"Max\" in this bucket\n          if (currentType === 'temp') val = d.temp?.c;\n          else if (currentType === 'hum') val = d.humidity;\n          else if (currentType === 'cloud') val = getCloudbase(d.temp?.c, d.dewpoint?.c);\n          else if (currentType === 'solar') val = d.solarRadiation;\n          else if (currentType === 'uv') val = d.uv;\n          else if (currentType === 'pres') val = d.pressure?.hPa;\n          else if (currentType === 'rain') val = d.precipRate?.mm;\n\n          if (val !== undefined && val !== null) {\n            \/\/ If this value is higher than what we have for this time slot, keep it\n            if (!buckets[bucketTime] || val > buckets[bucketTime]._maxVal) {\n              buckets[bucketTime] = { \n                ...d, \n                time: bucketTime, \n                _maxVal: val \n              };\n            }\n          }\n        });\n        rawData = Object.values(buckets).sort((a, b) => a.time - b.time);\n\n      } else if (timeframe === 'month') {\n        const res1 = await fetch(`${stationPath}${activeDate.getFullYear()}-${String(activeDate.getMonth() + 1).padStart(2, '0')}.summary.json${cacheBust}`);\n        const data1 = await res1.json();\n        daysBuffer = data1.days || [];\n        if (daysBuffer.length < 30) {\n          const prevDate = new Date(activeDate.getFullYear(), activeDate.getMonth() - 1, 1);\n          try {\n            const res2 = await fetch(`${stationPath}${prevDate.getFullYear()}-${String(prevDate.getMonth() + 1).padStart(2, '0')}.summary.json${cacheBust}`);\n            const data2 = await res2.json();\n            daysBuffer = [...(data2.days || []), ...daysBuffer];\n          } catch(e) { console.log(\"Prev month not found\"); }\n        }\n        rawData = daysBuffer.slice(-30);\n\n      } else {\n        let fileName = (timeframe === 'year') ? activeDate.getFullYear() + '.summary.json' : 'week.summary.json';\n        const res = await fetch(`${stationPath}${fileName}${cacheBust}`);\n        const json = await res.json();\n        rawData = json.days || [];\n\n      }\n      renderChart();\n    } catch (e) { \n        console.error(e);\n        if (climateChart) climateChart.destroy();\n    }\n  }\n\n  window.switchClimateType = function(type) {\n    currentType = type;\n    document.querySelectorAll('.sub-tab').forEach(t => t.classList.toggle('active', t.id === 'tab-'+type));\n    \n    \/\/ Crucial: Re-run bucket logic for the new sensor type if in Daily view\n    if (currentTime === 'day') {\n      loadClimateData('day');\n    } else {\n      renderChart();\n    }\n  };\n\n  \/\/ renderChart function \n  function renderChart() {\n    const ctx = document.getElementById('sc-climate-chart').getContext('2d');\n    if (climateChart) climateChart.destroy();\n\n    const cfg = typeConfig[currentType];\n    const isYear = currentTime === 'year' || currentTime === 'month' || (currentTime === 'week' && (currentType === 'temp' || currentType === 'hum' || currentType === 'rain' || currentType === 'cloud'));\n    let datasets = [];\n\n    if (isYear) {\n      let maxData = [], minData = [], yieldData = [], rainRateData = [], uvMaxData = [];\n      const grouped = rawData.reduce((acc, curr) => {\n        const date = curr.t ? curr.t.split(' ')[0] : curr.date;\n        if (!acc[date]) acc[date] = [];\n        acc[date].push(curr);\n        return acc;\n      }, {});\n\n      let lastBaseTime = null;\n      Object.keys(grouped).sort().forEach(date => {\n        const dayBlocks = grouped[date];\n        const baseTime = new Date(date.replace(\/-\/g, \"\/\")).getTime();\n        \n        \/\/ Define tiered threshold for the High\/Low bands\n        let bandThreshold = 90000000; \/\/ 25h for Week\n        if (currentTime === 'month') bandThreshold = 176400000; \n        \/\/ 49h for Month\n        if (currentTime === 'year')  bandThreshold = 604800000; \/\/ 7 days for Year\n\n        \/\/ If gap exceeds threshold, insert null break for both lines\n        if (lastBaseTime && (baseTime - lastBaseTime) > bandThreshold) {\n          const breakTime = baseTime - 3600000;\n          maxData.push({ x: breakTime, y: null });\n          minData.push({ x: breakTime, y: null });\n          rainRateData.push({ x: breakTime, y: null });\n          yieldData.push({ x: breakTime, y: null });\n          uvMaxData.push({ x: breakTime, y: null });\n        }\n        lastBaseTime = baseTime;\n\n        const hiKey = cfg.yKey;\n        const loKey = cfg.yKey.replace('hum_max', 'hum_min').replace('pressure_max', 'pressure_min').replace('temp_max', 'temp_min').replace('cloud_max', 'cloud_min');\n        \n        const highs = dayBlocks.map(d => {\n            let v = d[hiKey];\n            if ((v === undefined || v === 0) && d.stats) v = d.stats[cfg.sKey];\n            \n\/\/ Allow Rain & Temp to record 0, but treat 0 as null for everything else\n            return (v === 0 && !['rain', 'temp'].includes(currentType)) ? null : v;\n\n        }).filter(v => v !== null); \n\n        if (currentType === 'rain') {\n            const rates = dayBlocks.map(d => d.rain_max_rate || (d.stats ? d.stats.rain_max_rate : 0));\n            rainRateData.push({ x: baseTime, y: Math.max(...rates) || 0 });\n        }\n\n        const lows = dayBlocks.map(d => {\n            let v = d[loKey];\n            if ((v === undefined || v === 0) && d.stats) {\n                const sMinKey = cfg.sKey.replace('max', 'min').replace('total', 'min');\n                v = d.stats[sMinKey];\n            }\n            return v;\n        }).filter(v => v !== undefined && v !== null && (currentType !== 'temp' || (v !== 99 && v !== -99)) && (currentType !== 'hum' || v > 0));\n        \n        if (highs.length > 0 || lows.length > 0) {\n     \/\/ Push Max (or null if missing) to keep the array sequence intact\n  maxData.push({ x: baseTime, y: highs.length > 0 ? Math.max(...highs) : null });\n            \n            if (currentType === 'solar') {\n                const firstDay = dayBlocks[0];\n                const stats = firstDay.stats || {};\n                const yld = firstDay.solar_yield || stats.solar_yield || 0;\n                const uv = firstDay.uv_max || stats.uv_max || 0;\n                yieldData.push({ x: baseTime, y: yld });\n                \n                \/\/ Apply UV x40 multiplier here for visual flow\n                uvMaxData.push({ x: baseTime, y: uv * 40 });\n            }\n            \n       \/\/ Push Min (or null if missing) to maintain perfect index sync with Max\n            if (!['rain', 'solar', 'uv'].includes(currentType)) {\n                minData.push({ x: baseTime, y: lows.length > 0 ? Math.min(...lows) : null });\n            }\n        }\n      });\n\n      \/\/ 1. Optional background layers (Rain Rate \/ Solar Harvest)\n      if (currentType === 'rain') {\n        datasets.push({ label: 'Max Rain Rate', data: rainRateData.sort((a,b) => a.x - b.x), borderColor: '#2ecc71', borderWidth: 1.2, borderDash: [4, 4], pointRadius: 0, fill: false, tension: 0.4 });\n      }\n      if (currentType === 'solar') {\n        datasets.push({ label: 'Daily Harvest', data: yieldData.sort((a,b) => a.x - b.x), borderColor: '#f39c12', backgroundColor: '#f1c40f25', borderWidth: 1.5, fill: true, pointRadius: 0, tension: 0.4 });\n      }\n\n      \/\/ 2. The MAX Line (The ceiling of our band)\n      datasets.push({\n        label: currentType === 'rain' ? 'Total Rain' : cfg.label + ' Max',\n        data: maxData.sort((a,b) => a.x - b.x),\n        spanGaps: false,\n        borderColor: cfg.color, \n        backgroundColor: cfg.color + '15', \n        borderWidth: 1.5, \n        pointRadius: 0, \n        \/\/ Paint between Min and Max line ('+1'). Otherwise, fill to floor\n        \/\/ If cloud, fill to top ('end'). If range exists, fill to min line ('+1'). Else fill to floor.\n        fill: currentType === 'cloud' ? 'end' : ((minData.length > 0) ? '+1' : true),\n        tension: 0.3\n      });\n\n      \/\/ 3. The MIN Line (The floor of our band) - MUST be immediately after Max\n      if (minData.length > 0) {\n        datasets.push({ \n          label: cfg.label + ' Min', \n          data: minData.sort((a,b) => a.x - b.x), \n          spanGaps: false,\n          borderColor: cfg.color, \n          borderDash: [4, 4], \n          borderWidth: 1.2, \n          pointRadius: 0, \n          fill: false, \/\/ This stops it from painting to the floor from Min line\n          tension: 0.3 \n        });\n      }\n\n      \/\/ 4. UV Line ONLY for the YEAR (No fill)\n      if (currentTime === 'year' && currentType === 'solar') {\n          datasets.push({ \n              label: 'UV Index Max', \n              data: uvMaxData.sort((a,b) => a.x - b.x), \n              borderColor: '#9e6ab8', \n              borderWidth: 0.8, \n              borderDash: [2, 2], \n              pointRadius: 0.1, \n              fill: false, \n              tension: 0.4 \n          });\n      }\n\n    \n    } else {\n      let chartData = [];\n      let harvestPoints = [];\n      let rainRatePoints = [];\n      let lastX = null;\n\n      rawData.forEach((day, i) => { \/\/ Added 'i' for index\n        let xVal, yVal;\n        if (currentTime === 'day') {\n            xVal = day.time;\n\n            \/\/ --- GAP DETECTOR START ---\n            \/\/ If the gap between points is > 10 minutes, place null\n            if (i > 0 && (xVal - rawData[i-1].time) > 600000) {\n              chartData.push({ x: xVal - 1, y: null });\n            }\n            \/\/ --- GAP DETECTOR END ---\n\n            if (currentType === 'temp') yVal = day.temp?.c;\n            else if (currentType === 'hum') yVal = day.humidity;\n            else if (currentType === 'cloud') yVal = getCloudbase(day.temp?.c, day.dewpoint?.c);\n                    else if (currentType === 'uv') {\n              const uvRaw = day.uv; \n              yVal = (uvRaw === undefined || uvRaw === null) ? null : uvRaw;\n          }\n          else if (currentType === 'solar') yVal = day.solarRadiation;\n\n            else if (currentType === 'pres') yVal = day.pressure?.hPa;\n            else if (currentType === 'rain') yVal = day.precipRate?.mm;\n        } else {\n            const timeKey = day.t || day.date;\n            if (!timeKey) return;\n            if (currentType === 'rain') {\n                const dayTime = timeKey.includes(' ') ? new Date(timeKey.split(' ')[0] + \"T12:00:00Z\").getTime() : new Date(timeKey + \"T12:00:00Z\").getTime();\n                rainRatePoints.push({ x: dayTime, y: day.rain_max_rate || (day.stats ? day.stats.rain_max_rate : 0) });\n            }\n            if (cfg.hArray && currentType !== 'solar' && currentType !== 'uv' && day.hourly && day.hourly[cfg.hArray]?.length > 0) {\n                const points = day.hourly[cfg.hArray].sort((a, b) => a.t - b.t);\n\n                \/\/ Define how much silence we allow before breaking the line\n                let gapThreshold = 90000000; \n                \/\/ 25 hours (for 7-Day view)\n\n                if (currentTime === 'month') gapThreshold = 176400000; \n                \/\/ 49 hours (for 30-Day view)\n                \n                if (currentTime === 'year')  gapThreshold = 604800000; \n                \/\/ 168 hours, 7 days- Year view\n\n                points.forEach(p => {\n                    if (lastX && (p.t - lastX) > gapThreshold) {\n                        \/\/ Place null point 1 minute before the new data to break the line cleanly\n                        chartData.push({ x: p.t - 60000, y: null }); \n                    }\n                    chartData.push({ x: p.t, y: p[cfg.hKey] });\n                    lastX = p.t;\n                });\n                return;\n            }\n            xVal = timeKey.includes('T') ? new Date(timeKey).getTime() : new Date(timeKey + \"T12:00:00Z\").getTime();\n            \n            \/\/ Define threshold here as well to ensure Summary views (30d\/Year) follow the rules\n            let summaryThreshold = 90000000; \n            if (currentTime === 'month') summaryThreshold = 176400000; \n            if (currentTime === 'year')  summaryThreshold = 604800000;\n\n            if (lastX && (xVal - lastX) > summaryThreshold) {\n                \/\/ Break cleanly 1 hour before the next summary point\n                const breakTime = xVal - 3600000;\n                chartData.push({ x: breakTime, y: null });\n                rainRatePoints.push({ x: breakTime, y: null });\n                harvestPoints.push({ x: breakTime, y: null });\n            }\n                        lastX = xVal;\n            yVal = day.stats ? day.stats[cfg.sKey] : (day[cfg.sKey] || day[cfg.yKey]);\n            if (currentType === 'uv' && (yVal === 0 || yVal === undefined)) yVal = null;\n            if (currentType === 'solar') {\n\n                const dayTime = timeKey.includes(' ') ? new Date(timeKey.split(' ')[0] + \"T12:00:00Z\").getTime() : new Date(timeKey + \"T12:00:00Z\").getTime();\n                harvestPoints.push({ x: dayTime, y: day.solar_yield || (day.stats ? day.stats.solar_yield : 0) });\n            }\n        }\n        if (yVal !== undefined && yVal !== null) chartData.push({ x: xVal, y: yVal });\n      });\n\n      if (currentType === 'rain' && currentTime !== 'day') {\n          datasets.push({ label: 'Max Rain Rate', data: rainRatePoints.sort((a,b) => a.x - b.x), borderColor: '#2ecc71', borderWidth: 1.2, borderDash: [4, 4], pointRadius: 0, fill: false, tension: 0.4 });\n      }\n      if (currentType === 'solar' && currentTime !== 'day') {\n          datasets.push({ label: 'Daily Harvest', data: harvestPoints.sort((a,b) => a.x - b.x), borderColor: '#f39c12', borderWidth: 1.5, backgroundColor: '#f1c40f25', fill: true, pointRadius: 0, tension: 0.4 });\n      }\n\n            datasets.push({\n        label: (currentType === 'rain' && currentTime === 'day') ? 'Rain Rate' : (currentType === 'rain' ? 'Total Rain' : (currentType === 'uv' && currentTime !== 'day' ? 'UV Index Max' : cfg.label)),\n        data: chartData.sort((a,b) => a.x - b.x),\n        spanGaps: false,\n        \n\/\/ Use green for rain rate in day view, otherwise use Blue for Rain total\n        borderColor: (currentType === 'rain' && currentTime === 'day') ? '#2ecc71' : cfg.color,\n\/\/ We use a low opacity hex (15 or 10) for a gentle fill\n        backgroundColor: (currentType === 'rain' && currentTime === 'day') ? '#2ecc7115' : (currentType === 'solar' || currentType === 'uv' ? cfg.color + '20' : cfg.color + '15'),\n       \n        borderWidth: 2,\n        pointRadius: 0,\n        fill: true, \n\/\/ paint to top for clouds, otherwise fill to floor\n        fill: currentType === 'cloud' ? 'end' : true,\n        tension: 0.4\n      });\n    }\n\n    climateChart = new Chart(ctx, {\n      type: 'line',\n      data: { datasets: datasets },\n      options: {\n        responsive: true, maintainAspectRatio: false,\n        interaction: { mode: 'index', intersect: false },\n\n        scales: {\n          x: { \n            type: 'linear', \n            \/\/ 'currentTime' match script's variable name and stretch day graph edge to edge\n            min: (() => {\n              if (currentTime === 'day') return new Date(getISO(activeDate) + 'T00:00:00').getTime();\n              if (currentTime === 'week') return Date.now() - (7 * 24 * 60 * 60 * 1000);\n              if (currentTime === 'month') return Date.now() - (30 * 24 * 60 * 60 * 1000);\n              return undefined;\n            })(),\n            max: (() => {\n              if (currentTime === 'day') return new Date(getISO(activeDate) + 'T24:00:00').getTime();\n              if (currentTime === 'week' || currentTime === 'month') return Date.now();\n              return undefined;\n            })(),\n\n            ticks: { \n              autoSkip: true,\n              \/\/ Higher limits allow the \"super resolution\" look\n              maxTicksLimit: currentTime === 'day' ? 12 : (currentTime === 'week' ? 14 : 11),\n              \/\/ Setting a smaller stepSize forces the chart to consider more intervals\n              stepSize: currentTime === 'day' ? (1 * 60 * 60 * 1000) : (24 * 60 * 60 * 1000),\n              font: { size: 9, weight: '600' }, \n              callback: v => {\n                const d = new Date(v);\n                if (currentTime === 'day') {\n                    return d.toLocaleString('en-US', {hour: 'numeric', hour12: false, timeZone: 'Atlantic\/Madeira'}) + ':00';\n                }\n  if (currentTime === 'year') return d.toLocaleString('en-GB', { month: 'short', timeZone: 'Atlantic\/Madeira' });\n                return d.getDate() + '\/' + (d.getMonth() + 1);\n              }\n            },\n\n            grid: { display: false } \n          },\n          y: { \n            beginAtZero: (currentType === 'rain' || currentType === 'solar' || currentType === 'uv'),\n            \/\/ Only use grace for Rain\/Solar\/Temp\/UV; Humidity and Pressure should be tight\n            grace: (currentType === 'rain' || currentType === 'solar' || currentType === 'temp' || currentType === 'uv') ? '2%' : 0,\n            suggestedMin: currentType === 'pres' ? 1000 : (currentType === 'temp' ? 10 : (currentType === 'hum' ? 10 : null)),\n            suggestedMax: currentType === 'pres' ? 1035 : (currentType === 'temp' ? 30 : \n            (currentType === 'solar' ? 900 :\n            (currentType === 'hum' ? 100 : (currentType === 'rain' ? 15 : (currentType === 'cloud' ? 1200 : (currentType === 'uv' ? 12 : null)))))),\n            ticks: { \n                font: { size: 10 },\n                \/\/ This makes the cloudbase print every 100m on the graph\n                stepSize: currentType === 'cloud' ? 100 : undefined,\n\n                \/\/ Ensure humidity never shows labels above 100\n                callback: function(value) {\n                    if (currentType === 'hum' && value > 100) return null;\n                    return value;\n                }\n            }, \n            grid: { color: 'rgba(0,0,0,0.03)' } \n          } \n        }, \n        plugins: {\n          legend: { display: false },\n          zoom: {\n            \/\/ Use limits to prevent scrolling into empty dates\n            limits: { x: { min: 'original', max: 'original' } },\n            pan: { enabled: !isReadingMode, mode: 'x' },\n            zoom: {\n              wheel: { enabled: true },\n              pinch: { enabled: true },\n              mode: 'x'\n            }\n          },\n          tooltip: {\n            enabled: isReadingMode,\n\n            callbacks: {\n                           title: (items) => {\n                const d = new Date(items[0].parsed.x);\n                return (isYear || currentType === 'solar' || currentType === 'rain' || currentType === 'uv') && currentTime !== 'day'\n                  ? d.toLocaleDateString('en-GB', { day:'numeric', month:'short', year: 'numeric', timeZone: 'Atlantic\/Madeira' })\n                  : d.toLocaleString('en-GB', { day:'numeric', month:'short', hour:'2-digit', minute:'2-digit', hour12: false, timeZone: 'Atlantic\/Madeira' });\n\n\/\/ Added hour12: false for 24h format\n              },\n              label: (ctx) => {\n                let unit = cfg.unit;\n                let label = ctx.dataset.label;\n let value = ctx.parsed.y; \/\/ Default value\n\n if (label === 'Daily Harvest') unit = ' Wh\/m\u00b2';\n if (label === 'Max Rain Rate' || (label === 'Rain Rate' && currentTime === 'day')) unit = ' mm\/h';\n                                      \/\/ Handle UV Index: Divide by 40 to show real index and use uvUnit\n                if (label === 'UV Index Max' && currentType === 'solar') {\n         unit = cfg.uvUnit;\n         value = value \/ 40; \n                }\n                \n\/\/ Round to whole numbers for Cloud, Humidity, and Solar\n                const formattedValue = (['cloud', 'hum', 'solar'].includes(currentType)) \n                    ? Math.round(value) \n                    : value.toFixed(1); \/\/ Keeps decimal for Temp, Rain, Pressure, and UV Index\n             \nreturn `${label}: ${formattedValue}${unit}`;\n              }\n            }\n          }\n        }\n      }\n    });\n    \/\/ Auto-hide tooltip 5s after the user finishes touching the chart\n    let climateTimer; \/\/ Unique name to avoid conflict with other widgets\n    const chartCanvas = document.getElementById('sc-climate-chart');\n    chartCanvas.addEventListener('touchend', () => {\n      clearTimeout(climateTimer);\n      climateTimer = setTimeout(() => {\n        if (climateChart?.tooltip) {\n          \/\/ Clear highlight points and hide tooltip\n          climateChart.setActiveElements([]); \n          climateChart.tooltip.setActiveElements([], { x: 0, y: 0 });\n          climateChart.update();\n        }\n      }, 5000);\n    });\n  }\n\n  loadClimateData('week');\n  window.loadClimateData = loadClimateData;\n\n  \/\/ Toggle Mode\n  document.getElementById('cl-toggle-mode').addEventListener('click', (e) => {\n    isReadingMode = !isReadingMode;\n    e.target.textContent = isReadingMode ? \"READING Mode ACTIVE\" : \"ZOOM\/PAN Mode ACTIVE\";\n    \n    if (climateChart) {\n      climateChart.options.plugins.tooltip.enabled = isReadingMode;\n      climateChart.options.plugins.zoom.pan.enabled = !isReadingMode;\n      climateChart.update('none');\n    }\n  });\n\n  \/\/ Reset Zoom\n  document.getElementById('cl-reset-view').addEventListener('click', () => {\n    if (climateChart) climateChart.resetZoom();\n  });\n\n})();\n<\/script>\n\n\n\n<div style=\"height:16px\" 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<!-- RING OF POWER Version 3 with as close to truth mah as possible from php -->\n<style>\n  @import url('https:\/\/fonts.googleapis.com\/css2?family=Inter:wght@300;400;600&display=swap');\n\n  :root {\n    --ring-pulse-speed: 6s;\n    --ring-color-inner: #999;\n    --ring-color-outer: #555;\n    --ring-glow: rgba(150, 150, 150, 0.2);\n    --ring-opacity: 0.4;\n  }\n\n  .sc-ring-cockpit {\n    font-family: 'Inter', sans-serif;\n    max-width: 300px;\n    margin: 20px auto;\n    text-align: center;\n    background: transparent;\n    position: relative;\n    user-select: none;\n  }\n\n  .sc-ring-container {\n    width: 200px;\n    height: 200px;\n    margin: 0 auto;\n    position: relative;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    justify-content: center;\n    cursor: pointer;\n  }\n\n  \/* The Breathing Logic *\/\n  .sc-ring-core {\n    position: absolute;\n    width: 100%;\n    height: 100%;\n    border-radius: 50%;\n    border: 2px solid var(--ring-color-inner);\n    filter: blur(1px);\n    box-shadow: 0 0 15px var(--ring-glow);\n    opacity: var(--ring-opacity);\n    animation: sc-breathe var(--ring-pulse-speed) ease-in-out infinite;\n    transition: border-color 2s, box-shadow 2s;\n  }\n\n  .sc-ring-halo {\n    position: absolute;\n    width: 80%;\n    height: 80%;\n    border-radius: 50%;\n    border: 6px solid var(--ring-color-outer);\n    opacity: 0.3;\n    filter: blur(4px);\n    transition: border-color 2s;\n  }\n\n  @keyframes sc-breathe {\n    0%, 100% { transform: scale(0.92); opacity: var(--ring-opacity); }\n    50% { transform: scale(1.05); opacity: calc(var(--ring-opacity) + 0.3); }\n  }\n\n  .sc-ring-label {\n    position: relative;\n    z-index: 10;\n    display: flex;\n    flex-direction: column;\n    align-items: center;\n    gap: 2px;\n  }\n\n  .sc-ring-percent { \n    font-size: 28px; \n    font-weight: 400; \n    color: #555; \n    transition: opacity 1s, color 2s;\n  }\n  \n  .sc-ring-mah { \n    font-size: 12px; \n    font-weight: 250; \n    color: #888; \n    letter-spacing: 0.5px;\n  }\n\n  .sc-ring-status { \n    font-size: 10px; \n    font-weight: 700; \n    letter-spacing: 1px; \n    text-transform: uppercase; \n    color: #888;\n    margin-top: 8px;\n    transition: color 2s;\n  }\n\n  \/* Mystery Layer *\/\n  .sc-ring-yield-info {\n    margin-top: 15px;\n    font-size: 11px;\n    font-weight: 400;\n    color: #96752d;\n    opacity: 0;\n    transition: opacity 0.6s ease;\n  }\n\n  .sc-ring-cockpit:hover .sc-ring-yield-info,\n  .sc-ring-cockpit:active .sc-ring-yield-info {\n    opacity: 1;\n  }\n<\/style>\n\n<div class=\"sc-ring-cockpit\">\n  <div class=\"sc-ring-container\">\n    <div id=\"sc-ring-outer\" class=\"sc-ring-halo\"><\/div>\n    <div id=\"sc-ring-inner\" class=\"sc-ring-core\"><\/div>\n    <div class=\"sc-ring-label\">\n      <span id=\"sc-ring-val\" class=\"sc-ring-percent\">&#8211;%<\/span>\n      <span id=\"sc-ring-mah-val\" class=\"sc-ring-mah\">&#8212;- mAh<\/span>\n      <span id=\"sc-ring-txt\" class=\"sc-ring-status\">Initializing<\/span>\n    <\/div>\n  <\/div>\n  <div class=\"sc-ring-yield-info\" id=\"sc-ring-yield-info\">\n    Solar Ring of Power <\/br> Autonomy Yield: 0 \/ 2500 Wh\/m\u00b2\n  <\/div>\n<\/div>\n\n<script>\n(function() {\n  const FULL_CAPACITY = 24000; \/\/ 6 x 4000mAh (18650 litokala)\n  const TARGET_YIELD = 2500;   \/\/ Wh\/m2 \"Golden full autonomy\"\n\n  async function updateRing() {\n    try {\n      const now = Date.now();\n      const today = new Intl.DateTimeFormat('en-CA', { timeZone: 'Atlantic\/Madeira' }).format(new Date());\n\n      \/\/ Coordinate with your PHP output location\n      const url = `https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/santacruz\/${today}.summary.json?t=${now}`;\n      \n      const res = await fetch(url);\n      const data = await res.json();\n      \n      \/\/ 1. PULL TRUTH FROM JSON\n      const currentYield = data.stats.solar_yield || 0;\n      const solarMax = data.stats.solar_max || 0;\n      \/\/ The Golden Variable we added to the PHP\n      const currentMah = data.stats.battery_mah || FULL_CAPACITY; \n      \n      const pct = Math.min(100, Math.round((currentYield \/ TARGET_YIELD) * 100));\n\n      \/\/ 2. DOM Updates\n      const valEl = document.getElementById('sc-ring-val');\n      const mahEl = document.getElementById('sc-ring-mah-val');\n      const txtEl = document.getElementById('sc-ring-txt');\n      const infoEl = document.getElementById('sc-ring-yield-info');\n      const root = document.documentElement;\n\n      \/\/ Gentle transitions for the percentage\n      if (pct > 0) {\n        valEl.style.opacity = \"1\";\n        valEl.innerText = pct + '%';\n      } else {\n        valEl.style.opacity = \"0\"; \/\/ Hide 0% at night\n      }\n\n      \/\/ Display the true mAh from the server\n      mahEl.innerText = Math.round(currentMah).toLocaleString() + ' mAh';\n      infoEl.innerHTML = `Solar Ring of Power <br> Autonomy Yield: ${currentYield} \/ ${TARGET_YIELD} Wh\/m\u00b2`;\n\n      \/\/ 3. Color & Pulse Evolution Grey -> Blue -> Green\n      let inner, outer, glow, speed, status;\n      \n      if (pct < 1) {\n        inner = \"#bdc3c7\"; outer = \"#95a5a6\"; glow = \"rgba(189, 195, 199, 0.2)\";\n        speed = \"9s\"; status = \"Night Time\";\n      } else if (pct < 33) {\n        inner = \"#5dade2\"; outer = \"#2e86c1\"; glow = \"rgba(93, 173, 226, 0.2)\";\n        speed = \"6s\"; status = \"Awakening\";\n      } else if (pct < 100) {\n        inner = \"#58d68d\"; outer = \"#28b463\"; glow = \"rgba(88, 214, 141, 0.2)\";\n        speed = \"4s\"; status = \"Charge\";\n      } else {\n        inner = \"#2ecc71\"; outer = \"#27ae60\"; glow = \"rgba(46, 204, 113, 0.4)\";\n        speed = \"2s\"; status = \"Fully Sustainable\";\n        valEl.style.color = \"#27ae60\";\n      }\n\n      \/\/ Higher Solar intensity makes the ring breathe more alive\n      if (solarMax > 500) speed = \"3s\";\n\n      \/\/ Apply to CSS Variables\n      root.style.setProperty('--ring-color-inner', inner);\n      root.style.setProperty('--ring-color-outer', outer);\n      root.style.setProperty('--ring-glow', glow);\n      root.style.setProperty('--ring-pulse-speed', speed);\n      \n      txtEl.innerText = status;\n      txtEl.style.color = inner;\n\n    } catch (e) { \n      console.log(\"Ring searching for signal...\");\n      document.getElementById('sc-ring-txt').innerText = \"Searching..\";\n    }\n  }\n\n  updateRing();\n  setInterval(updateRing, 300000); \/\/ 5 min refresh\n})();\n<\/script>\n\n\n\n<p class=\"has-text-align-center\"><sub><sup><em>since 3\/12\/2025<\/em><\/sup><\/sub><\/p>\n\n\n\n<figure class=\"wp-block-table aligncenter is-style-regular\"><table class=\"has-fixed-layout\" style=\"border-style:none;border-width:0px\"><tbody><tr><td class=\"has-text-align-center\" data-align=\"center\"><a href=\"https:\/\/www.wunderground.com\/dashboard\/pws\/ISANTA2134\" target=\"_blank\" rel=\"noreferrer noopener\"><\/a><a href=\"https:\/\/www.wunderground.com\/dashboard\/pws\/ISANTA2134\" target=\"_blank\" rel=\"noreferrer noopener\"><\/a><a href=\"https:\/\/www.wunderground.com\/dashboard\/pws\/ISANTA2134\" target=\"_blank\" rel=\"noreferrer noopener\"><\/a><a href=\"https:\/\/www.wunderground.com\/dashboard\/pws\/ISANTA2134\" target=\"_blank\" rel=\"noopener\" title=\"\"><img decoding=\"async\" src=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/10\/wunderground-logo.png\" alt=\"wunderground logo\" style=\"width: 55px;\"><\/a><\/td><td class=\"has-text-align-center\" data-align=\"center\"><a href=\"https:\/\/www.windguru.cz\/station\/6014\" target=\"_blank\" rel=\"noopener\" title=\"\"><img loading=\"lazy\" decoding=\"async\" width=\"55\" height=\"46\" class=\"wp-image-893\" style=\"width: 55px;\" src=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/10\/Windguru-logo.png\" alt=\"Windguru logo\" srcset=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/10\/Windguru-logo.png 400w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/10\/Windguru-logo-300x250.png 300w\" sizes=\"auto, (max-width: 55px) 100vw, 55px\" \/><\/a><\/td><td class=\"has-text-align-center\" data-align=\"center\"><a href=\"https:\/\/www.windy.com\/station\/pws-2f0dbfb95?32.510,-17.047\" target=\"_blank\" rel=\"noopener\" title=\"\"><img loading=\"lazy\" decoding=\"async\" width=\"55\" height=\"55\" class=\"wp-image-895\" style=\"width: 55px;\" src=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/10\/Windy-logo.png\" alt=\"Windy logo\" srcset=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/10\/Windy-logo.png 333w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/10\/Windy-logo-300x300.png 300w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/10\/Windy-logo-150x150.png 150w\" sizes=\"auto, (max-width: 55px) 100vw, 55px\" \/><\/a><\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n<\/div>\n\n\n\n<div style=\"height:7px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div align=\"center\" style=\"margin-top:-15px;\">\n    <div id=\"wg-container-wrapper\" style=\"max-width: 200px; min-height: 150px;\">\n        <script id=\"wglive_15575_1767102502139\">\n        (function (window, document) {\n            var loader = function () {\n                var arg = [\"spot=15575\",\"uid=wglive_15575_1767102502139\",\"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\n            \/\/ Use Intersection Observer for reliable lazy loading\n            if ('IntersectionObserver' in window) {\n                var observer = new IntersectionObserver(function (entries, observer) {\n                    entries.forEach(function (entry) {\n                        if (entry.isIntersecting) {\n                            loader();\n                            observer.unobserve(entry.target); \/\/ Stop watching after load\n                        }\n                    });\n                }, { rootMargin: \"100px\" }); \/\/ Starts loading 100px before it enters view\n\n                observer.observe(document.getElementById('wg-container-wrapper'));\n            } else {\n                \/\/ Fallback for very old browsers\n                window.addEventListener ? window.addEventListener(\"load\", loader, false) : window.attachEvent(\"onload\", loader);\n            }\n        })(window, document);\n        <\/script>\n    <\/div>\n<\/div>\n\n\n\n<div style=\"height:21px\" 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 id=\"madeira-clock-v2\" style=\"width: 290px; background: linear-gradient(to bottom, #001d3d, #003566); padding: 25px; border-radius: 20px; color: white; font-family: 'Segoe UI', Tahoma, sans-serif; text-align: center; box-shadow: 0 10px 30px rgba(0,0,0,0.5); transition: all 3s ease-in-out;\">\n    <h2 id=\"widgetTitle\" style=\"margin: 0 0 5px 0; color: black; font-size: 1.4em; font-weight: 300; transition: color 3s;\">Madeira Solar Tracker<\/h2>\n    <div id=\"date-display\" style=\"font-size: 0.8em; opacity: 0.7; margin-bottom: 15px;\">Loading date&#8230;<\/div>\n\n    <svg viewBox=\"0 0 500 300\" style=\"width: 100%; height: auto; overflow: visible;\">\n        <path id=\"pathTop\" fill=\"none\" stroke=\"rgba(255, 215, 0, 0.25)\" stroke-width=\"2\" stroke-dasharray=\"4\" \/>\n        <path id=\"pathBottom\" fill=\"none\" stroke=\"rgba(255, 215, 0, 0.08)\" stroke-width=\"2\" stroke-dasharray=\"4\" \/>\n        \n        <line x1=\"50\" y1=\"185\" x2=\"450\" y2=\"185\" stroke=\"rgba(255,255,255,0.05)\" stroke-width=\"1\" stroke-linecap=\"round\" \/>\n\/\/ Horizon line visual details\n\n        <image \n            href=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2026\/01\/madeira-map-3D.webp\" \n            x=\"100\" y=\"110\" width=\"300\" height=\"150\" \n            preserveAspectRatio=\"xMidYMid meet\"\n            style=\"filter: drop-shadow(0px 5px 10px rgba(0,0,0,0.5));\"\n        \/>\n\n        <g id=\"sunGroup\" style=\"opacity: 0; transition: opacity 1s ease-in-out;\">\n            <circle id=\"sunGlow\" r=\"15\" fill=\"rgba(255, 215, 0, 0.4)\" style=\"transition: opacity 2s;\">\n                <animate id=\"glowAnim\" attributeName=\"r\" values=\"14;20;14\" dur=\"3s\" repeatCount=\"indefinite\" \/>\n            <\/circle>\n            <circle id=\"actualSun\" r=\"8\" fill=\"#ffcc00\" style=\"transition: opacity 2s, fill 2s;\" \/>\n        <\/g>\n    <\/svg>\n\n    <div style=\"display: flex; justify-content: space-between; margin-top: 15px; font-size: 0.85em;\">\n        <div>&#x1f305; <span id=\"riseTime\">&#8211;:&#8211;<\/span><\/div>\n        <div id=\"statusText\" style=\"color: #ffd700; font-weight: bold;\">Updating&#8230;<\/div>\n        <div>&#x1F304; <span id=\"setTime\">&#8211;:&#8211;<\/span><\/div>\n    <\/div>\n<\/div>\n\n<script>\n(async function initSolar() {\n    \/\/ Variable names and project structure preserved\n    const widget = document.getElementById('madeira-clock-v2');\n    const title = document.getElementById('widgetTitle');\n    const actualSun = document.getElementById('actualSun');\n    const glow = document.getElementById('sunGlow');\n    const sunGroup = document.getElementById('sunGroup');\n    \n    let sunData = null;\n\n    \/\/ Helper to turn \"08:05\" into a real Date object for your math\n    function parseTime(timeStr) {\n        const [hours, minutes] = timeStr.split(':');\n        const d = new Date();\n        d.setHours(parseInt(hours), parseInt(minutes), 0, 0);\n        return d;\n    }\n\n    async function getTimes() {\n        try {\n            \/\/ Using your local solunar data\n            const res = await fetch(`https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/moon-time\/solunar-data.json`);\n            const json = await res.json();\n            \n            if(json.today) {\n                sunData = {\n                    rise: parseTime(json.today.sunrise),\n                    set: parseTime(json.today.sunset)\n                };\n            }\n        } catch (e) { console.log(\"Local data check failed...\"); }\n    }\n\n    function update() {\n        if (!sunData) return;\n\n        const now = new Date();\n        const minsNow = now.getHours() * 60 + now.getMinutes() + (now.getSeconds() \/ 60);\n        const minsRise = sunData.rise.getHours() * 60 + sunData.rise.getMinutes();\n        const minsSet = sunData.set.getHours() * 60 + sunData.set.getMinutes();\n\n        document.getElementById('riseTime').innerText = sunData.rise.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit', hour12: false});\n        document.getElementById('setTime').innerText = sunData.set.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit', hour12: false});\n        document.getElementById('date-display').innerText = now.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' });\n\n        const start = new Date(now.getFullYear(), 0, 0);\n        const dayOfYear = Math.floor((now - start) \/ 86400000);\n        const seasonalFactor = Math.cos((dayOfYear - 172) * 2 * Math.PI \/ 365);\n        const currentRy = 95 + (seasonalFactor * 25); \n\n        let angle;\n        const dayLen = minsSet - minsRise;\n        const nightLen = 1440 - dayLen;\n\n        if (minsNow >= minsRise && minsNow <= minsSet) {\n            const t = (minsNow - minsRise) \/ dayLen;\n            angle = Math.PI - (t * Math.PI);\n            document.getElementById('statusText').innerText = \"DAYLIGHT\";\n            \n            glow.style.opacity = \"1\";\n            actualSun.style.opacity = \"1\";\n            actualSun.style.fill = \"#ffcc00\";\n            title.style.color = \"#001d3d\"; \n\n            \/\/ --- PRECISION GOLDEN HOUR LOGIC (in minutes) ---\n            const goldenHourMins = 60; \n            const minsSinceRise = minsNow - minsRise;\n            const minsUntilSet = minsSet - minsNow;\n\n            if (minsSinceRise < goldenHourMins || minsUntilSet < goldenHourMins) {\n                widget.style.background = \"linear-gradient(to bottom, #ffb347, #003566)\";\n            } else {\n                widget.style.background = \"linear-gradient(to bottom, #3a7bd5, #003566)\";\n            }\n        } else {\n            let nightT;\n            if (minsNow > minsSet) { nightT = (minsNow - minsSet) \/ nightLen; }\n            else { nightT = (minsNow + (1440 - minsSet)) \/ nightLen; }\n            \n            angle = -(nightT * Math.PI);\n            document.getElementById('statusText').innerText = \"NIGHT\";\n\n\/\/ --- DAWN & DUSK TWILIGHT ART LAYERS ---\n     const twilightMins = 30; \/\/ Soft glow duration for both after sunset and before sunrise\n\n            const minsSinceSet = (minsNow >= minsSet) ? (minsNow - minsSet) : (minsNow + (1440 - minsSet));\n            const minsUntilRise = (minsNow < minsRise) ? (minsRise - minsNow) : (minsRise + (1440 - minsNow));\n\n                        if (minsSinceSet < twilightMins) {\n                                \/\/ DUSK: soft orange to midnight blue\n               widget.style.background = \"linear-gradient(to bottom, #8c7332, #001d3d)\";\n                document.getElementById('statusText').innerText = \"DUSK\";\n            } else if (minsUntilRise < twilightMins) {\n                                \/\/ DAWN: Gold hint of morning\n                widget.style.background = \"linear-gradient(to bottom, #857428, #000814)\";\n                document.getElementById('statusText').innerText = \"DAWN\";\n            } else {\n          \/\/ FULL deep NIGHT\n                widget.style.background = \"linear-gradient(to bottom, #000814, #001d3d)\";\n                document.getElementById('statusText').innerText = \"NIGHT\";\n            }\n            \n            title.style.color = \"#888888\"; \n            glow.style.opacity = \"0\";\n            actualSun.style.opacity = \"0.5\"; \n            actualSun.style.fill = \"#91a3b0\"; \n        }\n\n        const cx = 250, cy = 185, rx = 160;\n        const x = cx + Math.cos(angle) * rx;\n        const y = cy - Math.sin(angle) * currentRy;\n\n\/\/ Apply position to the sun\n        sunGroup.setAttribute('transform', `translate(${x},${y})`);\n        \n\/\/ Show the sun group once it's in the right place\n        if (sunGroup.style.opacity === \"0\") {\n            sunGroup.style.opacity = \"1\";\n        }\n\n        \/\/ SEASONAL PATH LOGIC \n  \/\/ Update the two-part path to match the current seasonal Ry\n        document.getElementById('pathTop').setAttribute('d', `M ${cx-rx},${cy} A ${rx},${currentRy} 0 0,1 ${cx+rx},${cy}`);\n        document.getElementById('pathBottom').setAttribute('d', `M ${cx+rx},${cy} A ${rx},${currentRy} 0 0,1 ${cx-rx},${cy}`);\n    }\n\n    await getTimes();\n    update(); \n    setInterval(update, 1000); \n    setInterval(getTimes, 3600000); \n})();\n<\/script>\n\n\n\n<p class=\"has-text-align-center\"><sup><em><sub>Aerial view from the North. 3D map source: <a href=\"https:\/\/sketchfab.com\/3d-models\/madeira-island-terrain-map-1175000-scale-308e37a8ae0a4d2ca397f2934b9d3e69\" target=\"_blank\" rel=\"noopener\" title=\"\">Ian Grasshoff<\/a><\/sub><\/em><\/sup><\/p>\n<\/div>\n<\/div>\n\n\n\n<!-- emoji moon version, changed to sprite film strip scrolling moon image below\n<div id=\"madeira-moon-parallax\" style=\"position: relative; width: 290px; background: linear-gradient(to bottom, #000814, #051630); padding: 25px; border-radius: 20px; color: grey; font-family: 'Segoe UI', Tahoma, sans-serif; text-align: center; box-shadow: 0 10px 30px rgba(0,0,0,0.8); transition: background 3.3s ease-in-out; overflow: hidden;\">\n\n    <style>\n        @keyframes moonTitleFade {\n            from { opacity: 0; color: black; }\n            to { opacity: 1; color: #91a3b0; }\n        }\n    <\/style>\n\n    <h2 style=\"margin: 0 0 5px 0; color: #91a3b0; font-size: 1.4em; font-weight: 300; animation: moonTitleFade 1.2s ease-in-out forwards;\">\n        Madeira Moon Tracker\n    <\/h2>\n\n    <div id=\"moonStatusText\" style=\"font-size: 0.70em; opacity: 0.6; margin-bottom: 15px; color: grey; font-weight: bold; letter-spacing: 1px; animation: moonTitleFade 1.4s ease-in-out forwards;\">\n        TOPOCENTRIC SYNC\n    <\/div>\n\n    <div id=\"moon-stage\" style=\"position: relative; width: 100%; aspect-ratio: 500 \/ 300; margin-top: 10px;\">\n        <div id=\"moonEmoji\" style=\"position: absolute; font-size: 17px; pointer-events: none; z-index: 10; transform: translate(-50%, -50%); left: 0; top: 0; opacity: 0; transition: opacity 2.8s ease-in-out;\"><\/div>\n\n        <svg viewBox=\"0 0 500 300\" overflow=\"visible\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width: 100%; height: 100%; display: block;\">\n            <line x1=\"50\" y1=\"185\" x2=\"450\" y2=\"185\" stroke=\"rgba(145, 163, 176, 0.10)\" stroke-width=\"1\" \/>\n            \n            <g id=\"stars\" fill=\"white\" opacity=\"0.45\">\n                <circle cx=\"100\" cy=\"60\" r=\"1.1\"><animate attributeName=\"opacity\" values=\"0.3;0.8;0.2\" dur=\"3s\" repeatCount=\"indefinite\" \/><\/circle>\n                <circle cx=\"400\" cy=\"80\" r=\"1\"><animate attributeName=\"opacity\" values=\"1;0.2;0.7\" dur=\"4s\" repeatCount=\"indefinite\" \/><\/circle>\n                <circle cx=\"250\" cy=\"40\" r=\"1.4\"><animate attributeName=\"opacity\" values=\"0.2;0.9;0.1\" dur=\"5s\" repeatCount=\"indefinite\" \/><\/circle>\n            <\/g>\n\n            <path d=\"M 60,185 A 190,95 0 1,1 440,185\" fill=\"none\" stroke=\"rgba(200, 200, 255, 0.2)\" stroke-width=\"1\" stroke-dasharray=\"2\" \/>\n            <path d=\"M 440,185 A 190,95 0 1,1 60,185\" fill=\"none\" stroke=\"rgba(200, 200, 255, 0.06)\" stroke-width=\"1\" stroke-dasharray=\"2\" \/>\n\n            <image href=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2026\/01\/madeira-map-3D.webp\" x=\"100\" y=\"110\" width=\"300\" height=\"150\" style=\"filter: brightness(0.5) contrast(1.1) hue-rotate(200deg);\" \/>\n        <\/svg>\n    <\/div>\n\n    <div style=\"display: flex; justify-content: space-between; margin-top: 15px; font-size: 0.85em; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 10px; animation: moonTitleFade 1.4s ease-in-out forwards;\">\n        <div>Illumination: <span id=\"moonIllum\" style=\"color: #ffd700;\">--<\/span><\/div>\n        <div>Altitude: <span id=\"moonAltDisplay\" style=\"color: #ffd700;\">--<\/span><\/div>\n    <\/div>\n\n    <div style=\"display: flex; justify-content: space-between; padding: 0 8px; margin-top: 10px; font-size: 0.9em; opacity: 0.9; color: white; animation: moonTitleFade 1.8s ease-in-out forwards;\">\n        <div><span style=\"color:#ffd700;\">\u2191<\/span> <span id=\"mRise\">--:--<\/span><\/div>\n        <div style=\"width: 1px; background: rgba(255,255,255,0.1); margin: 0 5px;\"><\/div>\n        <div><span style=\"color:#ffd700;\">\u2193<\/span> <span id=\"mSet\">--:--<\/span><\/div>\n    <\/div>\n\n    <div style=\"margin-top: 12px; font-size: 0.75em; color: #91a3b0; line-height: 1.8; animation: moonTitleFade 1.6s ease-in-out forwards;\">\n        Currently: <span id=\"currPhaseName\" style=\"color: white;\">--<\/span><br>\n        Next: <span id=\"nextPhaseInfo\" style=\"color: white;\">--<\/span>\n    <\/div>\n<\/div>\n\n\n<script>\n(function() {\n    \/\/ 1. CONFIGURATION: Location for Madeira area\n    const LAT = 32.6667, LON = -16.9167;\n \n \/\/ 2. FETCH DATA: Independent & Fast (Local JSON)\nasync function fetchMoonTimes() {\n    try {\n        \/\/ Path to the LIVE Json file on the server for Madeira Moon Rise and Set times\n        const response = await fetch('https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/moon-time\/solunar-data.json');\n        const data = await response.json();\n        const now = new Date();\n\n        \/\/ VALIDATION: Strict 24-hour freshness check\n        const lastUpdate = new Date(data.updated);\n        const isStale = (now - lastUpdate) > (24 * 60 * 60 * 1000);\n\n        \/\/ If data is stale, we STOP here. \n        \/\/ The HTML remains \"--:--\" (or empty), which is the honest truth.\n        if (isStale) {\n            console.warn(\"Data is stale. Displaying nothing to maintain integrity.\");\n            document.getElementById('moonStatusText').innerText = \"SYNC PENDING...\";\n            return; \/\/ Exit the function immediately\n        }\n\n        \/\/ --- DATA IS FRESH: Proceed with display ---\n        let mRise = data.today.rise || \"--:--\";\n        let mSet = data.today.set || \"--:--\";\n\n   \/\/ Global storage for the background comparison in update()\n        window.sunRise = data.today.sunrise; \n        window.sunSet = data.today.sunset;\n\n\n \/\/ THE HONEST CYCLE: Using a 6-hour \"Memory Buffer\" to prevent the rise and set times to change immediately after they happened \n    \/\/ We only show Tomorrow's time if the current time is more than 6 hours past Today's event.\n\n  \/\/ Handle SET time logic\n        if (data.today.set && data.today.set !== \"--:--\") {\n            const [h, m] = data.today.set.split(':');\n            const setTimeToday = new Date(now.getFullYear(), now.getMonth(), now.getDate(), h, m);\n           \n  \/\/ Calculate the \"Switch Point\" (6 hours after the event)\n            const switchPointSet = new Date(setTimeToday.getTime() + (6 * 60 * 60 * 1000));\n            \n   \/\/ Only show tomorrow's set if we are past the 6-hour memory window\n            if (now > switchPointSet) {\n                mSet = data.tomorrow.set || \"--:--\";\n            }\n        }\n\n \/\/ Handle RISE time logic\n        if (data.today.rise && data.today.rise !== \"--:--\") {\n            const [rh, rm] = data.today.rise.split(':');\n            const riseTimeToday = new Date(now.getFullYear(), now.getMonth(), now.getDate(), rh, rm);\n            \n    \/\/ Calculate the \"Switch Point\" (6 hours after the event)\n            const switchPointRise = new Date(riseTimeToday.getTime() + (6 * 60 * 60 * 1000));\n\n   \/\/ Only show tomorrow's rise if we are past the 6-hour memory window\n            if (now > switchPointRise) {\n                mRise = data.tomorrow.rise || \"--:--\";\n            }\n        }\n\n        document.getElementById('mRise').innerText = mRise;\n        document.getElementById('mSet').innerText = mSet;\n\n         \/\/ call to update to apply background logic\n        update();\n\n    } catch (e) { \n        console.log(\"Local sync failed:\", e); \n    }\n}\n    \/\/ 3. ASTRONOMICAL ENGINE: Calculates Phase, Altitude, and Azimuth\n    function computeMoon(date) {\n        const jd = (Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds()) \/ 86400000) + 2440587.5;\n        const T = (jd - 2451545.0) \/ 36525;\n        const L0 = 218.316 + 481267.881 * T;\n        const M = 134.963 + 477198.867 * T;\n        const F = 93.272 + 483202.018 * T;\n        const lon = (L0 + 6.289 * Math.sin(M * Math.PI \/ 180)) % 360;\n        const lat = 5.128 * Math.sin(F * Math.PI \/ 180);\n        const Lsun = (280.460 + 36000.771 * T) % 360;\n        let elong = (lon - Lsun + 360) % 360;\n        const phaseValue = elong \/ 360;\n        const e = 23.439 - 0.013 * T;\n        const ra = Math.atan2(Math.sin(lon * Math.PI \/ 180) * Math.cos(e * Math.PI \/ 180), Math.cos(lon * Math.PI \/ 180)) * 180 \/ Math.PI;\n        const dec = Math.asin(Math.sin(lat * Math.PI \/ 180) * Math.cos(e * Math.PI \/ 180) + Math.cos(lat * Math.PI \/ 180) * Math.sin(e * Math.PI \/ 180) * Math.sin(lon * Math.PI \/ 180)) * 180 \/ Math.PI;\n        let lst = (100.46 + 0.985647 * (jd - 2451545.0) + LON + (date.getUTCHours() + date.getUTCMinutes() \/ 60) * 15) % 360;\n        const ha = (lst - ra + 360) % 360;\n        let alt = Math.asin(Math.sin(LAT * Math.PI \/ 180) * Math.sin(dec * Math.PI \/ 180) + Math.cos(LAT * Math.PI \/ 180) * Math.cos(dec * Math.PI \/ 180) * Math.cos(ha * Math.PI \/ 180)) * 180 \/ Math.PI;\n\n\/\/ To adjust for real time Madeira visual moon rise add this code... return { alt: alt + 0.57 - (0.95 * Math.cos(alt * Math.PI \/ 180)),...\n\n \n        return { alt: alt - (0.95 * Math.cos(alt * Math.PI \/ 180)), az: ha, phaseValue, lst, Lsun };\n    }\n\n    \/\/ 4. MAIN UPDATE: Handles visuals, positions, and UI logic\n    function update() {\n        const now = new Date();\n        const moon = computeMoon(now);\n        const widget = document.getElementById('madeira-moon-parallax');\n        const illum = Math.round((1 - Math.cos(2 * Math.PI * moon.phaseValue)) \/ 2 * 100);\n        \n        const names = [\"New Moon\", \"Waxing Crescent\", \"First Quarter\", \"Waxing Gibbous\", \"Full Moon\", \"Waning Gibbous\", \"Last Quarter\", \"Waning Crescent\"];\n        const emojis = [\"&#x1f311;\", \"&#x1f312;\", \"&#x1f313;\", \"&#x1f314;\", \"&#x1f315;\", \"&#x1f316;\", \"&#x1f317;\", \"&#x1f318;\"];\n  \n\n\/\/ Intuitive Phase Indexing - Based on Illumination %\n\/\/ --- INDEPENDENT PHASE INDEXING ---\nconst p = moon.phaseValue;\nconst isWaxing = p < 0.5;\n\n\/\/ 1. TECHNICAL NAME INDEX (Strict Thresholds)\nlet nameIdx;\nif (illum <= 1) nameIdx = 0; \/\/ Strict New Moon\n\nelse if (isWaxing) {\n    if (illum <= 48) nameIdx = 1;      \/\/ Waxing Crescent\n\n    else if (illum <= 52) nameIdx = 2; \/\/ First Quarter (Precise 50%)\n\n    else nameIdx = 3;                  \/\/ Waxing Gibbous\n\n} else {\n    if (illum > 52) nameIdx = 5;       \/\/ Waning Gibbous\n\n    else if (illum > 48) nameIdx = 6;  \/\/ Last Quarter (Precise 50%)\n\n    else if (illum > 1) nameIdx = 7;   \/\/ Waning Crescent\n\n    else nameIdx = 0;\n}\nif (illum >= 99) nameIdx = 4;          \/\/ Strict Full Moon label\n\n\/\/ 2. VISUAL EMOJI INDEX \"Human Eye\" Thresholds\nlet emojiIdx;\n\n\/\/ PHASE 1: The New Moon\n\/\/ Anything 2% or less looks completely dark\nif (illum <= 2) {\n    emojiIdx = 0; \n} \n\n\/\/ PHASE 2: The Waxing Cycle (Growing)\nelse if (isWaxing) {\n if (illum <= 42) emojiIdx = 1; \/\/ Waxing Crescent: 2% to 42%\n \nelse if (illum <= 73) emojiIdx = 2; \/\/ Quarter\/Half Moon: 42% to 73%\nelse emojiIdx = 3;                  \/\/ Waxing Gibbous: 73% to 88%\n} \n\n\/\/ PHASE 3: The Waning Cycle (Shrinking)\nelse {\nif (illum > 61) emojiIdx = 5;       \/\/ Waning Gibbous: 88% to 61%\n\nelse if (illum > 22) emojiIdx = 6;  \/\/ Last Quarter: 61% to 22%\n\nelse if (illum > 2) emojiIdx = 7; \/\/ Waning Crescent: 22% to 2%\n    else emojiIdx = 0;                  \/\/ Safety reset to New Moon\n}\n\n\/\/ PHASE 4: The \"Full Moon\" Master Rule makes the moon look full to the eye (88% - 100%)\nif (illum >= 88) {\n    emojiIdx = 4; \n}\n\n\/\/ 3. APPLY INDEPENDENTLY\nconst emojiEl = document.getElementById('moonEmoji');\nemojiEl.innerHTML = emojis[emojiIdx]; \/\/ The visual feeling\n\ndocument.getElementById('currPhaseName').innerText = names[nameIdx]; \/\/ The technical truth\n\n\/\/ UI Text Updates\ndocument.getElementById('moonIllum').innerText = illum + \"%\";\n        document.getElementById('moonAltDisplay').innerText = moon.alt.toFixed(1) + \"\u00b0\";\n    \n\n\n        \/\/ Next Phase Prediction Logic\n        const nextQ = [0.25, 0.5, 0.75, 1].find(q => q > (moon.phaseValue + 0.01)) || 1;\n        const nextDate = new Date(now.getTime() + (nextQ - moon.phaseValue) * 29.53 * 86400000);\n        document.getElementById('nextPhaseInfo').innerText = names[Math.floor(nextQ * 8) % 8] + \" on \" + nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });\n\n        \/\/ DYNAMIC POSITIONING (Smart Dynamic Radius)\n        const cx = 250, cy = 185, rx = 190;\n        const dynamicRy = (moon.alt > 0) ? 135 : 95; \/\/ Moon rises higher above Madeira\n        const x = cx + rx * Math.cos((moon.az - 90) * Math.PI \/ 180);\n        const y = cy - (Math.sin(moon.alt * Math.PI \/ 180) * dynamicRy);\n\n        \/\/ Map SVG coordinates to HTML % for the Div overlay\n        \/\/ Calculate the actual size of the stage in pixels\nconst stage = document.getElementById('moon-stage');\nconst stageW = stage.clientWidth;\nconst stageH = stage.clientHeight;\n\n\/\/ Map the 500x300 astronomical coordinates directly to pixels\nconst realX = (x \/ 500) * stageW;\nconst realY = (y \/ 300) * stageH;\n\nemojiEl.style.left = realX + \"px\";\nemojiEl.style.top = realY + \"px\";\n\n      \n \/\/ RELIABLE DAYLIGHT\/NIGHT LOGIC: Simple string comparison for bg color & stars\n        if (window.sunRise && window.sunSet) {\n            const nowTimeStr = now.getHours().toString().padStart(2, '0') + \":\" + now.getMinutes().toString().padStart(2, '0');\n            \n            if (nowTimeStr >= window.sunRise && nowTimeStr < window.sunSet) {\n                widget.style.background = \"linear-gradient(to bottom, #1a2a44, #051630)\";  \/\/ Daylight\n                document.getElementById('stars').style.opacity = \"0\";\n            } else {\n                widget.style.background = \"linear-gradient(to bottom, #000814, #051630)\";  \/\/ Night\n                document.getElementById('stars').style.opacity = \"0.45\";\n            } \n        }\n        \n        \/\/ VISIBILITY LOGIC: Handle moon presence above\/below horizon\n        const statusEl = document.getElementById('moonStatusText');\n        if (moon.alt > 0) {\n            statusEl.innerText = \"OBSERVABLE ABOVE MADEIRA\";\n            statusEl.style.color = \"White\";\n            emojiEl.style.opacity = \"1\";\n            emojiEl.style.filter = \"brightness(0.69) contrast(2.4) drop-shadow(0 0 14px rgba(255,255,255,0.8))\"; \/\/ Active Glow\n        } else {\n            statusEl.innerText = \"BELOW HORIZON\";\n            statusEl.style.color = \"grey\";\n            emojiEl.style.opacity = \"0.3\"; \/\/ Faded below horizon\n            emojiEl.style.filter = \"brightness(0.5) contrast(1.2)\"; \/\/ Dimmed\n        }\n\n      \/\/ --- INTRO Moon FADE-IN REVEAL ---\n        if (emojiEl.style.opacity === \"0\") {\n            emojiEl.style.opacity = moon.alt > 0 ? \"1\" : \"0.3\";\n        }\n    }\n\n    \/\/ Initialize\n    fetchMoonTimes();\n    update();\n    setInterval(update, 60000); \/\/ One minute refresh for stability, or 5000ms = 5secs real time motion\n})();\n<\/script>\n-->\n\n\n\n<!-- Sacred moon experience -->\n<div id=\"madeira-moon-parallax\" style=\"position: relative; width: 290px; background: linear-gradient(to bottom, #000814, #051630); padding: 25px; border-radius: 20px; color: grey; font-family: 'Segoe UI', Tahoma, sans-serif; text-align: center; box-shadow: 0 10px 30px rgba(0,0,0,0.8); transition: background 3.3s ease-in-out; overflow: hidden;\">\n\n    <style>\n        @keyframes moonTitleFade {\n            from { opacity: 0; color: black; }\n            to { opacity: 1; color: #91a3b0; }\n        }\n    <\/style>\n\n    <h2 style=\"margin: 0 0 5px 0; color: #91a3b0; font-size: 1.4em; font-weight: 300; animation: moonTitleFade 1.2s ease-in-out forwards;\">\n        Madeira Moon Tracker\n    <\/h2>\n\n    <div id=\"moonStatusText\" style=\"font-size: 0.70em; opacity: 0.6; margin-bottom: 15px; color: grey; font-weight: bold; letter-spacing: 1px; animation: moonTitleFade 1.4s ease-in-out forwards;\">\n        TOPOCENTRIC SYNC\n    <\/div>\n\n    <div id=\"moon-stage\" style=\"position: relative; width: 100%; aspect-ratio: 500 \/ 300; margin-top: 10px;\">\n        <div id=\"moonEmoji\" style=\"\n    position: absolute; \n    width: 32px; \n    height: 32px; \n    pointer-events: none; \n    z-index: 10; \n    transform: translate(-50%, -50%); \n    left: 0; \n    top: 0; \n    opacity: 0; \n    transition: opacity 2.8s ease-in-out;\n    background-image: url('https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2026\/03\/28-Moon-phases-v4.png');\n    background-size: auto 100%; \/* Changed from 896px *\/\n    background-repeat: no-repeat;\n    \/* mix-blend-mode removed: not needed for transparent assets *\/\nimage-rendering: -webkit-optimize-contrast; \/* For Chrome\/Safari *\/\nimage-rendering: crisp-edges;               \/* For Firefox\/Standard *\/\n\"><\/div>\n\n        <svg viewBox=\"0 0 500 300\" overflow=\"visible\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width: 100%; height: 100%; display: block;\">\n            <line x1=\"50\" y1=\"185\" x2=\"450\" y2=\"185\" stroke=\"rgba(145, 163, 176, 0.10)\" stroke-width=\"1\" \/>\n            \n            <g id=\"stars\" fill=\"white\" opacity=\"0.45\">\n                <circle cx=\"100\" cy=\"60\" r=\"1.1\"><animate attributeName=\"opacity\" values=\"0.3;0.8;0.2\" dur=\"3s\" repeatCount=\"indefinite\" \/><\/circle>\n                <circle cx=\"400\" cy=\"80\" r=\"1\"><animate attributeName=\"opacity\" values=\"1;0.2;0.7\" dur=\"4s\" repeatCount=\"indefinite\" \/><\/circle>\n                <circle cx=\"250\" cy=\"40\" r=\"1.4\"><animate attributeName=\"opacity\" values=\"0.2;0.9;0.1\" dur=\"5s\" repeatCount=\"indefinite\" \/><\/circle>\n            <\/g>\n\n            <path d=\"M 60,185 A 190,95 0 1,1 440,185\" fill=\"none\" stroke=\"rgba(200, 200, 255, 0.2)\" stroke-width=\"1\" stroke-dasharray=\"2\" \/>\n            <path d=\"M 440,185 A 190,95 0 1,1 60,185\" fill=\"none\" stroke=\"rgba(200, 200, 255, 0.06)\" stroke-width=\"1\" stroke-dasharray=\"2\" \/>\n\n            <image href=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2026\/01\/madeira-map-3D.webp\" x=\"100\" y=\"110\" width=\"300\" height=\"150\" style=\"filter: brightness(0.5) contrast(1.1) hue-rotate(200deg);\" \/>\n        <\/svg>\n    <\/div>\n\n    <div style=\"display: flex; justify-content: space-between; margin-top: 15px; font-size: 0.85em; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 10px; animation: moonTitleFade 1.4s ease-in-out forwards;\">\n        <div>Illumination: <span id=\"moonIllum\" style=\"color: #ffd700;\">--<\/span><\/div>\n        <div>Altitude: <span id=\"moonAltDisplay\" style=\"color: #ffd700;\">--<\/span><\/div>\n    <\/div>\n\n    <div style=\"display: flex; justify-content: space-between; padding: 0 8px; margin-top: 10px; font-size: 0.9em; opacity: 0.9; color: white; animation: moonTitleFade 1.8s ease-in-out forwards;\">\n        <div><span style=\"color:#ffd700;\">\u2191<\/span> <span id=\"mRise\">--:--<\/span><\/div>\n        <div style=\"width: 1px; background: rgba(255,255,255,0.1); margin: 0 5px;\"><\/div>\n        <div><span style=\"color:#ffd700;\">\u2193<\/span> <span id=\"mSet\">--:--<\/span><\/div>\n    <\/div>\n\n    <div style=\"margin-top: 12px; font-size: 0.75em; color: #91a3b0; line-height: 1.8; animation: moonTitleFade 1.6s ease-in-out forwards;\">\n        Currently: <span id=\"currPhaseName\" style=\"color: white;\">--<\/span><br>\n        Next: <span id=\"nextPhaseInfo\" style=\"color: white;\">--<\/span>\n    <\/div>\n<\/div>\n\n\n<script>\n(function() {\n    \/\/ 1. CONFIGURATION: Location for Madeira area\n    const LAT = 32.6667, LON = -16.9167;\n\n\/\/ Create date to Madeira timezone\nfunction getMadeiraDate() {\n    return new Date(new Date().toLocaleString(\"en-US\", {timeZone: \"Atlantic\/Madeira\"}));\n}\n \n \/\/ 2. FETCH DATA: Independent & Fast (Local JSON)\nasync function fetchMoonTimes() {\n    try {\n        \/\/ Path to the LIVE Json file on the server for Madeira Moon Rise and Set times\n        const response = await fetch('https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/moon-time\/solunar-data.json');\n const data = await response.json();\n const now = getMadeiraDate();\n\n\/\/ VALIDATION: 24-hour fresh check\n        const lastUpdate = new Date(data.updated);\n        const isStale = (now - lastUpdate) > (24 * 60 * 60 * 1000);\n\n\/\/ If data is stale, STOP here\n\/\/ remains \"--:--\" (or empty), which is the honest truth.\n        if (isStale) {\n            console.warn(\"Data is stale. Displaying nothing to maintain integrity.\");\n            document.getElementById('moonStatusText').innerText = \"SYNC PENDING...\";\n            return; \/\/ Exit the function immediately\n        }\n\n\/\/ --- DATA IS FRESH: Proceed with display ---\n        let mRise = data.today.rise || \"--:--\";\n        let mSet = data.today.set || \"--:--\";\n\n\/\/ Global storage for the background comparison in update()\n        window.sunRise = data.today.sunrise; \n        window.sunSet = data.today.sunset;\n\n\n \/\/ THE HONEST CYCLE: Using a 6-hour \"Memory Buffer\" to prevent the rise and set times to change immediately after they happened \n    \/\/ We only show Tomorrow's time if the current time is more than 6 hours past Today's event.\n\n  \/\/ Handle SET time logic\n        if (data.today.set && data.today.set !== \"--:--\") {\n            const [h, m] = data.today.set.split(':');\n\/\/ Clone the update time from the JSON and just update the hours\/minutes\nconst setTimeToday = new Date(data.updated);\nsetTimeToday.setHours(h, m, 0, 0);\n           \n\/\/ Calculate the \"Switch Point\" (6 hours after the event)\nconst switchPointSet = new Date(setTimeToday.getTime() + (6 * 60 * 60 * 1000));\n            \n\/\/ Only show tomorrow's set if we're past 6-hour memory window\n            if (now > switchPointSet) {\n                mSet = data.tomorrow.set || \"--:--\";\n            }\n        }\n\n \/\/ Handle RISE time logic\n        if (data.today.rise && data.today.rise !== \"--:--\") {\n            const [rh, rm] = data.today.rise.split(':');\n\/\/ Clone the update time from the JSON and just update the hours\/minutes\nconst riseTimeToday = new Date(data.updated);\nriseTimeToday.setHours(rh, rm, 0, 0);\n            \n\/\/ Calculate the \"Switch Point\" (6 hours after the event)\nconst switchPointRise = new Date(riseTimeToday.getTime() + (6 * 60 * 60 * 1000));\n\n\/\/ Only show tomorrow's rise if we are past the 6-hour memory window\n            if (now > switchPointRise) {\n                mRise = data.tomorrow.rise || \"--:--\";\n            }\n        }\n      document.getElementById('mRise').innerText = mRise;\n        document.getElementById('mSet').innerText = mSet;\n\n\/\/ call to update to apply background logic\n        update();\n\n    } catch (e) { \n        console.log(\"Local sync failed:\", e); \n    }\n}\n    \/\/ 3. ASTRONOMICAL ENGINE: Calculates Phase, Altitude, and Azimuth\n    function computeMoon(date) {\n        const jd = (Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds()) \/ 86400000) + 2440587.5;\n        const T = (jd - 2451545.0) \/ 36525;\n        const L0 = 218.316 + 481267.881 * T;\n        const M = 134.963 + 477198.867 * T;\n        const F = 93.272 + 483202.018 * T;\n        const lon = (L0 + 6.289 * Math.sin(M * Math.PI \/ 180)) % 360;\n        const lat = 5.128 * Math.sin(F * Math.PI \/ 180);\n        const Lsun = (280.460 + 36000.771 * T) % 360;\n        let elong = (lon - Lsun + 360) % 360;\n        const phaseValue = elong \/ 360;\n        const e = 23.439 - 0.013 * T;\n        const ra = Math.atan2(Math.sin(lon * Math.PI \/ 180) * Math.cos(e * Math.PI \/ 180), Math.cos(lon * Math.PI \/ 180)) * 180 \/ Math.PI;\n        const dec = Math.asin(Math.sin(lat * Math.PI \/ 180) * Math.cos(e * Math.PI \/ 180) + Math.cos(lat * Math.PI \/ 180) * Math.sin(e * Math.PI \/ 180) * Math.sin(lon * Math.PI \/ 180)) * 180 \/ Math.PI;\n        let lst = (100.46 + 0.985647 * (jd - 2451545.0) + LON + (date.getUTCHours() + date.getUTCMinutes() \/ 60) * 15) % 360;\n        const ha = (lst - ra + 360) % 360;\n        let alt = Math.asin(Math.sin(LAT * Math.PI \/ 180) * Math.sin(dec * Math.PI \/ 180) + Math.cos(LAT * Math.PI \/ 180) * Math.cos(dec * Math.PI \/ 180) * Math.cos(ha * Math.PI \/ 180)) * 180 \/ Math.PI;\n\n\/\/ To adjust for real time Madeira visual moon rise\n \n        return { alt: alt + 1.99 - (0.95 * Math.cos(alt * Math.PI \/ 180)), az: ha, phaseValue, lst, Lsun };\n    }\n\n\/\/ 4. MAIN UPDATE: Handles visuals, positions, and UI logic\n    function update() {\n const now = getMadeiraDate();\n const moon = computeMoon(now);\n        const widget = document.getElementById('madeira-moon-parallax');\n        const illum = ((1 - Math.cos(2 * Math.PI * moon.phaseValue)) \/ 2 * 100).toFixed(1);\n        \n        const names = [\"New Moon\", \"Waxing Crescent\", \"First Quarter\", \"Waxing Gibbous\", \"Full Moon\", \"Waning Gibbous\", \"Last Quarter\", \"Waning Crescent\"];\n        const emojis = [\"&#x1f311;\", \"&#x1f312;\", \"&#x1f313;\", \"&#x1f314;\", \"&#x1f315;\", \"&#x1f316;\", \"&#x1f317;\", \"&#x1f318;\"];\n  \n\n\/\/ Intuitive Phase Indexing - Based on Illumination %\n\/\/ --- INDEPENDENT PHASE INDEXING ---\nconst p = moon.phaseValue;\nconst isWaxing = p < 0.5;\n\n\/\/ 1. TECHNICAL NAME INDEX (Strict Thresholds)\nlet nameIdx;\nif (illum <= 0.9) nameIdx = 0; \/\/ Strict New Moon\n\nelse if (isWaxing) {\n    if (illum <= 49.5) nameIdx = 1;      \/\/ Waxing Crescent\n\n    else if (illum <= 50.5) nameIdx = 2; \/\/ First Quarter (Precise 50%)\n\n    else nameIdx = 3;                  \/\/ Waxing Gibbous\n\n} else {\n    if (illum > 50.5) nameIdx = 5;       \/\/ Waning Gibbous\n\n    else if (illum > 49.5) nameIdx = 6;  \/\/ Last Quarter (Precise 50%)\n\n    else if (illum > 0.2) nameIdx = 7;   \/\/ Waning Crescent\n\n    else nameIdx = 0;\n}\nif (illum >= 99.6) nameIdx = 4;          \/\/ Strict Full Moon label\n\n\/\/ 2. VISUAL EMOJI INDEX \"Human Eye\" Thresholds (Daily Frame Shift)\nconst emojiEl = document.getElementById('moonEmoji');\n\n\/\/ Calculate exactly which of your 28 images to show\nconst totalFrames = 28;\nconst frameIdx = Math.floor(moon.phaseValue * totalFrames) % totalFrames;\n\n\/\/ Drift-proof calculation: (Current Frame \/ (Total Frames - 1)) * 100\nconst percentX = (frameIdx \/ (totalFrames - 1)) * 100;\n\n\/\/ Apply the percent shift instead of pixels\nemojiEl.style.backgroundPosition = `${percentX}% 0%`;\n\ndocument.getElementById('currPhaseName').innerText = names[nameIdx]; \/\/ The technical truth\n\n\/\/ UI Text Updates\ndocument.getElementById('moonIllum').innerText = illum + \"%\";\n        document.getElementById('moonAltDisplay').innerText = moon.alt.toFixed(1) + \"\u00b0\";\n   \n\n\/\/ --- DYNAMIC PHASE PREDICTION ENGINE ---\n\/\/ Identifies the next major milestone: 0.25 (1st Qtr), 0.5 (Full), 0.75 (Last Qtr), or 1 (New Moon)\nconst milestones = [0.25, 0.5, 0.75, 1];\nlet nextQ = milestones.find(q => q > (moon.phaseValue + 0.00)) || 0.25;\n\n\/\/ VALIDATION: If the predicted milestone shares the same name as 'Currently', jump to the next quarter\nif (names[Math.floor(nextQ * 8) % 8] === names[nameIdx]) {\n    const nextIdx = (milestones.indexOf(nextQ) + 1) % 4;\n    nextQ = milestones[nextIdx];\n}\n\n\/\/ Calculate the timeframe and update the UI\nconst predictedValue = (nextQ <= moon.phaseValue) ? (nextQ + 1) : nextQ;\nconst nextDate = new Date(now.getTime() + (predictedValue - moon.phaseValue) * 29.53059 * 86400000);\n\ndocument.getElementById('nextPhaseInfo').innerText = \n    names[Math.floor(nextQ * 8) % 8] + \" on \" + \n    nextDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric', timeZone: 'Atlantic\/Madeira' });\n\n\/\/ DYNAMIC POSITIONING (Smart Dynamic Radius)\n        const cx = 250, cy = 185, rx = 190;\n        const dynamicRy = (moon.alt > 0) ? 135 : 95; \/\/ Moon rises higher above Madeira\n        const x = cx + rx * Math.cos((moon.az - 90) * Math.PI \/ 180);\n        const y = cy - (Math.sin(moon.alt * Math.PI \/ 180) * dynamicRy);\n\n\/\/ Map SVG coordinates to HTML % for the Div overlay\n\/\/ Calculate the actual size of the stage in pixels\nconst stage = document.getElementById('moon-stage');\nconst stageW = stage.clientWidth;\nconst stageH = stage.clientHeight;\n\n\/\/ Map the 500x300 astronomical coordinates directly to pixels\nconst realX = (x \/ 500) * stageW;\nconst realY = (y \/ 300) * stageH;\n\nemojiEl.style.left = realX + \"px\";\nemojiEl.style.top = realY + \"px\";\n\n\/\/ RELIABLE DAYLIGHT\/NIGHT LOGIC: Simple string comparison for bg color & stars\n        if (window.sunRise && window.sunSet) {\n            const nowTimeStr = now.getHours().toString().padStart(2, '0') + \":\" + now.getMinutes().toString().padStart(2, '0');\n            \n            if (nowTimeStr >= window.sunRise && nowTimeStr < window.sunSet) {\n                widget.style.background = \"linear-gradient(to bottom, #1a2a44, #051630)\"; \/\/ Daylight\n                document.getElementById('stars').style.opacity = \"0\";\n            } else {\n                widget.style.background = \"linear-gradient(to bottom, #000814, #051630)\";  \/\/ Night\n                document.getElementById('stars').style.opacity = \"0.45\";\n            } \n        }\n        \n\/\/ VISIBILITY LOGIC: Handle moon presence above\/below horizon\n        const statusEl = document.getElementById('moonStatusText');\n        if (moon.alt > 0) {\n            statusEl.innerText = \"OBSERVABLE ABOVE MADEIRA\";\n            statusEl.style.color = \"White\";\n            emojiEl.style.opacity = \"1\";\n            emojiEl.style.filter = \"brightness(0.69) contrast(2.7) drop-shadow(0 0 11px rgba(255,255,255,0.8)) drop-shadow(0 0 2px rgba(255,255,255,0.4))\"; \/\/ Active Glow\n        } else {\n            statusEl.innerText = \"BELOW HORIZON\";\n            statusEl.style.color = \"grey\";\n            emojiEl.style.opacity = \"0.3\"; \/\/ Faded below horizon\n            emojiEl.style.filter = \"brightness(0.5) contrast(1.2)\"; \/\/ Dimmed\n        }\n\n\/\/ -INTRO Moon FADE-IN REVEAL-\n        if (emojiEl.style.opacity === \"0\") {\n            emojiEl.style.opacity = moon.alt > 0 ? \"1\" : \"0.3\";\n        }\n    }\n\n    \/\/ Initialize\n    fetchMoonTimes();\n    update();\n    setInterval(update, 60000); \/\/ One minute refresh\n})();\n<\/script>\n","protected":false},"excerpt":{"rendered":"<p>WIND TEMP UV RAIN &#8212; hPa &#8212; km\/h &#8212; km\/h &#8212; km\/h &#8212; km\/h VRB &#8212; km\/h VRB &#8212; km\/h Airport &#8211; Ros\u00e1rio weather station Real Time &#8212; &#8211;\u00b0 &#8212; &#8212;km\/h MAX &nbsp;&#8212; km\/h Temp &#8212;\u00b0C Hum &#8212;% Rain Live &#8212;mm\/h Solar &#8212;W\/m\u00b2 Pressure &#8212;hPa 2h Wind Speed &#038; Gust evolution (km\/h) 2h Wind Direction [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"slim_seo":{"description":"Soon home to the world's first Vevor autonomous weather station prototype. Powered by the sun, connected via Yukatel","title":"Golden stream - Madeira Island weather"},"footnotes":""},"class_list":["post-6847","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages\/6847","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=6847"}],"version-history":[{"count":1119,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages\/6847\/revisions"}],"predecessor-version":[{"id":11231,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages\/6847\/revisions\/11231"}],"wp:attachment":[{"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/media?parent=6847"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}