{"id":1720,"date":"2024-11-08T22:30:08","date_gmt":"2024-11-08T22:30:08","guid":{"rendered":"https:\/\/paragliding-in-madeira.com\/weather\/?page_id=1720"},"modified":"2026-08-31T13:38:32","modified_gmt":"2026-08-31T12:38:32","slug":"porto-da-cruz","status":"publish","type":"page","link":"https:\/\/paragliding-in-madeira.com\/weather\/porto-da-cruz\/","title":{"rendered":"Porto da Cruz"},"content":{"rendered":"\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><a href=\"https:\/\/paragliding-in-madeira.com\/weather\/weather-stations\/\"><img loading=\"lazy\" decoding=\"async\" width=\"777\" height=\"500\" src=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/10\/weather-station.png\" alt=\"weather station since 20\/08\/24\" class=\"wp-image-378\" style=\"width:287px;height:auto\" srcset=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/10\/weather-station.png 777w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/10\/weather-station-300x193.png 300w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/10\/weather-station-768x494.png 768w\" sizes=\"auto, (max-width: 777px) 100vw, 777px\" \/><\/a><\/figure>\n\n\n\n<div class=\"wp-block-buttons is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-0aa01211 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<p class=\"has-text-align-center\"><a href=\"https:\/\/paragliding-in-madeira.com\/weather\/weather-stations\/\">All<\/a> | <a href=\"https:\/\/paragliding-in-madeira.com\/weather\/canhas\">Canhas<\/a> | <a href=\"#PortoDaCruz\">Porto da Cruz<\/a> | <a href=\"https:\/\/paragliding-in-madeira.com\/weather\/portela\/\">Portela<\/a><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\" https:\/\/www.windguru.cz\/map\/station?lat=32.75671841110203&amp;lon=-16.824026612163266&amp;zoom=11.231691084595456\" target=\"_blank\" rel=\" noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"475\" src=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/11\/Porto-da-Cruz-Maiata-map-1024x475.png\" alt=\"\" class=\"wp-image-1901\" srcset=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/11\/Porto-da-Cruz-Maiata-map-1024x475.png 1024w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/11\/Porto-da-Cruz-Maiata-map-300x139.png 300w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/11\/Porto-da-Cruz-Maiata-map-768x356.png 768w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/11\/Porto-da-Cruz-Maiata-map.png 1111w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\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;6a969605d91e2&quot;}\" data-wp-interactive=\"core\/image\" data-wp-key=\"6a969605d91e2\" class=\"wp-block-image aligncenter size-large 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--pointerdown=\"actions.preloadImage\" data-wp-on--pointerenter=\"actions.preloadImageWithDelay\" data-wp-on--pointerleave=\"actions.cancelPreload\" 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:93px;height:auto\" 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\tdata-wp-bind--aria-label=\"state.thisImage.triggerButtonAriaLabel\"\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.thisImage.buttonRight\"\n\t\t\tdata-wp-style--top=\"state.thisImage.buttonTop\"\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 wp-block-paragraph\" id=\"PortoDaCruz\"><strong><a style=\"text-decoration:none\" href=\"https:\/\/paragliding-in-madeira.com\/weather\/porto-da-cruz\/#PortoDaCruz\" target=\"_PARENT\">Porto da Cruz<\/a><\/strong><sup><em> <\/em><\/sup><sup><em>@39m <a style=\"text-decoration:none\" href=\"https:\/\/beachcam.meo.pt\/livecams\/madeira-maiata\/\" target=\"_blank\" rel=\"noopener\" title=\"\">asl<\/a> <br>(flyable ideally N\/NE @20-25kmh)<\/em><\/sup><\/p>\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\/maiata\/';\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<\/div>\n\n\n\n<div class=\"webcam-section\">\n<!-- WEBCAM-START -->\n\n<!-- WEBCAM-END -->\n<\/div>\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\/maiata\/';\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\ncanvas.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\/\/ --- CONFIG IDEAL FLYING Direction ---\nconst showIdealCorridor = true; \/\/ Set to false to hide\nconst idealMin = 333;          \/\/ NorthWest\nconst idealMax = 55;          \/\/ NorthEast\nconst corridorColor = 'rgba(40, 200, 40, 0.1)'; \/\/ Gentle green\n\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\nconst timeFormatter = (val) => {\nconst d = new Date(val);\n        return `${d.getHours()}:${String(d.getMinutes()).padStart(2, '0')}`;\n      };\n\nconst 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\ndrawSection(sunrise, sunset, 'rgba(255, 255, 255, 0.5)'); \/\/ clears the Night tint for today\n          ctx.restore();\n        }\n      };\n\n\/\/ Ideal direction Plugin Logic\nconst idealDirectionPlugin = {\n  id: 'idealDirection',\n  beforeDraw: (chart) => {\n \/\/ Only apply to the direction chart and if enabled\n    if (!showIdealCorridor || chart.canvas.id !== 'sc-dir-chart') return;\n    \n    const { ctx, chartArea: { left, right, top, bottom }, scales: { x, y } } = chart;\n    \n\/\/ Day\/Night Logic: Calc horiz start and end based on sunlight\n    const startPx = Math.max(left, x.getPixelForValue(sunrise));\n    const endPx = Math.min(right, x.getPixelForValue(sunset));\n\/\/ If the current 2h view has no daylight visible, don't draw\n    if (startPx >= endPx) return;\n\n    ctx.save();\n    ctx.fillStyle = corridorColor;\n\n    const drawBox = (min, max) => {\n      const yTop = y.getPixelForValue(max);\n      const yBottom = y.getPixelForValue(min);\n\/\/ startPx and endPx make green box for daylight hours only\n      ctx.fillRect(startPx, yTop, endPx - startPx, yBottom - yTop);\n    };\n\n    if (idealMin > idealMax) {\n      drawBox(idealMin, 360);\n      drawBox(0, idealMax);\n    } else {\n      drawBox(idealMin, idealMax);\n    }\n    \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 (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 },\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, idealDirectionPlugin] \/\/ 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<!-- added hide \/show wind gust below\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 &amp; 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\/maiata\/';\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\/\/ --- CONFIG IDEAL FLYING Direction ---\nconst showIdealCorridor = true; \/\/ Set to false to hide\nconst idealMin = 333;          \/\/ NorthWest\nconst idealMax = 55;          \/\/ NorthEast\nconst corridorColor = 'rgba(40, 200, 40, 0.1)'; \/\/ Gentle green\n\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\/\/ Ideal direction Plugin Logic\nconst idealDirectionPlugin = {\n  id: 'idealDirection',\n  beforeDraw: (chart) => {\n \/\/ Only apply to the direction chart and if enabled\n    if (!showIdealCorridor || chart.canvas.id !== 'sc-dir-chart') return;\n    \n    const { ctx, chartArea: { left, right, top, bottom }, scales: { x, y } } = chart;\n    \n\/\/ Day\/Night Logic: Calc horiz start and end based on sunlight\n    const startPx = Math.max(left, x.getPixelForValue(sunrise));\n    const endPx = Math.min(right, x.getPixelForValue(sunset));\n\/\/ If the current 2h view has no daylight visible, don't draw\n    if (startPx >= endPx) return;\n\n    ctx.save();\n    ctx.fillStyle = corridorColor;\n\n    const drawBox = (min, max) => {\n      const yTop = y.getPixelForValue(max);\n      const yBottom = y.getPixelForValue(min);\n\/\/ startPx and endPx make green box for daylight hours only\n      ctx.fillRect(startPx, yTop, endPx - startPx, yBottom - yTop);\n    };\n\n    if (idealMin > idealMax) {\n      drawBox(idealMin, 360);\n      drawBox(0, idealMax);\n    } else {\n      drawBox(idealMin, idealMax);\n    }\n    \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 (i > 0 && (d.time - arr[i-1].time) > 300000) 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) > 300000) 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        \/\/ Corrected array: ChartZoom removed\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        \/\/ Corrected array: ChartZoom removed\n        plugins: [solarBackgroundPlugin, idealDirectionPlugin]\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\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\">\n    <!-- Added ID, pointer cursor, user-select none (to prevent text highlighting), transition and Inline spans for independent opacity fading on title -->\n    <span id=\"sc-speed-title\" style=\"cursor: pointer; user-select: none; -webkit-user-select: none;\" title=\"Tap to isolate lines\">\n  2h <span id=\"sc-lbl-speed\" style=\"transition: opacity 0.3s ease;\">Wind Speed<\/span> \n  <span id=\"sc-lbl-amp\" style=\"transition: opacity 0.3s ease;\">&amp;<\/span> \n  <span id=\"sc-lbl-gust\" style=\"transition: opacity 0.3s ease;\">Gust evolution<\/span> (km\/h)\n<\/span>\n  <\/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\/\/ Wind lines display state: 0 = Both, 1 = Gust Only, 2 = Speed Only\n  let speedLineState = 0; \n\n  const baseDir = 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/maiata\/';\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\/\/ --- CONFIG IDEAL FLYING Direction ---\nconst showIdealCorridor = true; \/\/ Set to false to hide\nconst idealMin = 333;          \/\/ NorthWest\nconst idealMax = 55;          \/\/ NorthEast\nconst corridorColor = 'rgba(40, 200, 40, 0.1)'; \/\/ Gentle green\n\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\/\/ Ideal direction Plugin Logic\nconst idealDirectionPlugin = {\n  id: 'idealDirection',\n  beforeDraw: (chart) => {\n \/\/ Only apply to the direction chart and if enabled\n    if (!showIdealCorridor || chart.canvas.id !== 'sc-dir-chart') return;\n    \n    const { ctx, chartArea: { left, right, top, bottom }, scales: { x, y } } = chart;\n    \n\/\/ Day\/Night Logic: Calc horiz start and end based on sunlight\n    const startPx = Math.max(left, x.getPixelForValue(sunrise));\n    const endPx = Math.min(right, x.getPixelForValue(sunset));\n\/\/ If the current 2h view has no daylight visible, don't draw\n    if (startPx >= endPx) return;\n\n    ctx.save();\n    ctx.fillStyle = corridorColor;\n\n    const drawBox = (min, max) => {\n      const yTop = y.getPixelForValue(max);\n      const yBottom = y.getPixelForValue(min);\n\/\/ startPx and endPx make green box for daylight hours only\n      ctx.fillRect(startPx, yTop, endPx - startPx, yBottom - yTop);\n    };\n\n    if (idealMin > idealMax) {\n      drawBox(idealMin, 360);\n      drawBox(0, idealMax);\n    } else {\n      drawBox(idealMin, idealMax);\n    }\n    \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            { \n              label: 'Gust',\n              hidden: speedLineState === 2, \/\/ Check state map\n              data: windowData.reduce((acc, d, i, arr) => {\n                if (i > 0 && (d.time - arr[i-1].time) > 300000) 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,\n              spanGaps: false \/\/ Don't connect across null non existent values\n             },\n            { \n              label: 'Speed',\n              hidden: speedLineState === 1, \/\/ Check state map\n              data: windowData.reduce((acc, d, i, arr) => {\n                if (i > 0 && (d.time - arr[i-1].time) > 300000) 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\n              borderColor: 'rgba(24, 94, 150, 0.8)', backgroundColor: 'rgba(24, 94, 150, 0.1)', fill: true, pointRadius: 0, borderWidth: 2, tension: 0.3,\n              spanGaps: 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        \/\/ Corrected array: ChartZoom removed\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        \/\/ Corrected array: ChartZoom removed\n        plugins: [solarBackgroundPlugin, idealDirectionPlugin]\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  \/\/ Handle Title Click Line Cycle (Both -> Gust -> Speed -> Both)\ndocument.getElementById('sc-speed-title').addEventListener('click', () => {\n  speedLineState = (speedLineState + 1) % 3;\n  \n\/\/ Individually update text opacity based on current state\n  const lblSpeed = document.getElementById('sc-lbl-speed');\n  const lblGust  = document.getElementById('sc-lbl-gust');\n  const lblAmp   = document.getElementById('sc-lbl-amp');\n\n  \/\/ State 0: Both visible | State 1: Gust only | State 2: Speed only\n  lblSpeed.style.opacity = (speedLineState === 1) ? '0.3' : '1';\n  lblGust.style.opacity  = (speedLineState === 2) ? '0.3' : '1';\n  \n \/\/ Fade out the ampersand whenever either line is hidden solo\n  lblAmp.style.opacity   = (speedLineState === 0) ? '1' : '0.2';\n  \n  if (speedChart) {\n    speedChart.setDatasetVisibility(0, speedLineState !== 2); \/\/ show Gust\n    speedChart.setDatasetVisibility(1, speedLineState !== 1); \/\/ show wind\n\n\/\/ Adjust Gust line contrast when isolated\n    if (speedLineState === 1) {\n      speedChart.data.datasets[0].borderColor = 'rgba(255, 165, 0, 0.8)';\n      speedChart.data.datasets[0].backgroundColor = 'rgba(255, 165, 0, 0.15)'; \n    } else {\n\/\/ Revert to original blending opacity when Both or Speed only\n      speedChart.data.datasets[0].borderColor = 'rgba(255, 165, 0, 0.45)';\n      speedChart.data.datasets[0].backgroundColor = 'rgba(255, 165, 0, 0.05)';\n    }\n\n    speedChart.update(); \n  }\n});\n\n\n\n})();\n<\/script>\n\n\n\n<!-- 24h temp and hum graph changed from 12h 26\/05\/26 -->\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\/maiata\/';\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) + 30; \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 + 30; \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 -->\n<!-- added zoom below\n<style>\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<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js\"><\/script>\n<script>\n(function() {\n  let solarChart;\n  let solarTimeout;\n  const stationPath = 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/maiata\/';\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: true,\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          },\n          scales: {\n            x: {\n              type: 'linear',\n              min: rolling24,\n              max: now,\n              ticks: { stepSize: 3 * 3600000, 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\n  updateSolar();\n  setInterval(updateSolar, 300000);\n})();\n<\/script>\n-->\n\n\n\n<!-- 24h Solar evolution graph with UV and Total Yield Harvest - added zoom 30-04-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\/maiata\/';\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 -->\n<!-- added zoom below\n<style>\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; \/\/ increase from 180\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<\/style>\n\n<div class=\"sc-rain-cockpit\">\n  <div class=\"sc-rain-label\">\n    <span>24h Rain Evolution<\/span>\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<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js\"><\/script>\n\n<script>\n(function() {\n  let rainChart;\n  const stationPath = 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/maiata\/';\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\nfunction 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);\nconst todayStr = d.toLocaleDateString(\"en-CA\", {timeZone: \"Atlantic\/Madeira\"});\nd.setDate(d.getDate() - 1);\nconst 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\ndocument.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: true,\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          },\n          scales: {\n            x: { \n              type: 'linear', \n              min: twentyFourHoursAgo, \n              max: now, \n              ticks: { font: { size: 9 }, callback: (v) => formatTime24h(v), stepSize: 4 * 60 * 60 * 1000 }, \n              grid: { display: false } \n            },\n\n\/\/ Graph legends rate and total\n           yRate: { \n            type: 'linear',\n            position: 'left',\n            beginAtZero: true, \nsuggestedMax: 7, \/\/ For better visual consistency \n\n         display: true,\n         ticks: { \n         font: { size: 10, weight: 600 }, \ncolor: '#4caf50', \/\/ Matching line color\nautoSkip: false\n              }, \n              grid: { display: false } \n            },\n            y: { \n         type: 'linear',\n         position: 'right',\n         beginAtZero: true, \n         grace: '33%',\nsuggestedMax: 3, \/\/ Keeps low totals from filling the whole height\n\n            ticks: { \n           font: { size: 10, weight: 600 }, \ncolor: '#185e96', \/\/ Matching line color \ncallback: (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     autoHideTooltip(rainChart)\n    } catch (e) { console.error(\"Rain Sync Error:\", e); }\n  }\n\n  initRainWidget();\n  setInterval(initRainWidget, 60000);\n})();\n<\/script>\n-->\n\n\n\n<!-- 24h rain evolution graph with daily totals - added zoom 30-04-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\/maiata\/';\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\/maiata\/\";\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 at 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\/maiata\/';\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: 5 \/\/ 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: { \n\nfont: { size: 10, weight: '600' },\nprecision: 0, \nstepSize: 1,\n callback: (v) => v.toFixed(0),\n                padding: 5\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 5px 8px 5px;\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;\">Maiata 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 MAIATA 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-02-07\");\n\n  const 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\/maiata\/`;\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 = \"Maiata 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 (24h)\" : \"YEARLY TOTALS (24h)\";\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<!-- added zoom below\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      <\/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;\">--\/--<\/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\">MAIATA 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<style>\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>\n(function() {\n  let windChart;\n  let currentTimeframe = 'day';\n  let activeDate = new Date();\n\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\/maiata\/';\n  const stationStartDate = new Date(\"2026-02-07\"); \/\/ MAIATA - Change this date based on weather station deployment\n\n\/\/ --- CONFIG IDEAL FLYABLE Direction ---\n  const showIdealCorridor = true; \/\/ set false to hide \n  const idealMin = 333;          \/\/ NorthWest\n  const idealMax = 55;          \/\/ NorthEast\n  const corridorColor = 'rgba(40, 200, 40, 0.05)'; \n\n \/\/ Flyable directions Plugin\n  const idealDirectionPlugin = {\n    id: 'idealDirection',\n    beforeDraw: (chart) => {\n      if (!showIdealCorridor) return;\n      \n      const { ctx, chartArea: { left, right }, scales: { yDir } } = chart;\n\nif (!yDir) return; \/\/ Safety check    \n\n      ctx.save();\n      ctx.fillStyle = corridorColor;\n\n      const drawBox = (min, max) => {\n        const yTop = yDir.getPixelForValue(max);\n        const yBottom = yDir.getPixelForValue(min);\n        \/\/ Draws across the full width of the chart area\n        ctx.fillRect(left, yTop, right - left, yBottom - yTop);\n      };\n\n      if (idealMin > idealMax) {\n        drawBox(idealMin, 360);\n        drawBox(0, idealMax);\n      } else {\n        drawBox(idealMin, idealMax);\n      }\n      ctx.restore();\n    }\n  };\n\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');    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    } 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    \/\/ Label Update Logic (Now handles all timeframes correctly)\n    if (timeframe === 'year') {\n      stationLabel.innerText = \"Maiata wind 12h to 18h\";\n    } else if (is24hMode) {\n      stationLabel.innerText = \"Maiata wind 24h\";\n    } else {\n      stationLabel.innerText = \"Maiata wind 9h to 21h\";\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\nif (timeframe === 'day') {\n        const file = getISO(activeDate) + '.json';\n        const res = await fetch(stationPath + file + cacheBust);\n        const json = await res.json();\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 a null point to break the line\n              processedData.push({ t: prevTime + 1, 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\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\nif (timeframe === 'year') {\n  processedData = days\n  \/\/ Filters for the 12:00 entry (the 12h-18h block) for every day\n    .filter(d => d.t.includes(\"12:00:00\")) \n    .map(d => ({ \n\/\/ Convert string to timestamp for Chart.js\n      t: new Date(d.t.replace(\/-\/g, \"\/\")).getTime(), \n      s: d.wind, \n      g: d.gust, \n      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\n\/\/ This makes the graph go edge to edge 9h to 21h and 24h\nmin: currentTimeframe === 'day' ? new Date(getISO(activeDate) + 'T' + (is24hMode ? '00:00:00' : '09:00:00')).getTime() : undefined,\nmax: currentTimeframe === 'day' ? new Date(getISO(activeDate) + 'T' + (is24hMode ? '24:00:00' : '21:00:00')).getTime() : undefined,\n\n            ticks: {\n\/\/ Adjust stepSize: 2 hours for 9-21h, 3 hours for 24h to keep it clean\n\/\/ 2 hours * 60 min * 60 sec * 1000 ms\nstepSize: currentTimeframe === 'day' ? ((is24hMode ? 3 : 2) * 60 * 60 * 1000) : undefined,\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: 'numeric', year: '2-digit', timeZone: 'Atlantic\/Madeira'});\n                return d.toLocaleString('en-GB', {day: 'numeric', month: 'numeric', timeZone: 'Atlantic\/Madeira'});\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{\n          legend: { display: false },\n          tooltip: {\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 plugins: [idealDirectionPlugin] \/\/ Flyable direction Plugin inside the Chart object\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) {\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})();\n<\/script>\n-->\n\n\n\n<!-- Day plus Precious selection, 7 days, 30 days and year Wind 9h to 21h -->\n<!-- Added bg color on sunrise and set below\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      <\/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;\">--\/--<\/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\">MAIATA 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  \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;\n  let isReadingMode = true; \/\/ Added state for mode toggle\n  let currentTimeframe = 'day';\n  let activeDate = new Date();\n\n  \/\/ New 24h wind button\n  let is24hMode = false;\n\n  window.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\/maiata\/';\n  const stationStartDate = new Date(\"2026-02-07\"); \/\/ MAIATA - Change this date based on weather station deployment\n\n  \/\/ --- CONFIG IDEAL FLYABLE Direction ---\n  const showIdealCorridor = true; \/\/ set false to hide \n  const idealMin = 333;          \/\/ NorthWest\n  const idealMax = 55;          \/\/ NorthEast\n  const corridorColor = 'rgba(40, 200, 40, 0.05)'; \n\n  \/\/ Flyable directions Plugin\n  const idealDirectionPlugin = {\n    id: 'idealDirection',\n    beforeDraw: (chart) => {\n      if (!showIdealCorridor) return;\n      \n      const { ctx, chartArea: { left, right }, scales: { yDir } } = chart;\n\n      if (!yDir) return; \/\/ Safety check    \n\n      ctx.save();\n      ctx.fillStyle = corridorColor;\n\n      const drawBox = (min, max) => {\n        const yTop = yDir.getPixelForValue(max);\n        const yBottom = yDir.getPixelForValue(min);\n        \/\/ Draws across the full width of the chart area\n        ctx.fillRect(left, yTop, right - left, yBottom - yTop);\n      };\n\n      if (idealMin > idealMax) {\n        drawBox(idealMin, 360);\n        drawBox(0, idealMax);\n      } else {\n        drawBox(idealMin, idealMax);\n      }\n      ctx.restore();\n    }\n  };\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    } 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    \/\/ Label Update Logic (Now handles all timeframes correctly)\n    if (timeframe === 'year') {\n      stationLabel.innerText = \"Maiata wind 12h to 18h\";\n    } else if (is24hMode) {\n      stationLabel.innerText = \"Maiata wind 24h\";\n    } else {\n      stationLabel.innerText = \"Maiata wind 9h to 21h\";\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      else 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\n          \/\/ Filters for the 12:00 entry (the 12h-18h block) for every day\n            .filter(d => d.t.includes(\"12:00:00\")) \n            .map(d => ({ \n              \/\/ Convert string to timestamp for Chart.js\n              t: new Date(d.t.replace(\/-\/g, \"\/\")).getTime(), \n              s: d.wind, \n              g: d.gust, \n              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,\n            borderWidth: { 'day': 1.2, 'week': 0.7, 'month': 0.2, 'year': 1.1 }[currentTimeframe],\n            pointRadius: { '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\n            borderWidth: { 'day': 1.2, 'week': 0.6, 'month': 1.2, 'year': 1.0 }[currentTimeframe],\n            pointRadius: { '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\n            \/\/ This makes the graph go edge to edge 9h to 21h and 24h\n            min: currentTimeframe === 'day' ? new Date(getISO(activeDate) + 'T' + (is24hMode ? '00:00:00' : '09:00:00')).getTime() : undefined,\n            max: currentTimeframe === 'day' ? new Date(getISO(activeDate) + 'T' + (is24hMode ? '24:00:00' : '21:00:00')).getTime() : undefined,\n\n            ticks: {\n   \/\/ Set stepSize to a solid 1 hour; autoSkip will handle crowding in 24h mode\n         \/\/ 1 hour * 60 min * 60 sec * 1000 ms\n              stepSize: currentTimeframe === 'day' ? (60 * 60 * 1000) : undefined,\n              \n              \/\/ Added properties for resolution and scaling limits\n              autoSkip: true,\n              maxTicksLimit: 10,\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            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\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      plugins: [idealDirectionPlugin] \/\/ Flyable direction Plugin inside the Chart object\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\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\n<!-- Day plus Precious selection, 7 days, 30 days and year Wind 9h to 21h -->\n<!-- added gap awareness below -->\n<template>\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      <\/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\">MAIATA 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  \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<!-- Added SunCalc for historical day\/night bg color accuracy -->\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/suncalc\/1.9.0\/suncalc.min.js\"><\/script>\n\n<script>\n(function() {\n  let windChart;\n  let isReadingMode = true; \/\/ Added state for mode toggle\n  let currentTimeframe = 'day';\n  let activeDate = new Date();\n\n  \/\/ New 24h wind button\n  let is24hMode = false;\n\n  window.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\/maiata\/';\n  const stationStartDate = new Date(\"2026-02-07\"); \/\/ MAIATA - Change this date based on weather station deployment\n\n  \/\/ --- CONFIG IDEAL FLYABLE Direction ---\n  const showIdealCorridor = true; \/\/ set false to hide \n  const idealMin = 333;          \/\/ NorthWest\n  const idealMax = 55;          \/\/ NorthEast\n  const corridorColor = 'rgba(40, 200, 40, 0.05)'; \n\n\/\/ Flyable directions Plugin\n  const idealDirectionPlugin = {\n    id: 'idealDirection',\n    beforeDraw: (chart) => {\n      if (!showIdealCorridor) return;\n      \n      const { ctx, chartArea: { left, right }, scales: { x, yDir } } = chart;\n      if (!yDir || !x) return; \/\/ Safety check    \n\n      let startX = left;\n      let endX = right;\n\n      \/\/ Restrict corridor to daytime only in 'day' view\n      if (currentTimeframe === 'day') {\n        const lat = 32.77;\n        const lng = -16.83;\n        const times = SunCalc.getTimes(activeDate, lat, lng);\n\n        const alignToAxis = (dateObj) => {\n          const parts = new Intl.DateTimeFormat('en-US', {\n            timeZone: 'Atlantic\/Madeira',\n            hour: 'numeric', minute: 'numeric', second: 'numeric', hour12: false\n          }).formatToParts(dateObj);\n          \n          let h = 0, m = 0, s = 0;\n          parts.forEach(p => {\n            if (p.type === 'hour') h = parseInt(p.value);\n            if (p.type === 'minute') m = parseInt(p.value);\n            if (p.type === 'second') s = parseInt(p.value);\n          });\n          \n          const localDate = new Date(activeDate);\n          localDate.setHours(h === 24 ? 0 : h, m, s, 0);\n          return localDate.getTime();\n        };\n\n        const sunrise = alignToAxis(times.sunrise);\n        const sunset = alignToAxis(times.sunset);\n\n        \/\/ Convert times to pixel coordinates and clamp to the visible chart area\n        startX = Math.max(left, x.getPixelForValue(sunrise));\n        endX = Math.min(right, x.getPixelForValue(sunset));\n      }\n\n      ctx.save();\n      ctx.fillStyle = corridorColor;\n\n      const drawBox = (min, max) => {\n        const yTop = yDir.getPixelForValue(max);\n        const yBottom = yDir.getPixelForValue(min);\n        \n        \/\/ Only draw the box if the daytime area is currently visible on the screen\n        if (startX < endX) {\n          ctx.fillRect(startX, yTop, endX - startX, yBottom - yTop);\n        }\n      };\n\n      if (idealMin > idealMax) {\n        drawBox(idealMin, 360);\n        drawBox(0, idealMax);\n      } else {\n        drawBox(idealMin, idealMax);\n      }\n      ctx.restore();\n    }\n  };\n\n\/\/ NEW: Day\/Night Background Plugin for Historical Accuracy start\nconst solarBackgroundPlugin = {\n  id: 'solarBackground',\n  beforeDraw: (chart) => {\n    if (currentTimeframe !== 'day') return;\n\n    const { ctx, chartArea: { top, bottom, left, right }, scales: { x } } = chart;\n    if (!x) return;\n\n    \/\/ Coordinates for Madeira Island\n    const lat = 32.77;\n    const lng = -16.83;\n\n    \/\/ Get the absolute UTC solar times for the date\n    const times = SunCalc.getTimes(activeDate, lat, lng);\n\n    \/\/ Timezone alignment: Extract the exact wall-clock hour\/minute in Madeira, \n    \/\/ and apply it to our local chart axis to prevent timezone shifting bugs.\n    const alignToAxis = (dateObj) => {\n      const parts = new Intl.DateTimeFormat('en-US', {\n        timeZone: 'Atlantic\/Madeira',\n        hour: 'numeric', minute: 'numeric', second: 'numeric', hour12: false\n      }).formatToParts(dateObj);\n      \n      let h = 0, m = 0, s = 0;\n      parts.forEach(p => {\n        if (p.type === 'hour') h = parseInt(p.value);\n        if (p.type === 'minute') m = parseInt(p.value);\n        if (p.type === 'second') s = parseInt(p.value);\n      });\n      \n      const localDate = new Date(activeDate);\n      localDate.setHours(h === 24 ? 0 : h, m, s, 0);\n      return localDate.getTime();\n    };\n\n    const sunrise = alignToAxis(times.sunrise);\n    const sunset = alignToAxis(times.sunset);\n\n    const drawNight = (startTime, endTime) => {\n      const startPx = Math.max(left, x.getPixelForValue(startTime));\n      const endPx = Math.min(right, x.getPixelForValue(endTime));\n\n      \/\/ Only draw if the night section is actually visible on the chart\n      if (startPx < endPx) {\n        ctx.save();\n        ctx.fillStyle = 'rgba(0, 0, 0, 0.05)'; \/\/ Gentle night shade\n        ctx.fillRect(startPx, top, endPx - startPx, bottom - top);\n        ctx.restore();\n      }\n    };\n\n    \/\/ Set boundaries for the current day locally\n    const startOfDay = new Date(activeDate);\n    startOfDay.setHours(0, 0, 0, 0);\n    const endOfDay = new Date(activeDate);\n    endOfDay.setHours(23, 59, 59, 999);\n\n    \/\/ Draw the night blocks\n    drawNight(startOfDay.getTime(), sunrise);\n    drawNight(sunset, endOfDay.getTime());\n  }\n};\n\/\/ end of day night background plugin\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    } 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    \/\/ Label Update Logic (Now handles all timeframes correctly)\n    if (timeframe === 'year') {\n      stationLabel.innerText = \"Maiata wind 12h to 18h\";\n    } else if (is24hMode) {\n      stationLabel.innerText = \"Maiata wind 24h\";\n    } else {\n      stationLabel.innerText = \"Maiata wind 9h to 21h\";\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      else 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\n          \/\/ Filters for the 12:00 entry (the 12h-18h block) for every day\n            .filter(d => d.t.includes(\"12:00:00\")) \n            .map(d => ({ \n              \/\/ Convert string to timestamp for Chart.js\n              t: new Date(d.t.replace(\/-\/g, \"\/\")).getTime(), \n              s: d.wind, \n              g: d.gust, \n              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,\n            borderWidth: { 'day': 1.2, 'week': 0.7, 'month': 0.2, 'year': 1.1 }[currentTimeframe],\n            pointRadius: { '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\n            borderWidth: { 'day': 1.2, 'week': 0.6, 'month': 1.2, 'year': 1.0 }[currentTimeframe],\n            pointRadius: { '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\n            \/\/ This makes the graph go edge to edge 9h to 21h and 24h\n            min: currentTimeframe === 'day' ? new Date(getISO(activeDate) + 'T' + (is24hMode ? '00:00:00' : '09:00:00')).getTime() : undefined,\n            max: currentTimeframe === 'day' ? new Date(getISO(activeDate) + 'T' + (is24hMode ? '24:00:00' : '21:00:00')).getTime() : undefined,\n\n            ticks: {\n   \/\/ Set stepSize to a solid 1 hour; autoSkip will handle crowding in 24h mode\n         \/\/ 1 hour * 60 min * 60 sec * 1000 ms\n              stepSize: currentTimeframe === 'day' ? (60 * 60 * 1000) : undefined,\n              \n              \/\/ Added properties for resolution and scaling limits\n              autoSkip: true,\n              maxTicksLimit: 10,\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            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\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      plugins: [solarBackgroundPlugin, idealDirectionPlugin] \/\/ Flyable direction Plugin inside the Chart object and sun rise and set bg color plugin\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\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<\/template>\n\n\n\n<!-- Day plus Precious selection, 7 days, 30 days and year Wind 9h to 21h -->\n<!-- added legend visual enhance below -->\n<template>\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      <\/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\">MAIATA 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  \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<!-- Added SunCalc for historical day\/night bg color accuracy -->\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/suncalc\/1.9.0\/suncalc.min.js\"><\/script>\n\n<script>\n(function() {\n  let windChart;\n  let isReadingMode = true; \/\/ Added state for mode toggle\n  let currentTimeframe = 'day';\n  let activeDate = new Date();\n\n  \/\/ New 24h wind button\n  let is24hMode = false;\n\n  window.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\/maiata\/';\n  const stationStartDate = new Date(\"2026-02-07\"); \/\/ MAIATA - Change this date based on weather station deployment\n\n  \/\/ --- CONFIG IDEAL FLYABLE Direction ---\n  const showIdealCorridor = true; \/\/ set false to hide \n  const idealMin = 333;          \/\/ NorthWest\n  const idealMax = 55;          \/\/ NorthEast\n  const corridorColor = 'rgba(40, 200, 40, 0.05)'; \n\n\/\/ Flyable directions Plugin\n  const idealDirectionPlugin = {\n    id: 'idealDirection',\n    beforeDraw: (chart) => {\n      if (!showIdealCorridor) return;\n      \n      const { ctx, chartArea: { left, right }, scales: { x, yDir } } = chart;\n      if (!yDir || !x) return; \/\/ Safety check    \n\n      let startX = left;\n      let endX = right;\n\n      \/\/ Restrict corridor to daytime only in 'day' view\n      if (currentTimeframe === 'day') {\n        const lat = 32.77;\n        const lng = -16.83;\n        const times = SunCalc.getTimes(activeDate, lat, lng);\n\n        const alignToAxis = (dateObj) => {\n          const parts = new Intl.DateTimeFormat('en-US', {\n            timeZone: 'Atlantic\/Madeira',\n            hour: 'numeric', minute: 'numeric', second: 'numeric', hour12: false\n          }).formatToParts(dateObj);\n          \n          let h = 0, m = 0, s = 0;\n          parts.forEach(p => {\n            if (p.type === 'hour') h = parseInt(p.value);\n            if (p.type === 'minute') m = parseInt(p.value);\n            if (p.type === 'second') s = parseInt(p.value);\n          });\n          \n          const localDate = new Date(activeDate);\n          localDate.setHours(h === 24 ? 0 : h, m, s, 0);\n          return localDate.getTime();\n        };\n\n        const sunrise = alignToAxis(times.sunrise);\n        const sunset = alignToAxis(times.sunset);\n\n        \/\/ Convert times to pixel coordinates and clamp to the visible chart area\n        startX = Math.max(left, x.getPixelForValue(sunrise));\n        endX = Math.min(right, x.getPixelForValue(sunset));\n      }\n\n      ctx.save();\n      ctx.fillStyle = corridorColor;\n\n      const drawBox = (min, max) => {\n        const yTop = yDir.getPixelForValue(max);\n        const yBottom = yDir.getPixelForValue(min);\n        \n        \/\/ Only draw the box if the daytime area is currently visible on the screen\n        if (startX < endX) {\n          ctx.fillRect(startX, yTop, endX - startX, yBottom - yTop);\n        }\n      };\n\n      if (idealMin > idealMax) {\n        drawBox(idealMin, 360);\n        drawBox(0, idealMax);\n      } else {\n        drawBox(idealMin, idealMax);\n      }\n      ctx.restore();\n    }\n  };\n\n\/\/ NEW: Day\/Night Background Plugin for Historical Accuracy start\nconst solarBackgroundPlugin = {\n  id: 'solarBackground',\n  beforeDraw: (chart) => {\n    if (currentTimeframe !== 'day') return;\n\n    const { ctx, chartArea: { top, bottom, left, right }, scales: { x } } = chart;\n    if (!x) return;\n\n    \/\/ Coordinates for Madeira Island\n    const lat = 32.77;\n    const lng = -16.83;\n\n    \/\/ Get the absolute UTC solar times for the date\n    const times = SunCalc.getTimes(activeDate, lat, lng);\n\n    \/\/ Timezone alignment: Extract the exact wall-clock hour\/minute in Madeira, \n    \/\/ and apply it to our local chart axis to prevent timezone shifting bugs.\n    const alignToAxis = (dateObj) => {\n      const parts = new Intl.DateTimeFormat('en-US', {\n        timeZone: 'Atlantic\/Madeira',\n        hour: 'numeric', minute: 'numeric', second: 'numeric', hour12: false\n      }).formatToParts(dateObj);\n      \n      let h = 0, m = 0, s = 0;\n      parts.forEach(p => {\n        if (p.type === 'hour') h = parseInt(p.value);\n        if (p.type === 'minute') m = parseInt(p.value);\n        if (p.type === 'second') s = parseInt(p.value);\n      });\n      \n      const localDate = new Date(activeDate);\n      localDate.setHours(h === 24 ? 0 : h, m, s, 0);\n      return localDate.getTime();\n    };\n\n    const sunrise = alignToAxis(times.sunrise);\n    const sunset = alignToAxis(times.sunset);\n\n    const drawNight = (startTime, endTime) => {\n      const startPx = Math.max(left, x.getPixelForValue(startTime));\n      const endPx = Math.min(right, x.getPixelForValue(endTime));\n\n      \/\/ Only draw if the night section is actually visible on the chart\n      if (startPx < endPx) {\n        ctx.save();\n        ctx.fillStyle = 'rgba(0, 0, 0, 0.09)'; \/\/ Gentle night shade\n        ctx.fillRect(startPx, top, endPx - startPx, bottom - top);\n        ctx.restore();\n      }\n    };\n\n    \/\/ Set boundaries for the current day locally\n    const startOfDay = new Date(activeDate);\n    startOfDay.setHours(0, 0, 0, 0);\n    const endOfDay = new Date(activeDate);\n    endOfDay.setHours(23, 59, 59, 999);\n\n    \/\/ Draw the night blocks\n    drawNight(startOfDay.getTime(), sunrise);\n    drawNight(sunset, endOfDay.getTime());\n  }\n};\n\/\/ end of day night background plugin\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    } 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    \/\/ Label Update Logic (Now handles all timeframes correctly)\n    if (timeframe === 'year') {\n      stationLabel.innerText = \"Maiata wind 12h to 18h\";\n    } else if (is24hMode) {\n      stationLabel.innerText = \"Maiata wind 24h\";\n    } else {\n      stationLabel.innerText = \"Maiata wind 9h to 21h\";\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   \/\/ New block with gap awareness\n    try {\n      let rawData = [];\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        filtered.forEach((d) => {\n          rawData.push({\n            t: new Date(d.time).getTime(),\n            s: d.windSpeed?.kph ?? 0,\n            g: d.windGust?.kph ?? 0,\n            d: d.winddir ?? null\n          });\n        });\n      }\n      else 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 => rawData.push({ t: s.t, s: s.s, g: s.g, d: s.d }));\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          rawData = days\n            \/\/ Filters for the 12:00 entry (the 12h-18h block) for every day\n            .filter(d => d.t.includes(\"12:00:00\")) \n            .map(d => ({ \n              \/\/ Convert string to timestamp for Chart.js\n              t: new Date(d.t.replace(\/-\/g, \"\/\")).getTime(), \n              s: d.wind, \n              g: d.gust, \n              d: d.dir \n            }));\n        } else {\n          days.forEach(day => {\n            const samples = day.wind?.samples || [];\n            samples.forEach(s => rawData.push({ t: s.t, s: s.s, g: s.g, d: s.d }));\n          });\n        }\n      }\n\n      \/\/ --- UNIVERSAL GAP FILTER ---\n      let gapThreshold;\n      if (timeframe === 'day') gapThreshold = 10 * 60 * 1000; \/\/ 10 minutes\n      else if (timeframe === 'year') gapThreshold = 25 * 60 * 60 * 1000; \/\/ 25 hours (allows daily 12:00 readings to connect, but breaks if a full day is skipped)\n      else gapThreshold = 2 * 60 * 60 * 1000; \/\/ 2 hours for week\/month (breaks the line overnight)\n\n      let processedData = [];\n      rawData.forEach((point, i) => {\n        if (i > 0) {\n          const prevPoint = rawData[i - 1];\n          \/\/ If the time gap between points exceeds the threshold, inject breaks\n          if (point.t - prevPoint.t > gapThreshold) {\n            processedData.push({ t: prevPoint.t + 1000, s: null, g: null, d: null });\n            processedData.push({ t: point.t - 1000, s: null, g: null, d: null });\n          }\n        }\n        processedData.push(point);\n      });\n\n      if (status) status.innerText = \"\";\n      renderChart(processedData);\n      \n    } catch (e) {\n      console.error(e);\n      if (status) status.innerText = \"Data Pending...\";\n    }\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,\n            borderWidth: { 'day': 1.2, 'week': 0.7, 'month': 0.2, 'year': 1.1 }[currentTimeframe],\n            pointRadius: { '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\n            borderWidth: { 'day': 1.2, 'week': 0.6, 'month': 1.2, 'year': 1.0 }[currentTimeframe],\n            pointRadius: { '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\n            \/\/ This makes the graph go edge to edge 9h to 21h and 24h\n            min: currentTimeframe === 'day' ? new Date(getISO(activeDate) + 'T' + (is24hMode ? '00:00:00' : '09:00:00')).getTime() : undefined,\n            max: currentTimeframe === 'day' ? new Date(getISO(activeDate) + 'T' + (is24hMode ? '24:00:00' : '21:00:00')).getTime() : undefined,\n\n            ticks: {\n   \/\/ Set stepSize to a solid 1 hour; autoSkip will handle crowding in 24h mode\n         \/\/ 1 hour * 60 min * 60 sec * 1000 ms\n              stepSize: currentTimeframe === 'day' ? (60 * 60 * 1000) : undefined,\n              \n              \/\/ Added properties for resolution and scaling limits\n              autoSkip: true,\n              maxTicksLimit: 10,\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            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\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      plugins: [solarBackgroundPlugin, idealDirectionPlugin] \/\/ Flyable direction Plugin inside the Chart object and sun rise and set bg color plugin\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\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<\/template>\n\n\n\n<!-- Day plus Precious selection, 7 days, 30 days and year Wind 9h to 21h -->\n<!-- added 24h wind and gust max on year view below -->\n<template>\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      <\/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\">MAIATA wind 9h to 21h<\/div>\n  <\/div>\n\n  <div class=\"sc-legend\" onclick=\"cycleChartViews()\" style=\"cursor: pointer; user-select: none; 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; transition: 0.3s;\">\n  <div id=\"leg-wind\" style=\"display: flex; align-items: center; gap: 4px; transition: opacity 0.3s;\"><span style=\"width: 8px; height: 3px; background: rgba(41, 128, 185, 0.7);\"><\/span> Wind<\/div>\n  <div id=\"leg-gust\" style=\"display: flex; align-items: center; gap: 4px; transition: opacity 0.3s;\"><span style=\"width: 8px; height: 3px; background: rgba(230, 126, 34, 0.4);\"><\/span> Gusts<\/div>\n  <div id=\"leg-dir\" style=\"display: flex; align-items: center; gap: 4px; transition: opacity 0.3s;\"><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\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  \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<!-- Added SunCalc for historical day\/night bg color accuracy -->\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/suncalc\/1.9.0\/suncalc.min.js\"><\/script>\n\n<script>\n(function() {\n  let windChart;\n  let isReadingMode = true; \/\/ Added state for mode toggle\n  let currentTimeframe = 'day';\n  let activeDate = new Date();\n\n  \/\/ New 24h wind button\n  let is24hMode = false;\n  let currentCycleState = 0; \/\/ Moved up to allow resetting when timeframes change\n\n  window.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\/maiata\/';\n  const stationStartDate = new Date(\"2026-02-07\"); \/\/ MAIATA - Change this date based on weather station deployment\n\n  \/\/ --- CONFIG IDEAL FLYABLE Direction ---\n  const showIdealCorridor = true; \/\/ set false to hide \n  const idealMin = 333;          \/\/ NorthWest\n  const idealMax = 55;          \/\/ NorthEast\n  const corridorColor = 'rgba(40, 200, 40, 0.05)'; \n\n\/\/ Flyable directions Plugin\n    const idealDirectionPlugin = {\n    id: 'idealDirection',\n    beforeDraw: (chart) => {\n\/\/ Added: !chart.isDatasetVisible(0) to hide when direction is toggled off\n      if (!showIdealCorridor || !chart.isDatasetVisible(0)) return;\n\n      \n      const { ctx, chartArea: { left, right }, scales: { x, yDir } } = chart;\n      if (!yDir || !x) return; \/\/ Safety check    \n\n      let startX = left;\n      let endX = right;\n\n      \/\/ Restrict corridor to daytime only in 'day' view\n      if (currentTimeframe === 'day') {\n        const lat = 32.77;\n        const lng = -16.83;\n        const times = SunCalc.getTimes(activeDate, lat, lng);\n\n        const alignToAxis = (dateObj) => {\n          const parts = new Intl.DateTimeFormat('en-US', {\n            timeZone: 'Atlantic\/Madeira',\n            hour: 'numeric', minute: 'numeric', second: 'numeric', hour12: false\n          }).formatToParts(dateObj);\n          \n          let h = 0, m = 0, s = 0;\n          parts.forEach(p => {\n            if (p.type === 'hour') h = parseInt(p.value);\n            if (p.type === 'minute') m = parseInt(p.value);\n            if (p.type === 'second') s = parseInt(p.value);\n          });\n          \n          const localDate = new Date(activeDate);\n          localDate.setHours(h === 24 ? 0 : h, m, s, 0);\n          return localDate.getTime();\n        };\n\n        const sunrise = alignToAxis(times.sunrise);\n        const sunset = alignToAxis(times.sunset);\n\n        \/\/ Convert times to pixel coordinates and clamp to the visible chart area\n        startX = Math.max(left, x.getPixelForValue(sunrise));\n        endX = Math.min(right, x.getPixelForValue(sunset));\n      }\n\n      ctx.save();\n      ctx.fillStyle = corridorColor;\n\n      const drawBox = (min, max) => {\n        const yTop = yDir.getPixelForValue(max);\n        const yBottom = yDir.getPixelForValue(min);\n        \n        \/\/ Only draw the box if the daytime area is currently visible on the screen\n        if (startX < endX) {\n          ctx.fillRect(startX, yTop, endX - startX, yBottom - yTop);\n        }\n      };\n\n      if (idealMin > idealMax) {\n        drawBox(idealMin, 360);\n        drawBox(0, idealMax);\n      } else {\n        drawBox(idealMin, idealMax);\n      }\n      ctx.restore();\n    }\n  };\n\n\/\/ NEW: Day\/Night Background Plugin for Historical Accuracy start\nconst solarBackgroundPlugin = {\n  id: 'solarBackground',\n  beforeDraw: (chart) => {\n    if (currentTimeframe !== 'day') return;\n\n    const { ctx, chartArea: { top, bottom, left, right }, scales: { x } } = chart;\n    if (!x) return;\n\n    \/\/ Coordinates for Madeira Island\n    const lat = 32.77;\n    const lng = -16.83;\n\n    \/\/ Get the absolute UTC solar times for the date\n    const times = SunCalc.getTimes(activeDate, lat, lng);\n\n    \/\/ Timezone alignment: Extract the exact wall-clock hour\/minute in Madeira, \n    \/\/ and apply it to our local chart axis to prevent timezone shifting bugs.\n    const alignToAxis = (dateObj) => {\n      const parts = new Intl.DateTimeFormat('en-US', {\n        timeZone: 'Atlantic\/Madeira',\n        hour: 'numeric', minute: 'numeric', second: 'numeric', hour12: false\n      }).formatToParts(dateObj);\n      \n      let h = 0, m = 0, s = 0;\n      parts.forEach(p => {\n        if (p.type === 'hour') h = parseInt(p.value);\n        if (p.type === 'minute') m = parseInt(p.value);\n        if (p.type === 'second') s = parseInt(p.value);\n      });\n      \n      const localDate = new Date(activeDate);\n      localDate.setHours(h === 24 ? 0 : h, m, s, 0);\n      return localDate.getTime();\n    };\n\n    const sunrise = alignToAxis(times.sunrise);\n    const sunset = alignToAxis(times.sunset);\n\n    const drawNight = (startTime, endTime) => {\n      const startPx = Math.max(left, x.getPixelForValue(startTime));\n      const endPx = Math.min(right, x.getPixelForValue(endTime));\n\n      \/\/ Only draw if the night section is actually visible on the chart\n      if (startPx < endPx) {\n        ctx.save();\n        ctx.fillStyle = 'rgba(0, 0, 0, 0.09)'; \/\/ Gentle night shade\n        ctx.fillRect(startPx, top, endPx - startPx, bottom - top);\n        ctx.restore();\n      }\n    };\n\n    \/\/ Set boundaries for the current day locally\n    const startOfDay = new Date(activeDate);\n    startOfDay.setHours(0, 0, 0, 0);\n    const endOfDay = new Date(activeDate);\n    endOfDay.setHours(23, 59, 59, 999);\n\n    \/\/ Draw the night blocks\n    drawNight(startOfDay.getTime(), sunrise);\n    drawNight(sunset, endOfDay.getTime());\n  }\n};\n\/\/ end of day night background plugin\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 \/\/ Reset legend visual state when changing timeframes\n    currentCycleState = 0;\n    document.getElementById('leg-wind').style.opacity = '1';\n    document.getElementById('leg-gust').style.opacity = '1';\n    document.getElementById('leg-dir').style.opacity = '1';\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    } 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    \/\/ Label Update Logic (Now handles all timeframes correctly)\n    if (timeframe === 'year') {\n      stationLabel.innerText = \"Maiata wind 12h to 18h\";\n    } else if (is24hMode) {\n      stationLabel.innerText = \"Maiata wind 24h\";\n    } else {\n      stationLabel.innerText = \"Maiata wind 9h to 21h\";\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   \/\/ New block with gap awareness\n    try {\n      let rawData = [];\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        filtered.forEach((d) => {\n          rawData.push({\n            t: new Date(d.time).getTime(),\n            s: d.windSpeed?.kph ?? 0,\n            g: d.windGust?.kph ?? 0,\n            d: d.winddir ?? null\n          });\n        });\n      }\n      else 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 => rawData.push({ t: s.t, s: s.s, g: s.g, d: s.d }));\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          rawData = days\n            \/\/ Filters for the 12:00 entry (the 12h-18h block) for every day\n            .filter(d => d.t.includes(\"12:00:00\")) \n            .map(d => ({ \n              \/\/ Convert string to timestamp for Chart.js\n              t: new Date(d.t.replace(\/-\/g, \"\/\")).getTime(), \n              s: d.wind, \n              g: d.gust, \n              d: d.dir \n            }));\n        } else {\n          days.forEach(day => {\n            const samples = day.wind?.samples || [];\n            samples.forEach(s => rawData.push({ t: s.t, s: s.s, g: s.g, d: s.d }));\n          });\n        }\n      }\n\n      \/\/ --- UNIVERSAL GAP FILTER ---\n      let gapThreshold;\n      if (timeframe === 'day') gapThreshold = 10 * 60 * 1000; \/\/ 10 minutes\n      else if (timeframe === 'year') gapThreshold = 25 * 60 * 60 * 1000; \/\/ 25 hours (allows daily 12:00 readings to connect, but breaks if a full day is skipped)\n      else gapThreshold = 2 * 60 * 60 * 1000; \/\/ 2 hours for week\/month (breaks the line overnight)\n\n      let processedData = [];\n      rawData.forEach((point, i) => {\n        if (i > 0) {\n          const prevPoint = rawData[i - 1];\n          \/\/ If the time gap between points exceeds the threshold, inject breaks\n          if (point.t - prevPoint.t > gapThreshold) {\n            processedData.push({ t: prevPoint.t + 1000, s: null, g: null, d: null });\n            processedData.push({ t: point.t - 1000, s: null, g: null, d: null });\n          }\n        }\n        processedData.push(point);\n      });\n\n      if (status) status.innerText = \"\";\n      renderChart(processedData);\n      \n    } catch (e) {\n      console.error(e);\n      if (status) status.innerText = \"Data Pending...\";\n    }\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,\n            borderWidth: { 'day': 1.2, 'week': 0.7, 'month': 0.2, 'year': 1.1 }[currentTimeframe],\n            pointRadius: { '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\n            backgroundColor: 'rgba(41, 128, 185, 0.06)', \/\/ Added: Gentle blue fill color\n            fill: false, \/\/ Added: Starts hidden, toggles dynamically\n            borderWidth: { 'day': 1.2, 'week': 0.6, 'month': 1.2, 'year': 1.0 }[currentTimeframe],\n\n            pointRadius: { '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\n            \/\/ This makes the graph go edge to edge 9h to 21h and 24h\n            min: currentTimeframe === 'day' ? new Date(getISO(activeDate) + 'T' + (is24hMode ? '00:00:00' : '09:00:00')).getTime() : undefined,\n            max: currentTimeframe === 'day' ? new Date(getISO(activeDate) + 'T' + (is24hMode ? '24:00:00' : '21:00:00')).getTime() : undefined,\n\n            ticks: {\n   \/\/ Set stepSize to a solid 1 hour; autoSkip will handle crowding in 24h mode\n         \/\/ 1 hour * 60 min * 60 sec * 1000 ms\n              stepSize: currentTimeframe === 'day' ? (60 * 60 * 1000) : undefined,\n              \n\/\/ Added properties for resolution and scaling limits with 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            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\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      plugins: [solarBackgroundPlugin, idealDirectionPlugin] \/\/ Flyable direction Plugin inside the Chart object and sun rise and set bg color plugin\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\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\/\/ Legend button Cycle Logic with dynamic opacity & axis mirroring\n  window.cycleChartViews = function() {\n    currentCycleState = (currentCycleState + 1) % 7;\n    if (!windChart) return;\n\n    \/\/ Helper to set chart dataset visibility and legend opacity\n    const setVis = (index, legendId, isVisible) => {\n      windChart.setDatasetVisibility(index, isVisible);\n      document.getElementById(legendId).style.opacity = isVisible ? '1' : '0.3';\n    };\n\n    \/\/ Chart dataset structure: 0 = Direction, 1 = Gusts, 2 = Wind\n    if (currentCycleState === 0) { \n      \/\/ Default: All visible\n      setVis(2, 'leg-wind', true); setVis(1, 'leg-gust', true); setVis(0, 'leg-dir', true);\n    } else if (currentCycleState === 1) { \n      \/\/ 1st click: Gusts + Direction\n      setVis(2, 'leg-wind', false); setVis(1, 'leg-gust', true); setVis(0, 'leg-dir', true);\n    } else if (currentCycleState === 2) { \n      \/\/ 2nd click: Wind + Direction\n      setVis(2, 'leg-wind', true); setVis(1, 'leg-gust', false); setVis(0, 'leg-dir', true);\n    } else if (currentCycleState === 3) { \n      \/\/ 3rd click: Just Direction\n      setVis(2, 'leg-wind', false); setVis(1, 'leg-gust', false); setVis(0, 'leg-dir', true);\n    } else if (currentCycleState === 4) { \n      \/\/ 4th click: Just Wind\n      setVis(2, 'leg-wind', true); setVis(1, 'leg-gust', false); setVis(0, 'leg-dir', false);\n    } else if (currentCycleState === 5) {\n      \/\/ 5th click: Just Gusts\n      setVis(2, 'leg-wind', false); setVis(1, 'leg-gust', true); setVis(0, 'leg-dir', false);\n    } else if (currentCycleState === 6) { \n      \/\/ 6th click: Wind + Gusts\n      setVis(2, 'leg-wind', true); setVis(1, 'leg-gust', true); setVis(0, 'leg-dir', false);\n    }\n\n    \/\/ Evaluate active views\n    const isWindActive = windChart.isDatasetVisible(2);\n    const isGustActive = windChart.isDatasetVisible(1);\n    const isDirActive = windChart.isDatasetVisible(0);\n    const isSpeedActive = isWindActive || isGustActive;\n\n   \/\/ 1. DYNAMIC OPACITY BOOSTING\n    \/\/ Wind opacity logic\n    if (isWindActive && !isGustActive) {\n\/\/ Wind only (Strong boost + Background Fill)\n      windChart.data.datasets[2].borderColor = 'rgba(41, 128, 185, 0.60)';\nwindChart.data.datasets[2].fill = true;\n    } else if (isWindActive && isGustActive && !isDirActive) {\n \/\/ Wind + Gusts together without Direction (Slight boost)\n      windChart.data.datasets[2].borderColor = 'rgba(41, 128, 185, 0.45)';\nwindChart.data.datasets[2].fill = false;\n    } else {\n      \/\/ Default standard view\n      windChart.data.datasets[2].borderColor = 'rgba(41, 128, 185, 0.30)';\nwindChart.data.datasets[2].fill = false;\n    }\n\n\n    \/\/ Gust opacity logic\n    if (isGustActive && !isWindActive) {\n      \/\/ Gusts only (Strong boost)\n      windChart.data.datasets[1].borderColor = 'rgba(230, 126, 34, 0.60)';\n      windChart.data.datasets[1].backgroundColor = 'rgba(230, 126, 34, 0.10)';\n    } else if (isWindActive && isGustActive && !isDirActive) {\n \/\/ Wind + Gusts together without Direction (Slight boost)\n      windChart.data.datasets[1].borderColor = 'rgba(230, 126, 34, 0.40)';\n      windChart.data.datasets[1].backgroundColor = 'rgba(230, 126, 34, 0.08)';\n    } else {\n      \/\/ Default standard view\n      windChart.data.datasets[1].borderColor = 'rgba(230, 126, 34, 0.10)';\n      windChart.data.datasets[1].backgroundColor = 'rgba(230, 126, 34, 0.05)';\n    }\n\n\n    \/\/ 2. DYNAMIC AXIS MIRRORING\n    const scales = windChart.options.scales;\n\n\/\/ Define mirrored scales on demand if they do not exist yet\n    if (!scales.ySpeedRight) {\n      scales.ySpeedRight = {\n        position: 'right',\n        ticks: { font: { size: 10 }, color: '#888' },\n        grid: { display: false },\n        afterBuildTicks: (axis) => {\n          const mainAxis = axis.chart.scales.ySpeed;\n          if (mainAxis && mainAxis.ticks) {\n            axis.min = mainAxis.min;\n            axis.max = mainAxis.max;\n            axis.ticks = [...mainAxis.ticks]; \n          }\n        }\n      };\n    }\n    if (!scales.yDirLeft) {\n      scales.yDirLeft = {\n        position: 'left',\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\n\/\/ Toggle mirror states based on view selection\n    if (isSpeedActive && !isDirActive) {\n\/\/ Speed active only -> Display speed on left AND right, turn off direction\n      scales.ySpeed.display = true;\n      scales.ySpeedRight.display = true;\n      if (scales.yDir) scales.yDir.display = false;\n      scales.yDirLeft.display = false;\n    } else if (isDirActive && !isSpeedActive) {\n\/\/ Direction active only -> Display direction on right AND left, turn off speed\n      scales.yDir.display = true;\n      scales.yDirLeft.display = true;\n      if (scales.ySpeed) scales.ySpeed.display = false;\n      scales.ySpeedRight.display = false;\n    } else {\n      \/\/ Standard view -> Speed on left, Direction on right, mirrors turned off\n      if (scales.ySpeed) scales.ySpeed.display = isSpeedActive;\n      if (scales.yDir) scales.yDir.display = isDirActive;\n      scales.ySpeedRight.display = false;\n      scales.yDirLeft.display = false;\n    }\n\n    \/\/ Instant update without re-triggering entrance animation\n    windChart.update('none'); \n  };\n\n\n})();\n<\/script>\n<\/template>\n\n\n\n<!-- Day plus Precious selection, 7 days, 30 days and year Wind 9h to 21h -->\n<!-- added 24h wind and gust max on year view -->\n<!-- added avg daily wind on the year graph below -->\n<template>\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\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      <\/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\n\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\">Maiata wind 9h to 21h<\/div>\n  <\/div>\n\n  <div class=\"sc-legend\" onclick=\"cycleChartViews()\" style=\"cursor: pointer; user-select: none; 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; transition: 0.3s;\">\n  <div id=\"leg-wind\" style=\"display: flex; align-items: center; gap: 4px; transition: opacity 0.3s;\"><span style=\"width: 8px; height: 3px; background: rgba(41, 128, 185, 0.7);\"><\/span> Wind<\/div>\n  <div id=\"leg-gust\" style=\"display: flex; align-items: center; gap: 4px; transition: opacity 0.3s;\"><span style=\"width: 8px; height: 3px; background: rgba(230, 126, 34, 0.4);\"><\/span> Gusts<\/div>\n  <div id=\"leg-dir\" style=\"display: flex; align-items: center; gap: 4px; transition: opacity 0.3s;\"><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\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  \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<!-- Added SunCalc for historical day\/night bg color accuracy -->\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/suncalc\/1.9.0\/suncalc.min.js\"><\/script>\n\n<script>\n(function() {\n  let windChart;\n  let isReadingMode = true; \/\/ Added state for mode toggle\n  let currentTimeframe = 'day';\n  let activeDate = new Date();\n\n  \/\/ New 24h wind button\n  let is24hMode = false;\n  let currentCycleState = 0; \/\/ Moved up to allow resetting when timeframes change\n\n  window.toggle24h = function() {\n    is24hMode = !is24hMode;\n    loadWindData(currentTimeframe);\n  };\n\n  const stationPath = 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/maiata\/';\n  const stationStartDate = new Date(\"2026-02-07\"); \/\/ MAIATA - Change this date based on weather station deployment\n\n  \/\/ --- CONFIG IDEAL FLYABLE Direction ---\n  const showIdealCorridor = true; \/\/ set false to hide \n  const idealMin = 333;          \/\/ NorthWest\n  const idealMax = 55;          \/\/ NorthEast\n  const corridorColor = 'rgba(40, 200, 40, 0.05)'; \n\n\/\/ Flyable directions Plugin\n    const idealDirectionPlugin = {\n    id: 'idealDirection',\n    beforeDraw: (chart) => {\n\/\/ Added: !chart.isDatasetVisible(0) to hide when direction is toggled off\n      if (!showIdealCorridor || !chart.isDatasetVisible(0)) return;\n\n      \n      const { ctx, chartArea: { left, right }, scales: { x, yDir } } = chart;\n      if (!yDir || !x) return; \/\/ Safety check    \n\n      let startX = left;\n      let endX = right;\n\n      \/\/ Restrict corridor to daytime only in 'day' view\n      if (currentTimeframe === 'day') {\n        const lat = 32.77;\n        const lng = -16.83;\n        const times = SunCalc.getTimes(activeDate, lat, lng);\n\n        const alignToAxis = (dateObj) => {\n          const parts = new Intl.DateTimeFormat('en-US', {\n            timeZone: 'Atlantic\/Madeira',\n            hour: 'numeric', minute: 'numeric', second: 'numeric', hour12: false\n          }).formatToParts(dateObj);\n          \n          let h = 0, m = 0, s = 0;\n          parts.forEach(p => {\n            if (p.type === 'hour') h = parseInt(p.value);\n            if (p.type === 'minute') m = parseInt(p.value);\n            if (p.type === 'second') s = parseInt(p.value);\n          });\n          \n          const localDate = new Date(activeDate);\n          localDate.setHours(h === 24 ? 0 : h, m, s, 0);\n          return localDate.getTime();\n        };\n\n        const sunrise = alignToAxis(times.sunrise);\n        const sunset = alignToAxis(times.sunset);\n\n        \/\/ Convert times to pixel coordinates and clamp to the visible chart area\n        startX = Math.max(left, x.getPixelForValue(sunrise));\n        endX = Math.min(right, x.getPixelForValue(sunset));\n      }\n\n      ctx.save();\n      ctx.fillStyle = corridorColor;\n\n      const drawBox = (min, max) => {\n        const yTop = yDir.getPixelForValue(max);\n        const yBottom = yDir.getPixelForValue(min);\n        \n        \/\/ Only draw the box if the daytime area is currently visible on the screen\n        if (startX < endX) {\n          ctx.fillRect(startX, yTop, endX - startX, yBottom - yTop);\n        }\n      };\n\n      if (idealMin > idealMax) {\n        drawBox(idealMin, 360);\n        drawBox(0, idealMax);\n      } else {\n        drawBox(idealMin, idealMax);\n      }\n      ctx.restore();\n    }\n  };\n\n\/\/ NEW: Day\/Night Background Plugin for Historical Accuracy start\nconst solarBackgroundPlugin = {\n  id: 'solarBackground',\n  beforeDraw: (chart) => {\n    if (currentTimeframe !== 'day') return;\n\n    const { ctx, chartArea: { top, bottom, left, right }, scales: { x } } = chart;\n    if (!x) return;\n\n    \/\/ Coordinates for Madeira Island\n    const lat = 32.77;\n    const lng = -16.83;\n\n    \/\/ Get the absolute UTC solar times for the date\n    const times = SunCalc.getTimes(activeDate, lat, lng);\n\n    \/\/ Timezone alignment: Extract the exact wall-clock hour\/minute in Madeira, \n    \/\/ and apply it to our local chart axis to prevent timezone shifting bugs.\n    const alignToAxis = (dateObj) => {\n      const parts = new Intl.DateTimeFormat('en-US', {\n        timeZone: 'Atlantic\/Madeira',\n        hour: 'numeric', minute: 'numeric', second: 'numeric', hour12: false\n      }).formatToParts(dateObj);\n      \n      let h = 0, m = 0, s = 0;\n      parts.forEach(p => {\n        if (p.type === 'hour') h = parseInt(p.value);\n        if (p.type === 'minute') m = parseInt(p.value);\n        if (p.type === 'second') s = parseInt(p.value);\n      });\n      \n      const localDate = new Date(activeDate);\n      localDate.setHours(h === 24 ? 0 : h, m, s, 0);\n      return localDate.getTime();\n    };\n\n    const sunrise = alignToAxis(times.sunrise);\n    const sunset = alignToAxis(times.sunset);\n\n    const drawNight = (startTime, endTime) => {\n      const startPx = Math.max(left, x.getPixelForValue(startTime));\n      const endPx = Math.min(right, x.getPixelForValue(endTime));\n\n      \/\/ Only draw if the night section is actually visible on the chart\n      if (startPx < endPx) {\n        ctx.save();\n        ctx.fillStyle = 'rgba(0, 0, 0, 0.09)'; \/\/ Gentle night shade\n        ctx.fillRect(startPx, top, endPx - startPx, bottom - top);\n        ctx.restore();\n      }\n    };\n\n    \/\/ Set boundaries for the current day locally\n    const startOfDay = new Date(activeDate);\n    startOfDay.setHours(0, 0, 0, 0);\n    const endOfDay = new Date(activeDate);\n    endOfDay.setHours(23, 59, 59, 999);\n\n    \/\/ Draw the night blocks\n    drawNight(startOfDay.getTime(), sunrise);\n    drawNight(sunset, endOfDay.getTime());\n  }\n};\n\/\/ end of day night background plugin\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 \/\/ Reset legend visual state when changing timeframes\n    currentCycleState = 0;\n    document.getElementById('leg-wind').style.opacity = '1';\n    document.getElementById('leg-gust').style.opacity = '1';\n    document.getElementById('leg-dir').style.opacity = '1';\n \n        \/\/ Navigation & 24h wind logic\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    \/\/ Select the date-specific elements so we can manage them independently\n    const prevBtn = document.getElementById('sc-wind-prev-btn');\n    const nextBtn = document.getElementById('sc-wind-next-btn');\n    const displayDate = document.getElementById('sc-wind-display-date');\n\n    if (timeframe === 'day') {\n      dateNav.style.display = 'flex';\n      prevBtn.style.display = '';       \/\/ Reverts to your CSS default\n      nextBtn.style.display = '';\n      displayDate.style.display = '';\n      toggleBtn.style.display = 'inline-block';\n    } else if (timeframe === 'year') {\n      dateNav.style.display = 'flex';   \/\/ Keep the container visible for the toggle button\n      prevBtn.style.display = 'none';   \/\/ Hide the day-navigation arrows and text\n      nextBtn.style.display = 'none';\n      displayDate.style.display = 'none';\n      toggleBtn.style.display = 'inline-block';\n    } else {\n      dateNav.style.display = 'none';\n      toggleBtn.style.display = 'none';\n      is24hMode = false; \n    }\n\n    if (toggleBtn) {\n      if (timeframe === 'year') {\n        toggleBtn.innerText = is24hMode ? \"Back to 12-18h\" : \"Show 24h\";\n      } else {\n        toggleBtn.innerText = is24hMode ? \"Back to 9-21h\" : \"Show 24h\";\n      }\n    }\n\n    \/\/ Label Update Logic\n    if (timeframe === 'year') {\n      stationLabel.innerText = is24hMode ? \"Maiata max Wind and Gust with precise time and direction 24h\" : \"Maiata max Wind and Gust with Avg direction 12h to 18h\";\n    } else if (is24hMode) {\n      stationLabel.innerText = \"Maiata wind 24h\";\n    } else {\n      stationLabel.innerText = \"Maiata wind 9h to 21h\";\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   \/\/ New block with gap awareness\n    try {\n      let rawData = [];\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        filtered.forEach((d) => {\n          rawData.push({\n            t: new Date(d.time).getTime(),\n            s: d.windSpeed?.kph ?? 0,\n            g: d.windGust?.kph ?? 0,\n            d: d.winddir ?? null\n          });\n        });\n      }\n      else 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 => rawData.push({ t: s.t, s: s.s, g: s.g, d: s.d }));\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          if (is24hMode) {\n            rawData = days\n\/\/ Grabs the 12:00 entry to ensure one point per day, then maps the 24h peak variables\n              .filter(d => d.t && d.t.includes(\"12:00:00\") && d.peak_24h_wind_time) \n              .map(d => ({\n                t: new Date(d.t.replace(\/-\/g, \"\/\")).getTime(), \/\/ Anchor to 12:00 to keep visual day gaps clean\n                s: d.peak_24h_wind, \n                g: d.peak_24h_gust, \n                d: d.peak_24h_wind_dir, \n                \/\/ Expose new PHP variables for the tooltip\n                wTime: d.peak_24h_wind_time,\n                gTime: d.peak_24h_gust_time,\n                wDir: d.peak_24h_wind_dir,\n                gDir: d.peak_24h_gust_dir,\n                avg24: d.wind_avg_24h\n              }));\n          } else {\n            rawData = days\n              .filter(d => d.t && d.t.includes(\"12:00:00\")) \n              .map(d => ({ \n                t: new Date(d.t.replace(\/-\/g, \"\/\")).getTime(), \n                s: d.wind, \n                g: d.gust, \n                d: d.dir \n              }));\n          }\n        } else {\n          days.forEach(day => {\n            const samples = day.wind?.samples || [];\n            samples.forEach(s => rawData.push({ t: s.t, s: s.s, g: s.g, d: s.d }));\n          });\n        }\n      }\n\n      \/\/ --- UNIVERSAL GAP FILTER ---\n      let gapThreshold;\n      if (timeframe === 'day') gapThreshold = 10 * 60 * 1000; \/\/ 10 minutes\n      else if (timeframe === 'year') gapThreshold = 25 * 60 * 60 * 1000; \/\/ 25 hours (allows daily 12:00 readings to connect, but breaks if a full day is skipped)\n      else gapThreshold = 2 * 60 * 60 * 1000; \/\/ 2 hours for week\/month (breaks the line overnight)\n\n      let processedData = [];\n      rawData.forEach((point, i) => {\n        if (i > 0) {\n          const prevPoint = rawData[i - 1];\n          \/\/ If the time gap between points exceeds the threshold, inject breaks\n          if (point.t - prevPoint.t > gapThreshold) {\n            processedData.push({ t: prevPoint.t + 1000, s: null, g: null, d: null });\n            processedData.push({ t: point.t - 1000, s: null, g: null, d: null });\n          }\n        }\n        processedData.push(point);\n      });\n\n      if (status) status.innerText = \"\";\n      renderChart(processedData);\n      \n    } catch (e) {\n      console.error(e);\n      if (status) status.innerText = \"Data Pending...\";\n    }\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, raw: d})), \/\/ Added raw: 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, raw: d})), \/\/ Added raw: d\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,\n            borderWidth: { 'day': 1.2, 'week': 0.7, 'month': 0.2, 'year': 1.1 }[currentTimeframe],\n            pointRadius: { '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, raw: d})), \/\/ Added raw: d\n            borderColor: 'rgba(41, 128, 185, 0.4)', \/\/ Sophisticated, lighter blue\n            backgroundColor: 'rgba(41, 128, 185, 0.06)', \/\/ Added: Gentle blue fill color\n            fill: false, \/\/ Added: Starts hidden, toggles dynamically\n            borderWidth: { 'day': 1.2, 'week': 0.6, 'month': 1.2, 'year': 1.0 }[currentTimeframe],\n\n            pointRadius: { '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\n            \/\/ This makes the graph go edge to edge 9h to 21h and 24h\n            min: currentTimeframe === 'day' ? new Date(getISO(activeDate) + 'T' + (is24hMode ? '00:00:00' : '09:00:00')).getTime() : undefined,\n            max: currentTimeframe === 'day' ? new Date(getISO(activeDate) + 'T' + (is24hMode ? '24:00:00' : '21:00:00')).getTime() : undefined,\n\n            ticks: {\n   \/\/ Set stepSize to a solid 1 hour; autoSkip will handle crowding in 24h mode\n         \/\/ 1 hour * 60 min * 60 sec * 1000 ms\n              stepSize: currentTimeframe === 'day' ? (60 * 60 * 1000) : undefined,\n              \n\/\/ Added properties for resolution and scaling limits with 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            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\n          tooltip: {\n            enabled: isReadingMode,\n            backgroundColor: 'rgba(44, 62, 80, 0.9)',\n            \n            \/\/ 1. Hide the standalone Direction dot in 24h mode (we will bundle it below)\n            filter: function(tooltipItem) {\n              const isYear24 = currentTimeframe === 'year' && is24hMode;\n              if (isYear24 && tooltipItem.datasetIndex === 0) return false;\n              return true;\n            },\n            \n            callbacks: {\n              \/\/ 1. Title shows purely the Date in 24h mode to avoid confusing top\/bottom time splits\n              title: (items) => {\n                const isYear24 = currentTimeframe === 'year' && is24hMode;\n                const targetDate = new Date(items[0].parsed.x);\n                \n                if (isYear24) {\n                  return targetDate.toLocaleString('en-US', { \n                    weekday: 'short', month: 'short', day: 'numeric', \n                    timeZone: 'Atlantic\/Madeira' \n                  });\n                }\n\n                return targetDate.toLocaleString('en-US', { \n                  weekday: 'short', month: 'short', day: 'numeric', \n                  hour: '2-digit', minute: '2-digit', hour12: false, \n                  timeZone: 'Atlantic\/Madeira' \n                });\n              },\n              \n              \/\/ 2. Both Wind and Gust times appear directly inline with their respective readings\n              label: (ctx) => {\n                const raw = ctx.raw.raw;\n                const isYear24 = currentTimeframe === 'year' && is24hMode;\n                const formatDir = (deg) => deg != null ? `${Math.round(deg)}\u00b0 ${getCompass(deg)}` : '--';\n\n                const getTimeStr = (timeStr) => {\n                  if (!timeStr) return '';\n                  const exactD = new Date(timeStr.replace(\/-\/g, \"\/\"));\n                  return exactD.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false, timeZone: 'Atlantic\/Madeira' });\n                };\n\n                \/\/ Dataset 0: Direction\n                if (ctx.datasetIndex === 0) {\n                  return `Dir: ${formatDir(ctx.parsed.y)}`;\n                }\n\n                 \/\/ Dataset 1: Gust Point\n                if (ctx.datasetIndex === 1) {\n                  if (isYear24 && raw) {\n                    const gTime = getTimeStr(raw.gTime);\n                    const timeLabel = gTime ? ` at ${gTime}` : '';\n                    return [\n                      `Gust: ${ctx.parsed.y.toFixed(1)} km\/h${timeLabel}`,\n                      `G_Dir: ${formatDir(raw.gDir)}`\n                    ];\n                  }\n                  return `Gusts: ${ctx.parsed.y.toFixed(1)} km\/h`;\n                }\n\n                \/\/ Dataset 2: Wind Point\n                if (ctx.datasetIndex === 2) {\n                  if (isYear24 && raw) {\n                    const wTime = getTimeStr(raw.wTime);\n                    const timeLabel = wTime ? ` at ${wTime}` : '';\n                    return [\n                      `Wind: ${ctx.parsed.y.toFixed(1)} km\/h${timeLabel}`,\n                      `W_Dir: ${formatDir(raw.wDir)}`\n                    ];\n                  }\n                  return `Wind: ${ctx.parsed.y.toFixed(1)} km\/h`;\n                }\n              },\n\n              \/\/ 3. Daily Average displayed at the bottom of the Tooltip\n              footer: (items) => {\n                const isYear24 = currentTimeframe === 'year' && is24hMode;\n                const raw = items[0]?.raw?.raw;\n                if (!raw) return '';\n\n                if (isYear24 && raw.avg24 != null) {\n                  return `\\nDaily Avg: ${raw.avg24} km\/h`;\n                } else if (currentTimeframe === 'year' && !is24hMode && raw.avgFly != null) {\n                  return `\\nDaytime Avg (9-21h): ${raw.avgFly} km\/h`;\n                }\n                return '';\n              }\n            }\n          }\n        }\n      },\n      plugins: [solarBackgroundPlugin, idealDirectionPlugin] \/\/ Flyable direction Plugin inside the Chart object and sun rise and set bg color plugin\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\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\/\/ Legend button Cycle Logic with dynamic opacity & axis mirroring\n  window.cycleChartViews = function() {\n    currentCycleState = (currentCycleState + 1) % 7;\n    if (!windChart) return;\n\n    \/\/ Helper to set chart dataset visibility and legend opacity\n    const setVis = (index, legendId, isVisible) => {\n      windChart.setDatasetVisibility(index, isVisible);\n      document.getElementById(legendId).style.opacity = isVisible ? '1' : '0.3';\n    };\n\n    \/\/ Chart dataset structure: 0 = Direction, 1 = Gusts, 2 = Wind\n    if (currentCycleState === 0) { \n      \/\/ Default: All visible\n      setVis(2, 'leg-wind', true); setVis(1, 'leg-gust', true); setVis(0, 'leg-dir', true);\n    } else if (currentCycleState === 1) { \n      \/\/ 1st click: Gusts + Direction\n      setVis(2, 'leg-wind', false); setVis(1, 'leg-gust', true); setVis(0, 'leg-dir', true);\n    } else if (currentCycleState === 2) { \n      \/\/ 2nd click: Wind + Direction\n      setVis(2, 'leg-wind', true); setVis(1, 'leg-gust', false); setVis(0, 'leg-dir', true);\n    } else if (currentCycleState === 3) { \n      \/\/ 3rd click: Just Direction\n      setVis(2, 'leg-wind', false); setVis(1, 'leg-gust', false); setVis(0, 'leg-dir', true);\n    } else if (currentCycleState === 4) { \n      \/\/ 4th click: Just Wind\n      setVis(2, 'leg-wind', true); setVis(1, 'leg-gust', false); setVis(0, 'leg-dir', false);\n    } else if (currentCycleState === 5) {\n      \/\/ 5th click: Just Gusts\n      setVis(2, 'leg-wind', false); setVis(1, 'leg-gust', true); setVis(0, 'leg-dir', false);\n    } else if (currentCycleState === 6) { \n      \/\/ 6th click: Wind + Gusts\n      setVis(2, 'leg-wind', true); setVis(1, 'leg-gust', true); setVis(0, 'leg-dir', false);\n    }\n\n    \/\/ Evaluate active views\n    const isWindActive = windChart.isDatasetVisible(2);\n    const isGustActive = windChart.isDatasetVisible(1);\n    const isDirActive = windChart.isDatasetVisible(0);\n    const isSpeedActive = isWindActive || isGustActive;\n\n\/\/ --- NEW: DYNAMIC DIRECTION SWITCHING ---\n    if (windChart.data.datasets[0]) {\n      windChart.data.datasets[0].data.forEach(point => {\n        if (!point.raw) return;\n        if (isGustActive && !isWindActive) {\n          \/\/ If ONLY Gusts are viewed, move the dots to the Gust Direction\n          point.y = point.raw.gDir !== undefined ? point.raw.gDir : point.raw.d;\n        } else {\n          \/\/ Otherwise, default to Wind Direction\n          point.y = point.raw.wDir !== undefined ? point.raw.wDir : point.raw.d;\n        }\n      });\n    }\n    \/\/ ----------------------------------------\n\n   \/\/ 1. DYNAMIC OPACITY BOOSTING\n    \/\/ Wind opacity logic\n    if (isWindActive && !isGustActive) {\n\/\/ Wind only (Strong boost + Background Fill)\n      windChart.data.datasets[2].borderColor = 'rgba(41, 128, 185, 0.60)';\nwindChart.data.datasets[2].fill = true;\n    } else if (isWindActive && isGustActive && !isDirActive) {\n \/\/ Wind + Gusts together without Direction (Slight boost)\n      windChart.data.datasets[2].borderColor = 'rgba(41, 128, 185, 0.45)';\nwindChart.data.datasets[2].fill = false;\n    } else {\n      \/\/ Default standard view\n      windChart.data.datasets[2].borderColor = 'rgba(41, 128, 185, 0.30)';\nwindChart.data.datasets[2].fill = false;\n    }\n\n\n    \/\/ Gust opacity logic\n    if (isGustActive && !isWindActive) {\n      \/\/ Gusts only (Strong boost)\n      windChart.data.datasets[1].borderColor = 'rgba(230, 126, 34, 0.60)';\n      windChart.data.datasets[1].backgroundColor = 'rgba(230, 126, 34, 0.10)';\n    } else if (isWindActive && isGustActive && !isDirActive) {\n \/\/ Wind + Gusts together without Direction (Slight boost)\n      windChart.data.datasets[1].borderColor = 'rgba(230, 126, 34, 0.40)';\n      windChart.data.datasets[1].backgroundColor = 'rgba(230, 126, 34, 0.08)';\n    } else {\n      \/\/ Default standard view\n      windChart.data.datasets[1].borderColor = 'rgba(230, 126, 34, 0.10)';\n      windChart.data.datasets[1].backgroundColor = 'rgba(230, 126, 34, 0.05)';\n    }\n\n\n    \/\/ 2. DYNAMIC AXIS MIRRORING\n    const scales = windChart.options.scales;\n\n\/\/ Define mirrored scales on demand if they do not exist yet\n    if (!scales.ySpeedRight) {\n      scales.ySpeedRight = {\n        position: 'right',\n        ticks: { font: { size: 10 }, color: '#888' },\n        grid: { display: false },\n        afterBuildTicks: (axis) => {\n          const mainAxis = axis.chart.scales.ySpeed;\n          if (mainAxis && mainAxis.ticks) {\n            axis.min = mainAxis.min;\n            axis.max = mainAxis.max;\n            axis.ticks = [...mainAxis.ticks]; \n          }\n        }\n      };\n    }\n    if (!scales.yDirLeft) {\n      scales.yDirLeft = {\n        position: 'left',\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\n\/\/ Toggle mirror states based on view selection\n    if (isSpeedActive && !isDirActive) {\n\/\/ Speed active only -> Display speed on left AND right, turn off direction\n      scales.ySpeed.display = true;\n      scales.ySpeedRight.display = true;\n      if (scales.yDir) scales.yDir.display = false;\n      scales.yDirLeft.display = false;\n    } else if (isDirActive && !isSpeedActive) {\n\/\/ Direction active only -> Display direction on right AND left, turn off speed\n      scales.yDir.display = true;\n      scales.yDirLeft.display = true;\n      if (scales.ySpeed) scales.ySpeed.display = false;\n      scales.ySpeedRight.display = false;\n    } else {\n      \/\/ Standard view -> Speed on left, Direction on right, mirrors turned off\n      if (scales.ySpeed) scales.ySpeed.display = isSpeedActive;\n      if (scales.yDir) scales.yDir.display = isDirActive;\n      scales.ySpeedRight.display = false;\n      scales.yDirLeft.display = false;\n    }\n\n    \/\/ Instant update without re-triggering entrance animation\n    windChart.update('none'); \n  };\n\n\n})();\n<\/script>\n<\/template>\n\n\n\n<!-- Day plus Precious selection, 7 days, 30 days and year Wind 9h to 21h -->\n<!-- added 24h wind and gust max on year view -->\n<!-- worked great fine tuned below -->\n<template>\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\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      <\/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\n\n    <\/div>\n    \n    <div style=\"font-size: 11px; color: #999; font-weight: 600; letter-spacing: 0.5px; text-align: right; padding-top: 5px; cursor: pointer; transition: color 0.2s;\" id=\"sc-station-label\" onclick=\"toggleAvgMode()\" title=\"Click to toggle Average \/ Max Mode\">Maiata wind 9h to 21h<\/div>\n  <\/div>\n\n  <div class=\"sc-legend\" onclick=\"cycleChartViews()\" style=\"cursor: pointer; user-select: none; 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; transition: 0.3s;\">\n  <div id=\"leg-wind\" style=\"display: flex; align-items: center; gap: 4px; transition: opacity 0.3s;\"><span style=\"width: 8px; height: 3px; background: rgba(41, 128, 185, 0.7);\"><\/span> Wind<\/div>\n  <div id=\"leg-gust\" style=\"display: flex; align-items: center; gap: 4px; transition: opacity 0.3s;\"><span style=\"width: 8px; height: 3px; background: rgba(230, 126, 34, 0.4);\"><\/span> Gusts<\/div>\n  <div id=\"leg-dir\" style=\"display: flex; align-items: center; gap: 4px; transition: opacity 0.3s;\"><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\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  \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<!-- Added SunCalc for historical day\/night bg color accuracy -->\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/suncalc\/1.9.0\/suncalc.min.js\"><\/script>\n\n<script>\n(function() {\n  let windChart;\n  let isReadingMode = true; \/\/ Added state for mode toggle\n  let currentTimeframe = 'day';\n  let activeDate = new Date();\n\n  \/\/ New 24h wind button\n  let is24hMode = false;\n  let isAvgMode = false; \/\/ State for average wind mode\n  let currentCycleState = 0; \/\/ Moved up to allow resetting when timeframes change\n\n  \/\/ --- TOGGLE AVERAGE MODE FUNCTION ---\n  window.toggleAvgMode = function() {\n    if (currentTimeframe !== 'year') return; \/\/ Only allow toggling in Year view\n    isAvgMode = !isAvgMode;\n    loadWindData('year');\n  };\n\n  window.toggle24h = function() {\n    is24hMode = !is24hMode;\n    loadWindData(currentTimeframe);\n  };\n\n  const stationPath = 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/maiata\/';\n  const stationStartDate = new Date(\"2026-02-07\"); \/\/ MAIATA - Change this date based on weather station deployment\n\n  \/\/ --- CONFIG IDEAL FLYABLE Direction ---\n  const showIdealCorridor = true; \/\/ set false to hide \n  const idealMin = 333;          \/\/ NorthWest\n  const idealMax = 55;          \/\/ NorthEast\n  const corridorColor = 'rgba(40, 200, 40, 0.05)'; \n\n\/\/ Flyable directions Plugin\n    const idealDirectionPlugin = {\n    id: 'idealDirection',\n    beforeDraw: (chart) => {\n\/\/ Added: !chart.isDatasetVisible(0) to hide when direction is toggled off\n      if (!showIdealCorridor || !chart.isDatasetVisible(0)) return;\n\n      \n      const { ctx, chartArea: { left, right }, scales: { x, yDir } } = chart;\n      if (!yDir || !x) return; \/\/ Safety check    \n\n      let startX = left;\n      let endX = right;\n\n      \/\/ Restrict corridor to daytime only in 'day' view\n      if (currentTimeframe === 'day') {\n        const lat = 32.77;\n        const lng = -16.83;\n        const times = SunCalc.getTimes(activeDate, lat, lng);\n\n        const alignToAxis = (dateObj) => {\n          const parts = new Intl.DateTimeFormat('en-US', {\n            timeZone: 'Atlantic\/Madeira',\n            hour: 'numeric', minute: 'numeric', second: 'numeric', hour12: false\n          }).formatToParts(dateObj);\n          \n          let h = 0, m = 0, s = 0;\n          parts.forEach(p => {\n            if (p.type === 'hour') h = parseInt(p.value);\n            if (p.type === 'minute') m = parseInt(p.value);\n            if (p.type === 'second') s = parseInt(p.value);\n          });\n          \n          const localDate = new Date(activeDate);\n          localDate.setHours(h === 24 ? 0 : h, m, s, 0);\n          return localDate.getTime();\n        };\n\n        const sunrise = alignToAxis(times.sunrise);\n        const sunset = alignToAxis(times.sunset);\n\n        \/\/ Convert times to pixel coordinates and clamp to the visible chart area\n        startX = Math.max(left, x.getPixelForValue(sunrise));\n        endX = Math.min(right, x.getPixelForValue(sunset));\n      }\n\n      ctx.save();\n      ctx.fillStyle = corridorColor;\n\n      const drawBox = (min, max) => {\n        const yTop = yDir.getPixelForValue(max);\n        const yBottom = yDir.getPixelForValue(min);\n        \n        \/\/ Only draw the box if the daytime area is currently visible on the screen\n        if (startX < endX) {\n          ctx.fillRect(startX, yTop, endX - startX, yBottom - yTop);\n        }\n      };\n\n      if (idealMin > idealMax) {\n        drawBox(idealMin, 360);\n        drawBox(0, idealMax);\n      } else {\n        drawBox(idealMin, idealMax);\n      }\n      ctx.restore();\n    }\n  };\n\n\/\/ NEW: Day\/Night Background Plugin for Historical Accuracy start\nconst solarBackgroundPlugin = {\n  id: 'solarBackground',\n  beforeDraw: (chart) => {\n    if (currentTimeframe !== 'day') return;\n\n    const { ctx, chartArea: { top, bottom, left, right }, scales: { x } } = chart;\n    if (!x) return;\n\n    \/\/ Coordinates for Madeira Island\n    const lat = 32.77;\n    const lng = -16.83;\n\n    \/\/ Get the absolute UTC solar times for the date\n    const times = SunCalc.getTimes(activeDate, lat, lng);\n\n    \/\/ Timezone alignment: Extract the exact wall-clock hour\/minute in Madeira, \n    \/\/ and apply it to our local chart axis to prevent timezone shifting bugs.\n    const alignToAxis = (dateObj) => {\n      const parts = new Intl.DateTimeFormat('en-US', {\n        timeZone: 'Atlantic\/Madeira',\n        hour: 'numeric', minute: 'numeric', second: 'numeric', hour12: false\n      }).formatToParts(dateObj);\n      \n      let h = 0, m = 0, s = 0;\n      parts.forEach(p => {\n        if (p.type === 'hour') h = parseInt(p.value);\n        if (p.type === 'minute') m = parseInt(p.value);\n        if (p.type === 'second') s = parseInt(p.value);\n      });\n      \n      const localDate = new Date(activeDate);\n      localDate.setHours(h === 24 ? 0 : h, m, s, 0);\n      return localDate.getTime();\n    };\n\n    const sunrise = alignToAxis(times.sunrise);\n    const sunset = alignToAxis(times.sunset);\n\n    const drawNight = (startTime, endTime) => {\n      const startPx = Math.max(left, x.getPixelForValue(startTime));\n      const endPx = Math.min(right, x.getPixelForValue(endTime));\n\n      \/\/ Only draw if the night section is actually visible on the chart\n      if (startPx < endPx) {\n        ctx.save();\n        ctx.fillStyle = 'rgba(0, 0, 0, 0.09)'; \/\/ Gentle night shade\n        ctx.fillRect(startPx, top, endPx - startPx, bottom - top);\n        ctx.restore();\n      }\n    };\n\n    \/\/ Set boundaries for the current day locally\n    const startOfDay = new Date(activeDate);\n    startOfDay.setHours(0, 0, 0, 0);\n    const endOfDay = new Date(activeDate);\n    endOfDay.setHours(23, 59, 59, 999);\n\n    \/\/ Draw the night blocks\n    drawNight(startOfDay.getTime(), sunrise);\n    drawNight(sunset, endOfDay.getTime());\n  }\n};\n\/\/ end of day night background plugin\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 \/\/ Reset legend visual state when changing timeframes\n    currentCycleState = 0;\n    document.getElementById('leg-wind').style.opacity = '1';\n    document.getElementById('leg-gust').style.opacity = '1';\n    document.getElementById('leg-dir').style.opacity = '1';\n \n        \/\/ Navigation & 24h wind logic\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    const legendEl = document.querySelector('.sc-legend');\n\n    \/\/ Select the date-specific elements so we can manage them independently\n    const prevBtn = document.getElementById('sc-wind-prev-btn');\n    const nextBtn = document.getElementById('sc-wind-next-btn');\n    const displayDate = document.getElementById('sc-wind-display-date');\n\n    if (timeframe === 'day') {\n      dateNav.style.display = 'flex';\n      prevBtn.style.display = '';       \/\/ Reverts to your CSS default\n      nextBtn.style.display = '';\n      displayDate.style.display = '';\n      toggleBtn.style.display = 'inline-block';\n    } else if (timeframe === 'year') {\n      dateNav.style.display = 'flex';   \/\/ Keep the container visible for the toggle button\n      prevBtn.style.display = 'none';   \/\/ Hide the day-navigation arrows and text\n      nextBtn.style.display = 'none';\n      displayDate.style.display = 'none';\n      toggleBtn.style.display = 'inline-block';\n    } else {\n      dateNav.style.display = 'none';\n      toggleBtn.style.display = 'none';\n      is24hMode = false; \n    }\n\n    if (toggleBtn) {\n      if (timeframe === 'year') {\n        toggleBtn.innerText = is24hMode ? \"Back to 12-18h\" : \"Show 24h\";\n      } else {\n        toggleBtn.innerText = is24hMode ? \"Back to 9-21h\" : \"Show 24h\";\n      }\n    }\n\n    \/\/ Label Update & Legend Locking Logic\n    if (timeframe === 'year') {\n      stationLabel.style.textDecoration = 'underline';\n      stationLabel.style.color = isAvgMode ? '#2980b9' : '#3b5875'; \/\/ Highlights blue when Avg mode active\n       const legendEl = document.querySelector('.sc-legend');\n\n      if (isAvgMode) {\n        stationLabel.innerText = is24hMode ? \"Maiata average Wind and direction 24h\" : \"Maiata average Wind and direction 9h to 21h\";\n\/\/ Lock legend clicks in avg view\n if (legendEl) {\n          legendEl.style.pointerEvents = 'none';\n          legendEl.style.cursor = 'default';\n        }\n\n        document.getElementById('leg-gust').style.opacity = '0.3';\n      } else {\n        stationLabel.innerText = is24hMode ? \"Maiata max Wind with precise time & direction 24h\" : \"Maiata max Wind with Avg direction 12h to 18h\";\n\/\/ Unlock legend\n if (legendEl) {\n          legendEl.style.pointerEvents = 'auto';\n          legendEl.style.cursor = 'pointer';\n        }\n\n      \n        document.getElementById('leg-gust').style.opacity = '1';\n      }\n    } else {\n      isAvgMode = false; \/\/ Reset if leaving year view\n      stationLabel.style.textDecoration = 'none';\n      stationLabel.style.color = '#999';\n      legendEl.style.pointerEvents = 'auto';\n      \n      if (is24hMode) {\n        stationLabel.innerText = \"Maiata wind 24h\";\n      } else {\n        stationLabel.innerText = \"Maiata wind 9h to 21h\";\n      }\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   \/\/ New block with gap awareness\n    try {\n      let rawData = [];\n\n      \/\/ OPTIMIZED CACHE LOGIC:\n      const now = new Date();\n      const isCurrentMonth = activeDate.getMonth() === now.getMonth() && activeDate.getFullYear() === now.getFullYear();\n      \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        filtered.forEach((d) => {\n          rawData.push({\n            t: new Date(d.time).getTime(),\n            s: d.windSpeed?.kph ?? 0,\n            g: d.windGust?.kph ?? 0,\n            d: d.winddir ?? null\n          });\n        });\n      }\n      else 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 => rawData.push({ t: s.t, s: s.s, g: s.g, d: s.d }));\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          if (is24hMode) {\n            rawData = days\n              .filter(d => d.t && d.t.includes(\"12:00:00\") && (isAvgMode ? d.wind_avg_24h !== undefined : d.peak_24h_wind_time)) \n              .map(d => ({\n                t: new Date(d.t.replace(\/-\/g, \"\/\")).getTime(), \n                \/\/ Uses your PHP engine's wind_avg_24h when isAvgMode is active\n                s: isAvgMode ? d.wind_avg_24h : d.peak_24h_wind, \n                g: isAvgMode ? null : d.peak_24h_gust, \n                d: isAvgMode ? d.peak_24h_wind_dir : d.peak_24h_wind_dir, \n                wTime: d.peak_24h_wind_time,\n                gTime: d.peak_24h_gust_time,\n                wDir: d.peak_24h_wind_dir,\n                gDir: d.peak_24h_gust_dir,\n                avg24: d.wind_avg_24h\n              }));\n          } else {\n            rawData = days\n              .filter(d => d.t && d.t.includes(\"12:00:00\")) \n              .map(d => ({ \n                t: new Date(d.t.replace(\/-\/g, \"\/\")).getTime(), \n                \/\/ Uses your PHP engine's wind_avg (9-21h flying hours average) when isAvgMode is active\n                s: isAvgMode ? d.wind_avg : d.wind, \n                g: isAvgMode ? null : d.gust, \n                d: d.dir \n              }));\n          }\n        } else {\n          days.forEach(day => {\n            const samples = day.wind?.samples || [];\n            samples.forEach(s => rawData.push({ t: s.t, s: s.s, g: s.g, d: s.d }));\n          });\n        }\n      }\n\n      \/\/ --- UNIVERSAL GAP FILTER ---\n      let gapThreshold;\n      if (timeframe === 'day') gapThreshold = 10 * 60 * 1000; \/\/ 10 minutes\n      else if (timeframe === 'year') gapThreshold = 25 * 60 * 60 * 1000; \/\/ 25 hours (allows daily 12:00 readings to connect, but breaks if a full day is skipped)\n      else gapThreshold = 2 * 60 * 60 * 1000; \/\/ 2 hours for week\/month (breaks the line overnight)\n\n      let processedData = [];\n      rawData.forEach((point, i) => {\n        if (i > 0) {\n          const prevPoint = rawData[i - 1];\n          \/\/ If the time gap between points exceeds the threshold, inject breaks\n          if (point.t - prevPoint.t > gapThreshold) {\n            processedData.push({ t: prevPoint.t + 1000, s: null, g: null, d: null });\n            processedData.push({ t: point.t - 1000, s: null, g: null, d: null });\n          }\n        }\n        processedData.push(point);\n      });\n\n      if (status) status.innerText = \"\";\n      renderChart(processedData);\n      \n    } catch (e) {\n      console.error(e);\n      if (status) status.innerText = \"Data Pending...\";\n    }\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, raw: d})), \/\/ Added raw: 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            hidden: isAvgMode, \/\/ Hides gusts automatically when in Avg Mode\n            data: data.map(d => ({x: d.t, y: d.g, raw: d})), \/\/ Added raw: d\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,\n            borderWidth: { 'day': 1.2, 'week': 0.7, 'month': 0.2, 'year': 1.1 }[currentTimeframe],\n            pointRadius: { '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, raw: d})), \/\/ Added raw: d\n            borderColor: 'rgba(41, 128, 185, 0.4)', \/\/ Sophisticated, lighter blue\n            backgroundColor: 'rgba(41, 128, 185, 0.06)', \/\/ Added: Gentle blue fill color\n            fill: false, \/\/ Added: Starts hidden, toggles dynamically\n            borderWidth: { 'day': 1.2, 'week': 0.6, 'month': 1.2, 'year': 1.0 }[currentTimeframe],\n\n            pointRadius: { '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\n            \/\/ This makes the graph go edge to edge 9h to 21h and 24h\n            min: currentTimeframe === 'day' ? new Date(getISO(activeDate) + 'T' + (is24hMode ? '00:00:00' : '09:00:00')).getTime() : undefined,\n            max: currentTimeframe === 'day' ? new Date(getISO(activeDate) + 'T' + (is24hMode ? '24:00:00' : '21:00:00')).getTime() : undefined,\n\n            ticks: {\n   \/\/ Set stepSize to a solid 1 hour; autoSkip will handle crowding in 24h mode\n         \/\/ 1 hour * 60 min * 60 sec * 1000 ms\n              stepSize: currentTimeframe === 'day' ? (60 * 60 * 1000) : undefined,\n              \n\/\/ Added properties for resolution and scaling limits with 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            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\n          tooltip: {\n            enabled: isReadingMode,\n            backgroundColor: 'rgba(44, 62, 80, 0.9)',\n            \n            \/\/ 1. Hide the standalone Direction dot in 24h mode (we will bundle it below)\n            filter: function(tooltipItem) {\n              const isYear24 = currentTimeframe === 'year' && is24hMode;\n              if (isYear24 && tooltipItem.datasetIndex === 0) return false;\n              return true;\n            },\n            \n            callbacks: {\n              \/\/ 1. Title shows purely the Date in 24h mode to avoid confusing top\/bottom time splits\n              title: (items) => {\n                const isYear24 = currentTimeframe === 'year' && is24hMode;\n                const targetDate = new Date(items[0].parsed.x);\n                \n                if (isYear24) {\n                  return targetDate.toLocaleString('en-US', { \n                    weekday: 'short', month: 'short', day: 'numeric', \n                    timeZone: 'Atlantic\/Madeira' \n                  });\n                }\n\n                return targetDate.toLocaleString('en-US', { \n                  weekday: 'short', month: 'short', day: 'numeric', \n                  hour: '2-digit', minute: '2-digit', hour12: false, \n                  timeZone: 'Atlantic\/Madeira' \n                });\n              },\n              \n              \/\/ 2. Both Wind and Gust times appear directly inline with their respective readings\n              label: (ctx) => {\n                const raw = ctx.raw.raw;\n                const isYear24 = currentTimeframe === 'year' && is24hMode;\n                const formatDir = (deg) => deg != null ? `${Math.round(deg)}\u00b0 ${getCompass(deg)}` : '--';\n\n                const getTimeStr = (timeStr) => {\n                  if (!timeStr) return '';\n                  const exactD = new Date(timeStr.replace(\/-\/g, \"\/\"));\n                  return exactD.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false, timeZone: 'Atlantic\/Madeira' });\n                };\n\n                \/\/ Dataset 0: Direction\n                if (ctx.datasetIndex === 0) {\n                  return `Dir: ${formatDir(ctx.parsed.y)}`;\n                }\n\n                 \/\/ Dataset 1: Gust Point\n                if (ctx.datasetIndex === 1) {\n                  if (isYear24 && raw) {\n                    const gTime = getTimeStr(raw.gTime);\n                    const timeLabel = gTime ? ` at ${gTime}` : '';\n                    return [\n                      `Gust: ${ctx.parsed.y.toFixed(1)} km\/h${timeLabel}`,\n                      `G_Dir: ${formatDir(raw.gDir)}`\n                    ];\n                  }\n                  return `Gusts: ${ctx.parsed.y.toFixed(1)} km\/h`;\n                }\n\n                \/\/ Dataset 2: Wind Point\n                if (ctx.datasetIndex === 2) {\n                  if (isYear24 && raw && !isAvgMode) {\n                    const wTime = getTimeStr(raw.wTime);\n                    const timeLabel = wTime ? ` at ${wTime}` : '';\n                    return [\n                      `Wind: ${ctx.parsed.y.toFixed(1)} km\/h${timeLabel}`,\n                      `W_Dir: ${formatDir(raw.wDir)}`\n                    ];\n                  }\n                  return isAvgMode ? `Avg Wind: ${ctx.parsed.y.toFixed(1)} km\/h` : `Wind: ${ctx.parsed.y.toFixed(1)} km\/h`;\n                }\n              },\n\n              \/\/ 3. Daily Average displayed at the bottom of the Tooltip\n              footer: (items) => {\n                if (isAvgMode) return ''; \/\/ Hide footer duplicate in average mode\n                \n                const isYear24 = currentTimeframe === 'year' && is24hMode;\n                const raw = items[0]?.raw?.raw;\n                if (!raw) return '';\n\n                if (isYear24 && raw.avg24 != null) {\n                  return `\\nDaily Avg: ${raw.avg24} km\/h`;\n                } else if (currentTimeframe === 'year' && !is24hMode && raw.avgFly != null) {\n                  return `\\nDaytime Avg (9-21h): ${raw.avgFly} km\/h`;\n                }\n                return '';\n              }\n            }\n          }\n        }\n      },\n      plugins: [solarBackgroundPlugin, idealDirectionPlugin] \/\/ Flyable direction Plugin inside the Chart object and sun rise and set bg color plugin\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\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\/\/ Legend button Cycle Logic with dynamic opacity & axis mirroring\n  window.cycleChartViews = function() {\n  if (isAvgMode) return; \/\/ <--- SAFETY SWITCH: Blocks clicks in Average mode\n\n  currentCycleState = (currentCycleState + 1) % 7;\n  if (!windChart) return;\n\n    \/\/ Helper to set chart dataset visibility and legend opacity\n    const setVis = (index, legendId, isVisible) => {\n      windChart.setDatasetVisibility(index, isVisible);\n      document.getElementById(legendId).style.opacity = isVisible ? '1' : '0.3';\n    };\n\n    \/\/ Chart dataset structure: 0 = Direction, 1 = Gusts, 2 = Wind\n    if (currentCycleState === 0) { \n      \/\/ Default: All visible\n      setVis(2, 'leg-wind', true); setVis(1, 'leg-gust', true); setVis(0, 'leg-dir', true);\n    } else if (currentCycleState === 1) { \n      \/\/ 1st click: Gusts + Direction\n      setVis(2, 'leg-wind', false); setVis(1, 'leg-gust', true); setVis(0, 'leg-dir', true);\n    } else if (currentCycleState === 2) { \n      \/\/ 2nd click: Wind + Direction\n      setVis(2, 'leg-wind', true); setVis(1, 'leg-gust', false); setVis(0, 'leg-dir', true);\n    } else if (currentCycleState === 3) { \n      \/\/ 3rd click: Just Direction\n      setVis(2, 'leg-wind', false); setVis(1, 'leg-gust', false); setVis(0, 'leg-dir', true);\n    } else if (currentCycleState === 4) { \n      \/\/ 4th click: Just Wind\n      setVis(2, 'leg-wind', true); setVis(1, 'leg-gust', false); setVis(0, 'leg-dir', false);\n    } else if (currentCycleState === 5) {\n      \/\/ 5th click: Just Gusts\n      setVis(2, 'leg-wind', false); setVis(1, 'leg-gust', true); setVis(0, 'leg-dir', false);\n    } else if (currentCycleState === 6) { \n      \/\/ 6th click: Wind + Gusts\n      setVis(2, 'leg-wind', true); setVis(1, 'leg-gust', true); setVis(0, 'leg-dir', false);\n    }\n\n    \/\/ Evaluate active views\n    const isWindActive = windChart.isDatasetVisible(2);\n    const isGustActive = windChart.isDatasetVisible(1);\n    const isDirActive = windChart.isDatasetVisible(0);\n    const isSpeedActive = isWindActive || isGustActive;\n\n\/\/ --- NEW: DYNAMIC DIRECTION SWITCHING ---\n    if (windChart.data.datasets[0]) {\n      windChart.data.datasets[0].data.forEach(point => {\n        if (!point.raw) return;\n        if (isGustActive && !isWindActive) {\n          \/\/ If ONLY Gusts are viewed, move the dots to the Gust Direction\n          point.y = point.raw.gDir !== undefined ? point.raw.gDir : point.raw.d;\n        } else {\n          \/\/ Otherwise, default to Wind Direction\n          point.y = point.raw.wDir !== undefined ? point.raw.wDir : point.raw.d;\n        }\n      });\n    }\n    \/\/ ----------------------------------------\n\n   \/\/ 1. DYNAMIC OPACITY BOOSTING\n    \/\/ Wind opacity logic\n    if (isWindActive && !isGustActive) {\n\/\/ Wind only (Strong boost + Background Fill)\n      windChart.data.datasets[2].borderColor = 'rgba(41, 128, 185, 0.60)';\nwindChart.data.datasets[2].fill = true;\n    } else if (isWindActive && isGustActive && !isDirActive) {\n \/\/ Wind + Gusts together without Direction (Slight boost)\n      windChart.data.datasets[2].borderColor = 'rgba(41, 128, 185, 0.45)';\nwindChart.data.datasets[2].fill = false;\n    } else {\n      \/\/ Default standard view\n      windChart.data.datasets[2].borderColor = 'rgba(41, 128, 185, 0.30)';\nwindChart.data.datasets[2].fill = false;\n    }\n\n\n    \/\/ Gust opacity logic\n    if (isGustActive && !isWindActive) {\n      \/\/ Gusts only (Strong boost)\n      windChart.data.datasets[1].borderColor = 'rgba(230, 126, 34, 0.60)';\n      windChart.data.datasets[1].backgroundColor = 'rgba(230, 126, 34, 0.10)';\n    } else if (isWindActive && isGustActive && !isDirActive) {\n \/\/ Wind + Gusts together without Direction (Slight boost)\n      windChart.data.datasets[1].borderColor = 'rgba(230, 126, 34, 0.40)';\n      windChart.data.datasets[1].backgroundColor = 'rgba(230, 126, 34, 0.08)';\n    } else {\n      \/\/ Default standard view\n      windChart.data.datasets[1].borderColor = 'rgba(230, 126, 34, 0.10)';\n      windChart.data.datasets[1].backgroundColor = 'rgba(230, 126, 34, 0.05)';\n    }\n\n\n    \/\/ 2. DYNAMIC AXIS MIRRORING\n    const scales = windChart.options.scales;\n\n\/\/ Define mirrored scales on demand if they do not exist yet\n    if (!scales.ySpeedRight) {\n      scales.ySpeedRight = {\n        position: 'right',\n        ticks: { font: { size: 10 }, color: '#888' },\n        grid: { display: false },\n        afterBuildTicks: (axis) => {\n          const mainAxis = axis.chart.scales.ySpeed;\n          if (mainAxis && mainAxis.ticks) {\n            axis.min = mainAxis.min;\n            axis.max = mainAxis.max;\n            axis.ticks = [...mainAxis.ticks]; \n          }\n        }\n      };\n    }\n    if (!scales.yDirLeft) {\n      scales.yDirLeft = {\n        position: 'left',\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\n\/\/ Toggle mirror states based on view selection\n    if (isSpeedActive && !isDirActive) {\n\/\/ Speed active only -> Display speed on left AND right, turn off direction\n      scales.ySpeed.display = true;\n      scales.ySpeedRight.display = true;\n      if (scales.yDir) scales.yDir.display = false;\n      scales.yDirLeft.display = false;\n    } else if (isDirActive && !isSpeedActive) {\n\/\/ Direction active only -> Display direction on right AND left, turn off speed\n      scales.yDir.display = true;\n      scales.yDirLeft.display = true;\n      if (scales.ySpeed) scales.ySpeed.display = false;\n      scales.ySpeedRight.display = false;\n    } else {\n      \/\/ Standard view -> Speed on left, Direction on right, mirrors turned off\n      if (scales.ySpeed) scales.ySpeed.display = isSpeedActive;\n      if (scales.yDir) scales.yDir.display = isDirActive;\n      scales.ySpeedRight.display = false;\n      scales.yDirLeft.display = false;\n    }\n\n    \/\/ Instant update without re-triggering entrance animation\n    windChart.update('none'); \n  };\n\n\n})();\n<\/script>\n<\/template>\n\n\n\n<!-- Day plus Precious selection, 7 days, 30 days and year Wind 9h to 21h -->\n<!-- added 24h wind and gust max on year view -->\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\n            <div id=\"sc-wind-time-nav\" style=\"display: flex; flex-wrap: nowrap; white-space: nowrap; gap: 1px; 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      <\/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        \n <!-- NEW Year Controls Group optimized for single-line mobile layout -->\n        <div id=\"sc-year-controls\" style=\"display: none; gap: 3px; margin-left: 3px; align-items: center; flex-wrap: nowrap;\">\n          <button id=\"btn-mode-max\" onclick=\"setYearMode('max')\" class=\"nav-btn\">Max<\/button>\n          <button id=\"btn-mode-avg\" onclick=\"setYearMode('avg')\" class=\"nav-btn\">Avg<\/button>\n          <span style=\"width: 1px; height: 16px; background: #ccc; margin: 0 4px;\"><\/span>\n          <button id=\"btn-time-fly\" onclick=\"setYearTime('fly')\" class=\"nav-btn\">12h-18h<\/button>\n          <button id=\"btn-time-24\" onclick=\"setYearTime('24h')\" class=\"nav-btn\">24h<\/button>\n        <\/div>\n      <\/div>\n\n\n\n    <\/div>\n    \n    <!-- Title is now completely static, acting as a clean readout legend -->\n    <div style=\"font-size: 11px; color: #777; font-weight: 600; white-space: nowrap; letter-spacing: 0.4px; text-align: right; padding-top: 5px;\" id=\"sc-station-label\">Maiata wind 9h to 21h<\/div>\n  <\/div>\n\n  <div class=\"sc-legend\" onclick=\"cycleChartViews()\" style=\"cursor: pointer; user-select: none; 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; transition: 0.3s;\">\n  <div id=\"leg-wind\" style=\"display: flex; align-items: center; gap: 4px; transition: opacity 0.3s;\"><span style=\"width: 8px; height: 3px; background: rgba(41, 128, 185, 0.7);\"><\/span> Wind<\/div>\n  <div id=\"leg-gust\" style=\"display: flex; align-items: center; gap: 4px; transition: opacity 0.3s;\"><span style=\"width: 8px; height: 3px; background: rgba(230, 126, 34, 0.4);\"><\/span> Gusts<\/div>\n  <div id=\"leg-dir\" style=\"display: flex; align-items: center; gap: 4px; transition: opacity 0.3s;\"><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\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  \n  .nav-btn { border: none; background: #eee; padding: 5px 11px; 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: 8px; white-space: nowrap; }\n    #sc-station-label { \ntext-align: left; white-space: nowrap;\n}\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<!-- Added SunCalc for historical day\/night bg color accuracy -->\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/suncalc\/1.9.0\/suncalc.min.js\"><\/script>\n\n<script>\n(function() {\n  let windChart;\n  let isReadingMode = true; \/\/ Added state for mode toggle\n  let currentTimeframe = 'day';\n  let activeDate = new Date();\n\n  \/\/ New 24h wind button\n  let is24hMode = false;\n  let isAvgMode = false; \/\/ State for average wind mode\n  let currentCycleState = 0; \/\/ Moved up to allow resetting when timeframes change\n\n  \/\/ --- NEW YEAR VIEW CONTROL FUNCTIONS ---\n  window.setYearMode = function(mode) {\n    if (currentTimeframe !== 'year') return;\n    isAvgMode = (mode === 'avg');\n    loadWindData('year');\n  };\n\n  window.setYearTime = function(time) {\n    if (currentTimeframe !== 'year') return;\n    is24hMode = (time === '24h');\n    loadWindData('year');\n  };\n\n  window.toggle24h = function() {\n    is24hMode = !is24hMode;\n    loadWindData(currentTimeframe);\n  };\n\n  const stationPath = 'https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/weather-stations\/maiata\/';\n  const stationStartDate = new Date(\"2026-02-07\"); \/\/ MAIATA - Change this date based on weather station deployment\n\n  \/\/ --- CONFIG IDEAL FLYABLE Direction ---\n  const showIdealCorridor = true; \/\/ set false to hide \n  const idealMin = 333;          \/\/ NorthWest\n  const idealMax = 55;          \/\/ NorthEast\n  const corridorColor = 'rgba(40, 200, 40, 0.05)'; \n\n\/\/ Flyable directions Plugin\n    const idealDirectionPlugin = {\n    id: 'idealDirection',\n    beforeDraw: (chart) => {\n\/\/ Added: !chart.isDatasetVisible(0) to hide when direction is toggled off\n      if (!showIdealCorridor || !chart.isDatasetVisible(0)) return;\n\n      \n      const { ctx, chartArea: { left, right }, scales: { x, yDir } } = chart;\n      if (!yDir || !x) return; \/\/ Safety check    \n\n      let startX = left;\n      let endX = right;\n\n      \/\/ Restrict corridor to daytime only in 'day' view\n      if (currentTimeframe === 'day') {\n        const lat = 32.77;\n        const lng = -16.83;\n        const times = SunCalc.getTimes(activeDate, lat, lng);\n\n        const alignToAxis = (dateObj) => {\n          const parts = new Intl.DateTimeFormat('en-US', {\n            timeZone: 'Atlantic\/Madeira',\n            hour: 'numeric', minute: 'numeric', second: 'numeric', hour12: false\n          }).formatToParts(dateObj);\n          \n          let h = 0, m = 0, s = 0;\n          parts.forEach(p => {\n            if (p.type === 'hour') h = parseInt(p.value);\n            if (p.type === 'minute') m = parseInt(p.value);\n            if (p.type === 'second') s = parseInt(p.value);\n          });\n          \n          const localDate = new Date(activeDate);\n          localDate.setHours(h === 24 ? 0 : h, m, s, 0);\n          return localDate.getTime();\n        };\n\n        const sunrise = alignToAxis(times.sunrise);\n        const sunset = alignToAxis(times.sunset);\n\n        \/\/ Convert times to pixel coordinates and clamp to the visible chart area\n        startX = Math.max(left, x.getPixelForValue(sunrise));\n        endX = Math.min(right, x.getPixelForValue(sunset));\n      }\n\n      ctx.save();\n      ctx.fillStyle = corridorColor;\n\n      const drawBox = (min, max) => {\n        const yTop = yDir.getPixelForValue(max);\n        const yBottom = yDir.getPixelForValue(min);\n        \n        \/\/ Only draw the box if the daytime area is currently visible on the screen\n        if (startX < endX) {\n          ctx.fillRect(startX, yTop, endX - startX, yBottom - yTop);\n        }\n      };\n\n      if (idealMin > idealMax) {\n        drawBox(idealMin, 360);\n        drawBox(0, idealMax);\n      } else {\n        drawBox(idealMin, idealMax);\n      }\n      ctx.restore();\n    }\n  };\n\n\/\/ NEW: Day\/Night Background Plugin for Historical Accuracy start\nconst solarBackgroundPlugin = {\n  id: 'solarBackground',\n  beforeDraw: (chart) => {\n    if (currentTimeframe !== 'day') return;\n\n    const { ctx, chartArea: { top, bottom, left, right }, scales: { x } } = chart;\n    if (!x) return;\n\n    \/\/ Coordinates for Madeira Island\n    const lat = 32.77;\n    const lng = -16.83;\n\n    \/\/ Get the absolute UTC solar times for the date\n    const times = SunCalc.getTimes(activeDate, lat, lng);\n\n    \/\/ Timezone alignment: Extract the exact wall-clock hour\/minute in Madeira, \n    \/\/ and apply it to our local chart axis to prevent timezone shifting bugs.\n    const alignToAxis = (dateObj) => {\n      const parts = new Intl.DateTimeFormat('en-US', {\n        timeZone: 'Atlantic\/Madeira',\n        hour: 'numeric', minute: 'numeric', second: 'numeric', hour12: false\n      }).formatToParts(dateObj);\n      \n      let h = 0, m = 0, s = 0;\n      parts.forEach(p => {\n        if (p.type === 'hour') h = parseInt(p.value);\n        if (p.type === 'minute') m = parseInt(p.value);\n        if (p.type === 'second') s = parseInt(p.value);\n      });\n      \n      const localDate = new Date(activeDate);\n      localDate.setHours(h === 24 ? 0 : h, m, s, 0);\n      return localDate.getTime();\n    };\n\n    const sunrise = alignToAxis(times.sunrise);\n    const sunset = alignToAxis(times.sunset);\n\n    const drawNight = (startTime, endTime) => {\n      const startPx = Math.max(left, x.getPixelForValue(startTime));\n      const endPx = Math.min(right, x.getPixelForValue(endTime));\n\n      \/\/ Only draw if the night section is actually visible on the chart\n      if (startPx < endPx) {\n        ctx.save();\n        ctx.fillStyle = 'rgba(0, 0, 0, 0.09)'; \/\/ Gentle night shade\n        ctx.fillRect(startPx, top, endPx - startPx, bottom - top);\n        ctx.restore();\n      }\n    };\n\n    \/\/ Set boundaries for the current day locally\n    const startOfDay = new Date(activeDate);\n    startOfDay.setHours(0, 0, 0, 0);\n    const endOfDay = new Date(activeDate);\n    endOfDay.setHours(23, 59, 59, 999);\n\n    \/\/ Draw the night blocks\n    drawNight(startOfDay.getTime(), sunrise);\n    drawNight(sunset, endOfDay.getTime());\n  }\n};\n\/\/ end of day night background plugin\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 \/\/ Reset legend visual state when changing timeframes\n    currentCycleState = 0;\n    document.getElementById('leg-wind').style.opacity = '1';\n    document.getElementById('leg-gust').style.opacity = '1';\n    document.getElementById('leg-dir').style.opacity = '1';\n \n        \/\/ Navigation & 24h wind logic\n    const dateNav = document.getElementById('sc-wind-date-nav');    \n    const toggleBtn = document.getElementById('sc-24h-toggle');\n    const yearControls = document.getElementById('sc-year-controls');\n    const stationLabel = document.getElementById('sc-station-label');\n    const legendEl = document.querySelector('.sc-legend');\n\n    \/\/ Select the date-specific elements so we can manage them independently\n    const prevBtn = document.getElementById('sc-wind-prev-btn');\n    const nextBtn = document.getElementById('sc-wind-next-btn');\n    const displayDate = document.getElementById('sc-wind-display-date');\n\n    if (timeframe === 'day') {\n      dateNav.style.display = 'flex';\n      prevBtn.style.display = '';       \/\/ Reverts to your CSS default\n      nextBtn.style.display = '';\n      displayDate.style.display = '';\n      toggleBtn.style.display = 'inline-block';\n      if (yearControls) yearControls.style.display = 'none';\n    } else if (timeframe === 'year') {\n      dateNav.style.display = 'flex';   \/\/ Keep the container visible for the toggle button\n      prevBtn.style.display = 'none';   \/\/ Hide the day-navigation arrows and text\n      nextBtn.style.display = 'none';\n      displayDate.style.display = 'none';\n      toggleBtn.style.display = 'none';\n      if (yearControls) yearControls.style.display = 'flex';\n    } else {\n      dateNav.style.display = 'none';\n      toggleBtn.style.display = 'none';\n      if (yearControls) yearControls.style.display = 'none';\n      is24hMode = false; \n    }\n\n    if (toggleBtn && timeframe === 'day') {\n      toggleBtn.innerText = is24hMode ? \"Back to 9-21h\" : \"Show 24h\";\n    }\n\n    \/\/ Dynamic active state toggling for the new Year view buttons\n    if (timeframe === 'year' && yearControls) {\n      document.getElementById('btn-mode-max').classList.toggle('active', !isAvgMode);\n      document.getElementById('btn-mode-avg').classList.toggle('active', isAvgMode);\n      document.getElementById('btn-time-fly').classList.toggle('active', !is24hMode);\n      document.getElementById('btn-time-24').classList.toggle('active', is24hMode);\n    }\n\n    \/\/ Static Title \/ Legend Updating Logic\n    if (timeframe === 'year') {\n      if (isAvgMode) {\n        stationLabel.innerText = is24hMode ? \"Maiata Average Wind & Direction (24h)\" : \"Maiata Average Wind & Direction (12h to 18h)\";\n        if (legendEl) {\n          legendEl.style.pointerEvents = 'none';\n          legendEl.style.cursor = 'default';\n        }\n        document.getElementById('leg-gust').style.opacity = '0.3';\n      } else {\n        stationLabel.innerText = is24hMode ? \"Maiata Max Wind with Time & Direction (24h)\" : \"Maiata Max Wind & Avg Direction (12h to 18h)\";\n        if (legendEl) {\n          legendEl.style.pointerEvents = 'auto';\n          legendEl.style.cursor = 'pointer';\n        }\n        document.getElementById('leg-gust').style.opacity = '1';\n      }\n    } else {\n      isAvgMode = false; \/\/ Reset if leaving year view\n      if (legendEl) {\n        legendEl.style.pointerEvents = 'auto';\n        legendEl.style.cursor = 'pointer';\n      }\n      \n      if (is24hMode) {\n        stationLabel.innerText = \"Maiata wind 24h\";\n      } else {\n        stationLabel.innerText = \"Maiata wind 9h to 21h\";\n      }\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   \/\/ New block with gap awareness\n    try {\n      let rawData = [];\n\n      \/\/ OPTIMIZED CACHE LOGIC:\n      const now = new Date();\n      const isCurrentMonth = activeDate.getMonth() === now.getMonth() && activeDate.getFullYear() === now.getFullYear();\n      \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        filtered.forEach((d) => {\n          rawData.push({\n            t: new Date(d.time).getTime(),\n            s: d.windSpeed?.kph ?? 0,\n            g: d.windGust?.kph ?? 0,\n            d: d.winddir ?? null\n          });\n        });\n      }\n      else 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 => rawData.push({ t: s.t, s: s.s, g: s.g, d: s.d }));\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          if (is24hMode) {\n            rawData = days\n              .filter(d => d.t && d.t.includes(\"12:00:00\") && (isAvgMode ? d.wind_avg_24h !== undefined : d.peak_24h_wind_time)) \n              .map(d => ({\n                t: new Date(d.t.replace(\/-\/g, \"\/\")).getTime(), \n                \/\/ Uses your PHP engine's wind_avg_24h when isAvgMode is active\n                s: isAvgMode ? d.wind_avg_24h : d.peak_24h_wind, \n                g: isAvgMode ? null : d.peak_24h_gust, \n                d: isAvgMode ? d.peak_24h_wind_dir : d.peak_24h_wind_dir, \n                wTime: d.peak_24h_wind_time,\n                gTime: d.peak_24h_gust_time,\n                wDir: d.peak_24h_wind_dir,\n                gDir: d.peak_24h_gust_dir,\n                avg24: d.wind_avg_24h\n              }));\n          } else {\n            rawData = days\n              .filter(d => d.t && d.t.includes(\"12:00:00\")) \n              .map(d => ({ \n                t: new Date(d.t.replace(\/-\/g, \"\/\")).getTime(), \n                \/\/ Uses your PHP engine's wind_avg (9-21h flying hours average) when isAvgMode is active\n                s: isAvgMode ? d.wind_avg : d.wind, \n                g: isAvgMode ? null : d.gust, \n                d: d.dir \n              }));\n          }\n        } else {\n          days.forEach(day => {\n            const samples = day.wind?.samples || [];\n            samples.forEach(s => rawData.push({ t: s.t, s: s.s, g: s.g, d: s.d }));\n          });\n        }\n      }\n\n      \/\/ --- UNIVERSAL GAP FILTER ---\n      let gapThreshold;\n      if (timeframe === 'day') gapThreshold = 10 * 60 * 1000; \/\/ 10 minutes\n      else if (timeframe === 'year') gapThreshold = 25 * 60 * 60 * 1000; \/\/ 25 hours (allows daily 12:00 readings to connect, but breaks if a full day is skipped)\n      else gapThreshold = 2 * 60 * 60 * 1000; \/\/ 2 hours for week\/month (breaks the line overnight)\n\n      let processedData = [];\n      rawData.forEach((point, i) => {\n        if (i > 0) {\n          const prevPoint = rawData[i - 1];\n          \/\/ If the time gap between points exceeds the threshold, inject breaks\n          if (point.t - prevPoint.t > gapThreshold) {\n            processedData.push({ t: prevPoint.t + 1000, s: null, g: null, d: null });\n            processedData.push({ t: point.t - 1000, s: null, g: null, d: null });\n          }\n        }\n        processedData.push(point);\n      });\n\n      if (status) status.innerText = \"\";\n      renderChart(processedData);\n      \n    } catch (e) {\n      console.error(e);\n      if (status) status.innerText = \"Data Pending...\";\n    }\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, raw: d})), \/\/ Added raw: 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            hidden: isAvgMode, \/\/ Hides gusts automatically when in Avg Mode\n            data: data.map(d => ({x: d.t, y: d.g, raw: d})), \/\/ Added raw: d\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,\n            borderWidth: { 'day': 1.2, 'week': 0.7, 'month': 0.2, 'year': 1.1 }[currentTimeframe],\n            pointRadius: { '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, raw: d})), \/\/ Added raw: d\n            borderColor: 'rgba(41, 128, 185, 0.4)', \/\/ Sophisticated, lighter blue\n            backgroundColor: 'rgba(41, 128, 185, 0.06)', \/\/ Added: Gentle blue fill color\n            fill: false, \/\/ Added: Starts hidden, toggles dynamically\n            borderWidth: { 'day': 1.2, 'week': 0.6, 'month': 1.2, 'year': 1.0 }[currentTimeframe],\n\n            pointRadius: { '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          \n          x: {\n            type: 'linear',\n\n            \/\/ This makes the graph go edge to edge 9h to 21h, 24h, and dynamically stretches the Year view\n            min: (() => {\n              if (currentTimeframe === 'day') {\n                return new Date(getISO(activeDate) + 'T' + (is24hMode ? '00:00:00' : '09:00:00')).getTime();\n              }\n              if (isYear && data.length > 0) {\n                const firstD = new Date(data[0].t);\n                return new Date(firstD.getFullYear(), firstD.getMonth(), 1, 0, 0, 0).getTime();\n              }\n              return undefined;\n            })(),\n            max: (() => {\n              if (currentTimeframe === 'day') {\n                return new Date(getISO(activeDate) + 'T' + (is24hMode ? '24:00:00' : '21:00:00')).getTime();\n              }\n              if (isYear && data.length > 0) {\n                const lastD = new Date(data[data.length - 1].t);\n                return new Date(lastD.getFullYear(), lastD.getMonth() + 1, 0, 23, 59, 59).getTime();\n              }\n              return undefined;\n            })(),\n\n            ticks: {\n              \/\/ Set stepSize to a solid 1 hour; autoSkip will handle crowding in 24h mode\n              \/\/ 1 hour * 60 min * 60 sec * 1000 ms\n              stepSize: currentTimeframe === 'day' ? (60 * 60 * 1000) : (24 * 60 * 60 * 1000),\n              \n              \/\/ Disable autoSkip on year view to keep all labels, and lock orientation to horizontal\n              autoSkip: !isYear,\n              maxRotation: 0,\n              minRotation: 0,\n              maxTicksLimit: 12,\n              includeBounds: true,\n\n              font: { size: 9, weight: '600' },\n              callback: (v, index, ticks) => {\n                const d = new Date(v);\n                if (currentTimeframe === 'day') return d.toLocaleString('en-US', {hour: 'numeric', hour12: false, timeZone: 'Atlantic\/Madeira'}) + ':00';\n                \n                if (isYear) {\n                  return d.toLocaleString('en-GB', {month: 'short', timeZone: 'Atlantic\/Madeira'});\n                }\n                \n                return d.toLocaleString('en-GB', {day: 'numeric', month: 'numeric', timeZone: 'Atlantic\/Madeira'});\n              }\n            },\n                        \/\/ Center the ticks precisely on the 15th of each active month for Year view\n            afterBuildTicks: (axis) => {\n              if (isYear && data.length > 0) {\n                const start = new Date(data[0].t);\n                const end = new Date(data[data.length - 1].t);\n                const startMonth = start.getMonth();\n                const endMonth = end.getMonth();\n                const year = start.getFullYear();\n                \n                const customTicks = [];\n                for (let m = startMonth; m <= endMonth; m++) {\n                  const tickTime = new Date(year, m, 15, 12, 0, 0).getTime();\n \/\/ FIX: Only draw ticks that are within the currently zoomed view\n                  if (tickTime >= axis.min && tickTime <= axis.max) {\n                    customTicks.push({ value: tickTime });\n                  }\n                }\n                axis.ticks = customTicks;\n              }\n            },\n            grid: { display: false }\n          },\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: 'original', \n                max: 'original' \n              } \n            },\n            pan: { enabled: !isReadingMode, mode: 'x' },\n            zoom: {\n              wheel: { enabled: true },\n              pinch: { enabled: true },\n              mode: 'x'\n            }\n          },\n\n          tooltip: {\n            enabled: isReadingMode,\n            backgroundColor: 'rgba(44, 62, 80, 0.9)',\n            \n\/\/ 1. Hide the standalone Direction dot in 24h Max mode (we bundle it below)\n            filter: function(tooltipItem) {\n              const isYear24 = currentTimeframe === 'year' && is24hMode;\n\/\/ Only hide if it's 24h mode AND we are NOT in Average Mode\n              if (isYear24 && !isAvgMode && tooltipItem.datasetIndex === 0) return false;\n              return true;\n            },\n            \n            callbacks: {\n              \n\/\/ 1. Title shows purely the Date in Year mode to avoid misleading anchor times\n              title: (items) => {\n                const targetDate = new Date(items[0].parsed.x);\n                \n\/\/ If we are anywhere in the Year view, only show the Date\n                if (currentTimeframe === 'year') {\n                  return targetDate.toLocaleString('en-US', { \n                    weekday: 'short', month: 'short', day: 'numeric', \n                    timeZone: 'Atlantic\/Madeira' \n                  });\n                }\n\n\/\/ For Day\/Week\/Month views, show Date and Time\n                return targetDate.toLocaleString('en-US', { \n                  weekday: 'short', month: 'short', day: 'numeric', \n                  hour: '2-digit', minute: '2-digit', hour12: false, \n                  timeZone: 'Atlantic\/Madeira' \n                });\n              },\n              \n              \/\/ 2. Both Wind and Gust times appear directly inline with their respective readings\n              label: (ctx) => {\n                const raw = ctx.raw.raw;\n                const isYear24 = currentTimeframe === 'year' && is24hMode;\n                const formatDir = (deg) => deg != null ? `${Math.round(deg)}\u00b0 ${getCompass(deg)}` : '--';\n\n                const getTimeStr = (timeStr) => {\n                  if (!timeStr) return '';\n                  const exactD = new Date(timeStr.replace(\/-\/g, \"\/\"));\n                  return exactD.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false, timeZone: 'Atlantic\/Madeira' });\n                };\n\n                \/\/ Dataset 0: Direction\n                if (ctx.datasetIndex === 0) {\n                  return `Dir: ${formatDir(ctx.parsed.y)}`;\n                }\n\n                 \/\/ Dataset 1: Gust Point\n                if (ctx.datasetIndex === 1) {\n                  if (isYear24 && raw) {\n                    const gTime = getTimeStr(raw.gTime);\n                    const timeLabel = gTime ? ` at ${gTime}` : '';\n                    return [\n                      `Gust: ${ctx.parsed.y.toFixed(1)} km\/h${timeLabel}`,\n                      `G_Dir: ${formatDir(raw.gDir)}`\n                    ];\n                  }\n                  return `Gusts: ${ctx.parsed.y.toFixed(1)} km\/h`;\n                }\n\n                     \/\/ Dataset 2: Wind Point\n                if (ctx.datasetIndex === 2) {\n                  \/\/ Only bundle direction\/time in 24h MAX mode\n                  if (isYear24 && raw && !isAvgMode) {\n                    const wTime = getTimeStr(raw.wTime);\n                    const timeLabel = wTime ? ` at ${wTime}` : '';\n                    return [\n                      `Wind: ${ctx.parsed.y.toFixed(1)} km\/h${timeLabel}`,\n                      `W_Dir: ${formatDir(raw.wDir)}`\n                    ];\n                  }\n                  \/\/ Standard return for all other modes, including 24h Avg\n                  return isAvgMode ? `Avg Wind: ${ctx.parsed.y.toFixed(1)} km\/h` : `Wind: ${ctx.parsed.y.toFixed(1)} km\/h`;\n                }\n         },\n\n              \/\/ 3. Daily Average displayed at the bottom of the Tooltip\n              footer: (items) => {\n                if (isAvgMode) return ''; \/\/ Hide footer duplicate in average mode\n                \n                const isYear24 = currentTimeframe === 'year' && is24hMode;\n                const raw = items[0]?.raw?.raw;\n                if (!raw) return '';\n\n                if (isYear24 && raw.avg24 != null) {\n                  return `\\nDaily Avg: ${raw.avg24} km\/h`;\n                } else if (currentTimeframe === 'year' && !is24hMode && raw.avgFly != null) {\n                  return `\\nDaytime Avg (9-21h): ${raw.avgFly} km\/h`;\n                }\n                return '';\n              }\n            }\n          }\n        }\n      },\n      plugins: [solarBackgroundPlugin, idealDirectionPlugin] \/\/ Flyable direction Plugin inside the Chart object and sun rise and set bg color plugin\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\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\/\/ Legend button Cycle Logic with dynamic opacity & axis mirroring\n  window.cycleChartViews = function() {\n  if (isAvgMode) return; \/\/ <--- SAFETY SWITCH: Blocks clicks in Average mode\n\n  currentCycleState = (currentCycleState + 1) % 7;\n  if (!windChart) return;\n\n    \/\/ Helper to set chart dataset visibility and legend opacity\n    const setVis = (index, legendId, isVisible) => {\n      windChart.setDatasetVisibility(index, isVisible);\n      document.getElementById(legendId).style.opacity = isVisible ? '1' : '0.3';\n    };\n\n    \/\/ Chart dataset structure: 0 = Direction, 1 = Gusts, 2 = Wind\n    if (currentCycleState === 0) { \n      \/\/ Default: All visible\n      setVis(2, 'leg-wind', true); setVis(1, 'leg-gust', true); setVis(0, 'leg-dir', true);\n    } else if (currentCycleState === 1) { \n      \/\/ 1st click: Gusts + Direction\n      setVis(2, 'leg-wind', false); setVis(1, 'leg-gust', true); setVis(0, 'leg-dir', true);\n    } else if (currentCycleState === 2) { \n      \/\/ 2nd click: Wind + Direction\n      setVis(2, 'leg-wind', true); setVis(1, 'leg-gust', false); setVis(0, 'leg-dir', true);\n    } else if (currentCycleState === 3) { \n      \/\/ 3rd click: Just Direction\n      setVis(2, 'leg-wind', false); setVis(1, 'leg-gust', false); setVis(0, 'leg-dir', true);\n    } else if (currentCycleState === 4) { \n      \/\/ 4th click: Just Wind\n      setVis(2, 'leg-wind', true); setVis(1, 'leg-gust', false); setVis(0, 'leg-dir', false);\n    } else if (currentCycleState === 5) {\n      \/\/ 5th click: Just Gusts\n      setVis(2, 'leg-wind', false); setVis(1, 'leg-gust', true); setVis(0, 'leg-dir', false);\n    } else if (currentCycleState === 6) { \n      \/\/ 6th click: Wind + Gusts\n      setVis(2, 'leg-wind', true); setVis(1, 'leg-gust', true); setVis(0, 'leg-dir', false);\n    }\n\n    \/\/ Evaluate active views\n    const isWindActive = windChart.isDatasetVisible(2);\n    const isGustActive = windChart.isDatasetVisible(1);\n    const isDirActive = windChart.isDatasetVisible(0);\n    const isSpeedActive = isWindActive || isGustActive;\n\n\/\/ --- NEW: DYNAMIC DIRECTION SWITCHING ---\n    if (windChart.data.datasets[0]) {\n      windChart.data.datasets[0].data.forEach(point => {\n        if (!point.raw) return;\n        if (isGustActive && !isWindActive) {\n          \/\/ If ONLY Gusts are viewed, move the dots to the Gust Direction\n          point.y = point.raw.gDir !== undefined ? point.raw.gDir : point.raw.d;\n        } else {\n          \/\/ Otherwise, default to Wind Direction\n          point.y = point.raw.wDir !== undefined ? point.raw.wDir : point.raw.d;\n        }\n      });\n    }\n    \/\/ ----------------------------------------\n\n   \/\/ 1. DYNAMIC OPACITY BOOSTING\n    \/\/ Wind opacity logic\n    if (isWindActive && !isGustActive) {\n\/\/ Wind only (Strong boost + Background Fill)\n      windChart.data.datasets[2].borderColor = 'rgba(41, 128, 185, 0.60)';\n      windChart.data.datasets[2].fill = true;\n    } else if (isWindActive && isGustActive && !isDirActive) {\n \/\/ Wind + Gusts together without Direction (Slight boost)\n      windChart.data.datasets[2].borderColor = 'rgba(41, 128, 185, 0.45)';\n      windChart.data.datasets[2].fill = false;\n    } else {\n      \/\/ Default standard view\n      windChart.data.datasets[2].borderColor = 'rgba(41, 128, 185, 0.30)';\n      windChart.data.datasets[2].fill = false;\n    }\n\n\n    \/\/ Gust opacity logic\n    if (isGustActive && !isWindActive) {\n      \/\/ Gusts only (Strong boost)\n      windChart.data.datasets[1].borderColor = 'rgba(230, 126, 34, 0.60)';\n      windChart.data.datasets[1].backgroundColor = 'rgba(230, 126, 34, 0.10)';\n    } else if (isWindActive && isGustActive && !isDirActive) {\n \/\/ Wind + Gusts together without Direction (Slight boost)\n      windChart.data.datasets[1].borderColor = 'rgba(230, 126, 34, 0.40)';\n      windChart.data.datasets[1].backgroundColor = 'rgba(230, 126, 34, 0.08)';\n    } else {\n      \/\/ Default standard view\n      windChart.data.datasets[1].borderColor = 'rgba(230, 126, 34, 0.10)';\n      windChart.data.datasets[1].backgroundColor = 'rgba(230, 126, 34, 0.05)';\n    }\n\n\n    \/\/ 2. DYNAMIC AXIS MIRRORING\n    const scales = windChart.options.scales;\n\n\/\/ Define mirrored scales on demand if they do not exist yet\n    if (!scales.ySpeedRight) {\n      scales.ySpeedRight = {\n        position: 'right',\n        ticks: { font: { size: 10 }, color: '#888' },\n        grid: { display: false },\n        afterBuildTicks: (axis) => {\n          const mainAxis = axis.chart.scales.ySpeed;\n          if (mainAxis && mainAxis.ticks) {\n            axis.min = mainAxis.min;\n            axis.max = mainAxis.max;\n            axis.ticks = [...mainAxis.ticks]; \n          }\n        }\n      };\n    }\n    if (!scales.yDirLeft) {\n      scales.yDirLeft = {\n        position: 'left',\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\n\/\/ Toggle mirror states based on view selection\n    if (isSpeedActive && !isDirActive) {\n\/\/ Speed active only -> Display speed on left AND right, turn off direction\n      scales.ySpeed.display = true;\n      scales.ySpeedRight.display = true;\n      if (scales.yDir) scales.yDir.display = false;\n      scales.yDirLeft.display = false;\n    } else if (isDirActive && !isSpeedActive) {\n\/\/ Direction active only -> Display direction on right AND left, turn off speed\n      scales.yDir.display = true;\n      scales.yDirLeft.display = true;\n      if (scales.ySpeed) scales.ySpeed.display = false;\n      scales.ySpeedRight.display = false;\n    } else {\n      \/\/ Standard view -> Speed on left, Direction on right, mirrors turned off\n      if (scales.ySpeed) scales.ySpeed.display = isSpeedActive;\n      if (scales.yDir) scales.yDir.display = isDirActive;\n      scales.ySpeedRight.display = false;\n      scales.yDirLeft.display = false;\n    }\n\n    \/\/ Instant update without re-triggering entrance animation\n    windChart.update('none'); \n  };\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 - NEW CloudBase added 19-02-2026 -->\n<!-- added 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;\">MAIATA 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('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<style>\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>\n(function() {\n  let climateChart;\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\/maiata\/';\n  \n \/\/ MAIATA Define Station Start Date - Fully synchronized with datePicker.min\n  const stationStartDate = new Date(\"2026-02-07\");\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' },\ncloud: { \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: ' 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\nconst getCloudbase = (t, dp) => {\n    if (t == null || dp == null) return null;\n   \/\/ Formula: (Temp - Dewpoint) * 125 + station_height\n    const base = ((t - dp) * 125) + 30;\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');\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 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 === '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\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) {\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 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 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\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) {\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\/\/ 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), \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     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;\nelse if (currentType === 'cloud') yVal = getCloudbase(day.temp?.c, day.dewpoint?.c);\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 adds the dashes only when viewing the 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\/\/ 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        scales: {\n          x: { \n  type: 'linear', \n\/\/ 'currentTime' match script's variable name and stretch day graph edge to edge\n            min: currentTime === 'day' ? new Date(getISO(activeDate) + 'T00:00:00').getTime() : undefined,\n            max: currentTime === 'day' ? new Date(getISO(activeDate) + 'T24:00:00').getTime() : undefined,\n            ticks: { \n\/\/ 3 hours * 60 min * 60 sec * 1000 ms\n              stepSize: currentTime === 'day' ? (3 * 60 * 60 * 1000) : undefined,\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 : (currentType === 'cloud' ? 1200 : 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          tooltip: {\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 Cloud, Humidity, and Solar\nconst formattedValue = (['cloud', 'hum', 'solar'].includes(currentType)) \n    ? Math.round(value) \n    : value.toFixed(1); \/\/ Keeps decimal for Temp, Rain, and Pressure\n\n    \n    return `${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    \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<\/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 added 19-02-2026 - Zoom added 27-04-26 -->\n<!-- added uv below \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;\">MAIATA 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('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\/maiata\/';\n  \n \/\/ MAIATA Define Station Start Date - Fully synchronized with datePicker.min\n  const stationStartDate = new Date(\"2026-02-07\");\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' },\ncloud: { \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: ' 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\nconst getCloudbase = (t, dp) => {\n    if (t == null || dp == null) return null;\n   \/\/ Formula: (Temp - Dewpoint) * 125 + station_height\n    const base = ((t - dp) * 125) + 30;\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');\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 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 === '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\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) {\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 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 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\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) {\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\/\/ 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), \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     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;\nelse if (currentType === 'cloud') yVal = getCloudbase(day.temp?.c, day.dewpoint?.c);\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 adds the dashes only when viewing the 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\/\/ 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})(),\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' ? (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.getMonth() + 1) + '\/' + String(d.getFullYear()).slice(-2);\n                return d.getDate() + '\/' + (d.getMonth() + 1);\n              }\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 : (currentType === 'cloud' ? 1200 : 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') && 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 Cloud, Humidity, and Solar\nconst formattedValue = (['cloud', 'hum', 'solar'].includes(currentType)) \n    ? Math.round(value) \n    : value.toFixed(1); \/\/ Keeps decimal for Temp, Rain, and Pressure\n\n    \n    return `${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    \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\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 added 19-02-2026 - Zoom added 27-04-26 and UV added 25-05-26 -->\n<!-- added bg solar color 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;\">MAIATA 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\/maiata\/';\n  \n  \/\/ MAIATA Define Station Start Date - Fully synchronized with datePicker.min\n  const stationStartDate = new Date(\"2026-02-07\");\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) + 30;\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\n<!-- Brilliant Climate day picker, 7 days, 30 days and year, temp, hum, rain, solar and pressure evolution - NEW CloudBase added 19-02-2026 - Zoom added 27-04-26 and UV added 25-05-26 -->\n<!-- fine tuned the cloud day paint only during day below -->\n<template>\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;\">MAIATA 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<!-- Added SunCalc for historical day\/night bg color accuracy -->\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/suncalc\/1.9.0\/suncalc.min.js\"><\/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\/maiata\/';\n  \n  \/\/ MAIATA Define Station Start Date - Fully synchronized with datePicker.min\n  const stationStartDate = new Date(\"2026-02-07\");\n\n  \/\/ NEW: Day\/Night Background Plugin for Historical Accuracy - Climate Widget\n  const solarBackgroundPlugin = {\n    id: 'solarBackground',\n    beforeDraw: (chart) => {\n      if (currentTime !== 'day') return;\n\n      const { ctx, chartArea: { top, bottom, left, right }, scales: { x } } = chart;\n      if (!x) return;\n\n      \/\/ Coordinates for Madeira Island\n      const lat = 32.77;\n      const lng = -16.83;\n\n      \/\/ Get the absolute UTC solar times for the date\n      const times = SunCalc.getTimes(activeDate, lat, lng);\n\n      \/\/ Timezone alignment: Extract the exact wall-clock hour\/minute in Madeira, \n      \/\/ and apply it to our local chart axis to prevent timezone shifting bugs.\n      const alignToAxis = (dateObj) => {\n        const parts = new Intl.DateTimeFormat('en-US', {\n          timeZone: 'Atlantic\/Madeira',\n          hour: 'numeric', minute: 'numeric', second: 'numeric', hour12: false\n        }).formatToParts(dateObj);\n        \n        let h = 0, m = 0, s = 0;\n        parts.forEach(p => {\n          if (p.type === 'hour') h = parseInt(p.value);\n          if (p.type === 'minute') m = parseInt(p.value);\n          if (p.type === 'second') s = parseInt(p.value);\n        });\n        \n        const localDate = new Date(activeDate);\n        localDate.setHours(h === 24 ? 0 : h, m, s, 0);\n        return localDate.getTime();\n      };\n\n      const sunrise = alignToAxis(times.sunrise);\n      const sunset = alignToAxis(times.sunset);\n\n      const drawNight = (startTime, endTime) => {\n        const startPx = Math.max(left, x.getPixelForValue(startTime));\n        const endPx = Math.min(right, x.getPixelForValue(endTime));\n\n        \/\/ Only draw if the night section is actually visible on the chart\n        if (startPx < endPx) {\n          ctx.save();\n          ctx.fillStyle = 'rgba(0, 0, 0, 0.04)'; \/\/ Gentle night shade\n          ctx.fillRect(startPx, top, endPx - startPx, bottom - top);\n          ctx.restore();\n        }\n      };\n\n      \/\/ Set boundaries for the current day locally\n      const startOfDay = new Date(activeDate);\n      startOfDay.setHours(0, 0, 0, 0);\n      const endOfDay = new Date(activeDate);\n      endOfDay.setHours(23, 59, 59, 999);\n\n      \/\/ Draw the night blocks\n      drawNight(startOfDay.getTime(), sunrise);\n      drawNight(sunset, endOfDay.getTime());\n    }\n  };\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: '#758494', \/\/ cloud color changed from #7f8c8d\nopacity: '25',  \/\/ added cloud fill opacity\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) + 30;\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) for a gentle fill\n\/\/ added cloud infill opacity here\n        backgroundColor: (currentType === 'rain' && currentTime === 'day') ? '#2ecc7115' : (currentType === 'solar' || currentType === 'uv' ? cfg.color + '20' : (currentType === 'cloud' ? cfg.color + (cfg.opacity || '15') : 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      \/\/ Added the solarBackgroundPlugin here\n      plugins: [solarBackgroundPlugin]\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<\/template>\n\n\n\n<!-- Brilliant Climate day picker, 7 days, 30 days and year, temp, hum, rain, solar and pressure evolution - NEW CloudBase added 19-02-2026 - Zoom added 27-04-26 and UV added 25-05-26 -->\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;\">MAIATA 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<!-- Added SunCalc for historical day\/night bg color accuracy -->\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/suncalc\/1.9.0\/suncalc.min.js\"><\/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\/maiata\/';\n  \n  \/\/ MAIATA Define Station Start Date - Fully synchronized with datePicker.min\n  const stationStartDate = new Date(\"2026-02-07\");\n\n  \/\/ NEW: Helper to calculate exact local sunrise\/sunset for a given date\n  \/\/ This centralizes the logic so both the plugin and dataset rules can use it\n  const getLocalSunTimes = (date) => {\n    \/\/ Coordinates for Madeira Island\n    const lat = 32.77;\n    const lng = -16.83;\n    const times = SunCalc.getTimes(date, lat, lng);\n\n    \/\/ Timezone alignment: Extract the exact wall-clock hour\/minute in Madeira, \n    \/\/ and apply it to our local chart axis to prevent timezone shifting bugs.\n    const alignToAxis = (dateObj) => {\n      const parts = new Intl.DateTimeFormat('en-US', {\n        timeZone: 'Atlantic\/Madeira',\n        hour: 'numeric', minute: 'numeric', second: 'numeric', hour12: false\n      }).formatToParts(dateObj);\n      \n      let h = 0, m = 0, s = 0;\n      parts.forEach(p => {\n        if (p.type === 'hour') h = parseInt(p.value);\n        if (p.type === 'minute') m = parseInt(p.value);\n        if (p.type === 'second') s = parseInt(p.value);\n      });\n      \n      const localDate = new Date(date);\n      localDate.setHours(h === 24 ? 0 : h, m, s, 0);\n      return localDate.getTime();\n    };\n\n    return {\n      sunrise: alignToAxis(times.sunrise),\n      sunset: alignToAxis(times.sunset)\n    };\n  };\n\n  \/\/ NEW: Day\/Night Background Plugin for Historical Accuracy - Climate Widget\n  const solarBackgroundPlugin = {\n    id: 'solarBackground',\n    beforeDraw: (chart) => {\n      if (currentTime !== 'day') return;\n\n      const { ctx, chartArea: { top, bottom, left, right }, scales: { x } } = chart;\n      if (!x) return;\n\n      const { sunrise, sunset } = getLocalSunTimes(activeDate);\n\n      const drawNight = (startTime, endTime) => {\n        const startPx = Math.max(left, x.getPixelForValue(startTime));\n        const endPx = Math.min(right, x.getPixelForValue(endTime));\n\n        \/\/ Only draw if the night section is actually visible on the chart\n        if (startPx < endPx) {\n          ctx.save();\n          ctx.fillStyle = 'rgba(0, 0, 0, 0.04)'; \/\/ Gentle night shade\n          ctx.fillRect(startPx, top, endPx - startPx, bottom - top);\n          ctx.restore();\n        }\n      };\n\n      \/\/ Set boundaries for the current day locally\n      const startOfDay = new Date(activeDate);\n      startOfDay.setHours(0, 0, 0, 0);\n      const endOfDay = new Date(activeDate);\n      endOfDay.setHours(23, 59, 59, 999);\n\n      \/\/ Draw the night blocks\n      drawNight(startOfDay.getTime(), sunrise);\n      drawNight(sunset, endOfDay.getTime());\n    }\n  };\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: '#758494', \/\/ cloud color changed from #7f8c8d\nopacity: '30',  \/\/ added cloud fill opacity\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) + 30;\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\/\/ Year Solar with daytime offline protection\n      if (currentType === 'solar') {\n                const firstDay = dayBlocks[0];\n                const stats = firstDay.stats || {};\n                let yld = firstDay.solar_yield || stats.solar_yield || 0;\n                let uv = firstDay.uv_max || stats.uv_max || 0;\n                \n                \/\/ Treat 0 as missing data so the line breaks cleanly instead of dipping\n                if (yld === 0) yld = null;\n                if (uv === 0) uv = null;\n\n                yieldData.push({ x: baseTime, y: yld });\n                \n                \/\/ Apply UV x40 multiplier here for visual flow\n                uvMaxData.push({ x: baseTime, y: uv !== null ? uv * 40 : null });\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 \/\/ Week and month solar with safety check         \n             yVal = day.stats ? day.stats[cfg.sKey] : (day[cfg.sKey] || day[cfg.yKey]);\n            \n            \/\/ Expand the 0-check to include solar as well as UV\n            if ((currentType === 'uv' || currentType === 'solar') && (yVal === 0 || yVal === undefined)) yVal = null;\n            \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                let yld = day.solar_yield || (day.stats ? day.stats.solar_yield : 0);\n                if (yld === 0) yld = null;\n                harvestPoints.push({ x: dayTime, y: yld });\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) for a gentle fill\n\/\/ added cloud infill opacity here\n        backgroundColor: (currentType === 'rain' && currentTime === 'day') ? '#2ecc7115' : (currentType === 'solar' || currentType === 'uv' ? cfg.color + '20' : (currentType === 'cloud' ? cfg.color + (cfg.opacity || '15') : cfg.color + '15')),\n        \n        borderWidth: 2,\n        pointRadius: 0,\n\/\/ paint to top for clouds, otherwise fill to floor\n        fill: currentType === 'cloud' ? 'end' : true,\n        tension: 0.4,\n        \n        \/\/ NEW: Segment logic to remove cloud infill during night hours cleanly\n        segment: {\n          backgroundColor: ctx => {\n            if (currentTime === 'day' && currentType === 'cloud') {\n              const xVal = ctx.p0.parsed.x;\n              const { sunrise, sunset } = getLocalSunTimes(activeDate);\n              \/\/ Make fill transparent if the segment point is before sunrise or after sunset\n              if (xVal < sunrise || xVal > sunset) {\n                return 'transparent';\n              }\n            }\n            return undefined; \/\/ Falls back to standard backgroundColor automatically\n          }\n        }\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                        afterBuildTicks: (axis) => {\n              if (currentTime === 'year' && rawData.length > 0) {\n                const uniqueMonths = new Set();\n                rawData.forEach(d => {\n                  const timeKey = d.t || d.date;\n                  if (timeKey) {\n                    \/\/ Normalize the date string to prevent timezone offset issues\n                    const dateStr = timeKey.includes(' ') ? timeKey.split(' ')[0] : timeKey;\n                    const dateObj = new Date(dateStr.includes('T') ? dateStr : dateStr + \"T12:00:00Z\");\n                    uniqueMonths.add(`${dateObj.getFullYear()}-${dateObj.getMonth()}`);\n                  }\n                });\n                \n                const customTicks = [];\n                uniqueMonths.forEach(yearMonth => {\n                  const [y, m] = yearMonth.split('-');\n                  \/\/ Center the tick precisely on the 15th of the active month\n                  const tickTime = new Date(parseInt(y), parseInt(m), 15, 12, 0, 0).getTime();\n                  if (tickTime >= axis.min && tickTime <= axis.max) {\n                    customTicks.push({ value: tickTime });\n                  }\n                });\n                axis.ticks = customTicks;\n              }\n            },\n            ticks: { \n              includeBounds: true,\n              autoSkip: true,\n              \/\/ Higher limits allow the \"super resolution\" look\n              maxTicksLimit: currentTime === 'day' ? 12 : (currentTime === 'week' ? 14 : 12),\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' || currentType === 'cloud'),\n            \/\/ Only use grace for Rain\/Solar\/Temp\/UV\/cloud; Humidity and Pressure should be tight\n\n            grace: (currentType === 'rain' || currentType === 'solar' || currentType === 'temp' || currentType === 'uv' || currentType === 'cloud') ? '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      \/\/ Added the solarBackgroundPlugin here\n      plugins: [solarBackgroundPlugin]\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:22px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\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\/IPORTO452\" target=\"_blank\" rel=\"noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"444\" height=\"308\" class=\"wp-image-892\" style=\"width: 50%;\" src=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/10\/wunderground-logo.png\" alt=\"\" srcset=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/10\/wunderground-logo.png 444w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/10\/wunderground-logo-300x208.png 300w\" sizes=\"auto, (max-width: 444px) 100vw, 444px\" \/><\/a><\/td><td class=\"has-text-align-center\" data-align=\"center\"><a href=\"https:\/\/www.windguru.cz\/station\/5899\" target=\"_blank\" rel=\"noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"400\" height=\"333\" class=\"wp-image-893\" style=\"width: 50%;\" src=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/10\/Windguru-logo.png\" alt=\"\" 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: 400px) 100vw, 400px\" \/><\/a><\/td><td class=\"has-text-align-center\" data-align=\"center\"><a href=\"https:\/\/www.windy.com\/station\/pws-f0dbfb95?32.639,-16.822,11\"><img loading=\"lazy\" decoding=\"async\" width=\"333\" height=\"333\" class=\"wp-image-895\" style=\"width: 50%;\" src=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2024\/10\/Windy-logo.png\" alt=\"\" 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: 333px) 100vw, 333px\" \/><\/a><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<!-- old wunderground url https:\/\/www.wunderground.com\/dashboard\/pws\/IPORTO361 -->\n<div style=\"text-align: center; margin-top:-10px;\">\n<p class=\"has-text-align-center\"><small><sub><sup><em>since 20\/08\/24<\/em><\/sup><\/sub><\/small><\/p>\n<\/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_5899_1728522217581\">\n(function (window, document) {\n  var loader = function () {\n    var arg = [\"spot=5899\",\"uid=wglive_5899_1728522217581\",\"color=light\",\"wj=kmh\",\"tj=c\",\"avg_min=0\",\"gsize=200\",\"msize=250\",\"m=3\",\"arrow=y\",\"show=l,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               \/\/ 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<script>\n  if (window !== window.parent && document.body.classList.contains('logged-in')) {\n    \/\/ Only apply if inside an iframe AND user is logged in\n    const style = document.createElement(\"style\");\n    style.innerHTML = `\n      html.wp-toolbar,\n      body {\n        margin-top: 0 !important;\n      }\n      body {\n        position: relative;\n        top: -32px !important;\n      }\n    `;\n    document.head.appendChild(style);\n  }\n<\/script>\n","protected":false},"excerpt":{"rendered":"<p>All | Canhas | Porto da Cruz | Portela Porto da Cruz @39m asl (flyable ideally N\/NE @20-25kmh) 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 &amp; Gust evolution (km\/h) 2h Wind Direction READING Mode ACTIVE RESET View 24h &nbsp; [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"slim_seo":{"title":"Porto da Cruz Weather station","description":"Weather station with live data at one of the most frequent Paragliding takeoffs on the North of Madeira Island in Maiata at Porto da Cruz"},"footnotes":""},"class_list":["post-1720","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages\/1720","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\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/comments?post=1720"}],"version-history":[{"count":303,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages\/1720\/revisions"}],"predecessor-version":[{"id":11837,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages\/1720\/revisions\/11837"}],"wp:attachment":[{"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/media?parent=1720"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}