{"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-04-05T00:41:39","modified_gmt":"2026-04-04T23:41:39","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-a89b3969 wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/paragliding-in-madeira.com\/weather\/\">Home<\/a><\/div>\n\n\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/paragliding-in-madeira.com\/weather\/weather-info\/\">Weather Info<\/a><\/div>\n\n\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/paragliding-in-madeira.com\/weather\/contribute\/\">Contribute<\/a><\/div>\n<\/div>\n\n\n\n<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<figure data-wp-context=\"{&quot;imageId&quot;:&quot;69d4e46ba54e7&quot;}\" data-wp-interactive=\"core\/image\" data-wp-key=\"69d4e46ba54e7\" 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-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\taria-label=\"Enlarge\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"state.imageButtonRight\"\n\t\t\tdata-wp-style--top=\"state.imageButtonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<p class=\"has-text-align-center\" id=\"PortoDaCruz\"><strong><a href=\"https:\/\/paragliding-in-madeira.com\/weather\/porto-da-cruz\/#PortoDaCruz\" target=\"_PARENT\" style=\"text-decoration:none\">Porto da Cruz<\/a><\/strong><sup><em> @39m asl <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  const todayPrefix = new Date().toISOString().slice(0, 10);\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 = 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      \/\/ Timestamp\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      \/\/ Primary Data\n      document.getElementById('sc-speed').innerText = latest.windSpeed.kph.toFixed(1);\n      document.getElementById('sc-gust').innerText = latest.windGust.kph.toFixed(1);\n      document.getElementById('sc-deg').innerText = latest.winddir + '\u00b0';\n      document.getElementById('sc-card').innerText = getCardinal(latest.winddir);\n      document.getElementById('sc-temp').innerText = latest.temp.c.toFixed(1);\n      document.getElementById('sc-hum').innerText = Math.round(latest.humidity);\n      \n      \/\/ Fixed Pressure\n      document.getElementById('sc-pres').innerText = Math.round(latest.pressure.hPa);\n      \n      renderInteractive();\n\n      \/\/ Rotate Arrow\n      const arrow = document.getElementById('sc-arrow');\n      arrow.style.transform = `rotate(${(latest.winddir + 180) % 360}deg)`;\n\n    } catch (e) { console.error(\"Widget error:\", e); }\n  }\n\n  updateWidget();\n  setInterval(updateWidget, 60000);\n})();\n<\/script>\n\n\n\n<div class=\"webcam-section\">\n<!-- WEBCAM-START -->\n<div class=\"epyt-video-wrapper\"><div  style=\"display: block; margin: 0px auto;\"  id=\"_ytid_20044\"  width=\"333\" height=\"188\"  data-origwidth=\"333\" data-origheight=\"188\"  data-relstop=\"1\" data-facadesrc=\"https:\/\/www.youtube.com\/embed\/igioSn9nFwg?enablejsapi=1&autoplay=0&cc_load_policy=1&cc_lang_pref=en&iv_load_policy=3&loop=0&rel=0&fs=1&playsinline=0&autohide=2&hl=en_US&theme=dark&color=red&controls=1&\" class=\"__youtube_prefs__ epyt-facade no-lazyload\" data-vol=\"0\"  data-epautoplay=\"1\" ><img decoding=\"async\" data-spai-excluded=\"true\" class=\"epyt-facade-poster skip-lazy\" loading=\"lazy\"  alt=\"YouTube player\"  src=\"https:\/\/i.ytimg.com\/vi\/igioSn9nFwg\/hqdefault.jpg\"  \/><button class=\"epyt-facade-play\" aria-label=\"Play\"><svg data-no-lazy=\"1\" height=\"100%\" version=\"1.1\" viewBox=\"0 0 68 48\" width=\"100%\"><path class=\"ytp-large-play-button-bg\" d=\"M66.52,7.74c-0.78-2.93-2.49-5.41-5.42-6.19C55.79,.13,34,0,34,0S12.21,.13,6.9,1.55 C3.97,2.33,2.27,4.81,1.48,7.74C0.06,13.05,0,24,0,24s0.06,10.95,1.48,16.26c0.78,2.93,2.49,5.41,5.42,6.19 C12.21,47.87,34,48,34,48s21.79-0.13,27.1-1.55c2.93-0.78,4.64-3.26,5.42-6.19C67.94,34.95,68,24,68,24S67.94,13.05,66.52,7.74z\" fill=\"#f00\"><\/path><path d=\"M 45,24 27,14 27,34\" fill=\"#fff\"><\/path><\/svg><\/button><\/div><\/div>\n<!-- WEBCAM-END -->\n<\/div>\n\n\n\n<!-- 2h wind widget -->\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 &#038; Gust evolution (km\/h)<\/span><\/div>\n  <div class=\"sc-chart-wrapper\">\n    <canvas id=\"sc-speed-chart\"><\/canvas>\n  <\/div>\n  <div class=\"sc-graph-label\"><span>2h Wind Direction<\/span><\/div>\n  <div class=\"sc-chart-wrapper\" style=\"height: 160px;\">\n    <canvas id=\"sc-dir-chart\"><\/canvas>\n  <\/div>\n<\/div>\n\n<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\n const getFileUrl = (date) => baseDir + `${date.toISOString().slice(0, 10)}.json`;\n      \n\/\/ Get Today with Cache Busting\n      const resToday = await fetch(getFileUrl(new Date()) + cacheBust);\n      let combinedData = await resToday.json();\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  acc.push({ x: d.time, y: d.windGust.kph });\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  acc.push({ x: d.time, y: d.windSpeed.kph });\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<!-- 12h temp and hum graph -->\n<!-- Added independent layers below\n<div class=\"sc-pro-cockpit\" style=\"margin-top: 10px;\">\n <div class=\"sc-legend\" style=\"display: flex; justify-content: left; gap: 11px; margin-bottom: 8px; font-size: 11px; font-weight: 600; color: #888;\">\n  <div style=\"display: flex; align-items: center; gap: 4px;\">\n   12h &nbsp;<span style=\"width: 12px; height: 3px; background: #3498db; border-radius: 2px;\"><\/span> Hum %\n  <\/div>\n  <div style=\"display: flex; align-items: center; gap: 4px;\">\n    <span style=\"width: 12px; height: 3px; background: #e67e22; border-radius: 2px;\"><\/span> Temp \u00baC\n  <\/div>\n  <div style=\"display: flex; align-items: center; gap: 4px;\">\n    <span style=\"width: 12px; height: 9px; background: rgba(200, 200, 200, 0.6); border-radius: 2px;\"><\/span> Daytime Cloudbase\n  <\/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<script>\n(function() {\n  let storyChart;\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 \/\/ This clears the highlight points on the lines\n        chart.setActiveElements([]); \n  \/\/ This hides the tooltip box\n        if (chart.tooltip) {\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  async function updateStory() {\n    try {\n      const now = Date.now();\n      const cacheBust = '?t=' + now; \/\/ Ensuring cache busting is defined\n      const eightHoursAgo = now - (12 * 60 * 60 * 1000);\n\/\/ 12h graph window\n      \n\/\/ CONFIG TOGGLE - CHANGE THIS TO False to ALWAYS SHOW CLOUDS\n      const showCloudsOnlyDuringDay = true; \n\n\/\/ --- Get Sunrise and set from our json file ---\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 timeFormatter = (val) => {\n        const d = new Date(val);\n        return `${d.getHours()}:${String(d.getMinutes()).padStart(2, '0')}`;\n      };\n\n      const getFileUrl = (date) => baseDir + `${date.toISOString().slice(0, 10)}.json`;\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 > eightHoursAgo)) {\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 > (eightHoursAgo - 10000))\n  .filter((value, index, self) => \n    index === self.findIndex((t) => t.time === value.time)\n  );\n\n\/\/ --- GAP DETECTOR START ---\nconst processedStoryData = [];\nconst GAP_THRESHOLD = 10 * 60 * 1000; \/\/ 10 minutes\n\nwindowData.forEach((d, i) => {\n  if (i > 0 && (d.time - windowData[i - 1].time) > GAP_THRESHOLD) {\n\/\/ Place null for all 3 metrics to hide all lines at once\n          processedStoryData.push({ \n      time: d.time - 1, \n      humidity: null, \n      temp: null, \n      dewpoint: null \n    });\n  }\n  processedStoryData.push(d); \n});\n\/\/ --- GAP DETECTOR END ---\n\n      if (storyChart) storyChart.destroy();\n      \n      const ctx = document.getElementById('sc-8h-story-chart').getContext('2d');\n\n      const cloudLayerPlugin = {\n        id: 'cloudLayer',\n        beforeDatasetsDraw: (chart) => {\n          const { ctx, chartArea: { top, bottom, left, right }, scales: { x } } = chart;\n          ctx.save();\n          ctx.beginPath();\n          let first = true;\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; \/\/ +station height for accuracy\n              const daytimeCloudY = bottom - (Math.min(cbMeters, 2000) \/ 2000) * (bottom - top);\n              \n  \/\/ Apply dynamic night check\n              const isNight = getIsNight(d.time);\n              const cloudY = (showCloudsOnlyDuringDay && isNight) ? bottom : daytimeCloudY;\n\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  { \n    label: 'Humidity', \ndata: processedStoryData.map(d => ({x: d.time, y: d.humidity})),\n    borderColor: '#3498db', \n    backgroundColor: 'rgba(52, 152, 219, 0.05)', \n    yAxisID: 'yHum', \n    pointRadius: 0, \n    borderWidth: 2, \n    tension: 0.4, \n    fill: true, \n    spanGaps: false,\n    zIndex: 1 \/\/ Lowest visual layer, but top of tooltip\n  },\n  { \n    label: 'Temp', \ndata: processedStoryData.map(d => ({x: d.time, y: d.temp ? d.temp.c : null})),\n    borderColor: '#e67e22', \n    backgroundColor: 'rgba(230, 126, 34, 0.1)', \n    yAxisID: 'yTemp', \n    pointRadius: 0, \n    borderWidth: 2, \n    tension: 0.4, \n    spanGaps: false,\n    zIndex: 3 \/\/ appears on top\n  },\n  { \n    label: 'DewPoint', \ndata: processedStoryData.map(d => ({x: d.time, y: d.dewpoint ? d.dewpoint.c : null})),\n    borderColor: 'transparent', \/\/ Visually remove the line\n    pointRadius: 0, \n    borderWidth: 0,\n    yAxisID: 'yTemp', \n    tension: 0.1, \n    spanGaps: false,\n    zIndex: 2 \n  }\n]\n        },\n        options: {\n          responsive: true,\n          maintainAspectRatio: false,\n\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) => {\n      const val = ctx.parsed.y;\n      if (val === null) return '';\n      \n      \/\/ 1. Humidity is first in the dataset array, so it shows at the top\n      if (ctx.dataset.label === 'Humidity') return `Humidity: ${Math.round(val)}%`;\n      \n      \/\/ 2. Temp is in the middle\n      if (ctx.dataset.label === 'Temp') return `Temp: ${val.toFixed(1)}\u00b0C`;\n      \n      \/\/ 3. DewPoint is last, so we attach the Cloud Base here to put it at the bottom\n      if (ctx.dataset.label === 'DewPoint') {\n        const d = windowData[ctx.dataIndex];\n        const dpLine = `Dew Point: ${val.toFixed(1)}\u00b0C`;\n        const cb = (d.temp.c - d.dewpoint.c) * 125 + 30; \/\/ + station height for accuracy\n \/\/ dynamic night check for the tooltip to hide the cloudbase calc\n        const isNight = getIsNight(d.time);\n\n        if (showCloudsOnlyDuringDay && isNight) return dpLine;\n        return [dpLine, `Est. Cloud Base: ${Math.max(0, Math.round(cb))}m` ];\n      }\n      return '';\n    }\n  }\n}\n          },\n          scales: {\n            x: { type: 'linear', min: eightHoursAgo, max: now, ticks: { stepSize: 3600000, callback: (v) => timeFormatter(v), font: { size: 10 }, maxRotation: 0 }, grid: { color: 'rgba(0,0,0,0.09)' } },\n            yTemp: { \n              type: 'linear', \n              position: 'left', \n              grace: '14%', \n              ticks: { font: { size: 10, weight: '600' }, callback: (v) => v.toFixed(0) + '\u00b0', stepSize: 1 }, \n              grid: { display: true, color: 'rgba(34, 34, 34, 0.12)', drawTicks: false, borderDash: [3, 3] }, \n         \/\/  suggestedMin: 14, \n         \/\/  suggestedMax: 28,\n \/\/ FILTER:  scale to ignore invisible DewPoint for min\/max calculation\n              afterDataLimits: (axis) => {\n                const tempDataset = axis.chart.data.datasets.find(ds => ds.label === 'Temp');\n                if (tempDataset) {\n                  const values = tempDataset.data.map(d => d.y).filter(v => v !== null);\n                                    const min = Math.min(...values);\nconst max = Math.max(...values);\n\n\/\/ Adaptive Scale - afterDataLimits logic for yTemp \"secret sauce\" calculates real temperature spread and makes the axis roughly double that height ((max - min) * 1.1 + 1.1). Mathematically places Temp line on bottom half of the graph, whether 15\u00b0C or 30\u00b0C\n\naxis.min = min - 1;\naxis.max = max + ((max - min) * 1.1 + 1.1); \n                }\n              }\n            },\n            yHum: { type: 'linear', position: 'right', min: 0, max: 100, ticks: { font: { size: 10 }, callback: (v) => v + '%' }, grid: { display: false } }\n          }\n        },\n        plugins: [solarBackgroundPlugin, cloudLayerPlugin]\n      });\n      autoHideTooltip(storyChart);\n    } catch (e) { console.error(\"Story Chart Error:\", e); }\n  }\n\n  updateStory();\n  setInterval(updateStory, 300000); \/\/ 5 minute refresh\n})();\n<\/script>\n-->\n\n\n\n<!-- 12h temp and hum graph -->\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     12h &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<script>\n(function() {\n  let storyChart;\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();\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\/\/ 12h graph window\n const twelveHoursAgo = now - (12 * 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 timeFormatter = (val) => {\n        const d = new Date(val);\n        return `${d.getHours()}:${String(d.getMinutes()).padStart(2, '0')}`;\n      };\n\n      const getFileUrl = (date) => baseDir + `${date.toISOString().slice(0, 10)}.json`;\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(); ctx.beginPath();\n          let first = true;\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          responsive: true,\n          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) => {\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          },\n          scales: {\n            x: { type: 'linear', min: twelveHoursAgo, max: now, ticks: { stepSize: 3600000, callback: (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  updateStory();\n\/\/ 5 minute refresh\n  setInterval(updateStory, 300000); \n})();\n<\/script>\n\n\n\n<!-- 12h pressure evolution graph -->\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        12h &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  const timeFormatter = (val) => {\n    const d = new Date(val);\n    return `${d.getHours()}:${String(d.getMinutes()).padStart(2, '0')}`;\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 - (12 * 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) => baseDir + `${date.toISOString().slice(0, 10)}.json`;\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 - (12 * 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      const d = new Date(v);\n                  return `${d.getHours()}:00`;\n                },\n                font: { size: 10 },\n                maxRotation: 0,\n                padding: 10 \/\/ Added space for readability\n              },\n              grid: { color: 'rgba(0,0,0,0.03)', drawBorder: false }\n            },\n            y: {\n              type: 'linear',\n       suggestMin: minP - 0.5,\n       suggestMax: maxP + 0.5,\n              ticks: { \nfont: { size: 10, weight: '600' },\nprecision: 0, \nstepSize: 1,\n callback: (v) => v.toFixed(0),\n                padding: 8\n              },\n              grid: { color: 'rgba(0,0,0,0.06)', borderDash: [4, 4], drawBorder: false }\n            }\n          }\n        },\n        plugins: [solarBackgroundPlugin]\n      });\n      autoHideTooltip(pressureChart);\n    } catch (e) { console.error(\"Pressure Error:\", e); }\n  }\n\n  updatePressure();\n  setInterval(updatePressure, 300000);\n})();\n<\/script>\n\n\n\n<!-- 24h rain evolution graph with daily totals -->\n<!-- Fine tuned the legend 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 { position: relative; height: 180px; }\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    <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 todayStr = new Date().toISOString().slice(0, 10);\n      const yesterdayStr = new Date(now - 86400000).toISOString().slice(0, 10);\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).toISOString().split('T')[0];\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, \n\nsuggestedMax: 7, \/\/ For better visual consistency \n\n         display: true,\n         ticks: { \n         font: { size: 9, weight: 600 }, \ncolor: '#4caf50', \/\/ Matching line color\ncallback: (v) => v + ' mm\/h'\n              }, \n              grid: { display: false } \n            },\n            y: { \n         type: 'linear',\n         position: 'right',\n         beginAtZero: true, \n         grace: '20%',\n\nsuggestedMax: 3, \/\/ Keeps low totals from filling the whole height\n\n            ticks: { \n           font: { size: 9, weight: 600 }, \ncolor: '#185e96', \/\/ Matching line color\ncallback: (v) => v.toFixed(1) + ' mm' \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 -->\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 todayStr = new Date().toISOString().slice(0, 10);\n      const yesterdayStr = new Date(now - 86400000).toISOString().slice(0, 10);\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).toISOString().split('T')[0];\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<!-- 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' });\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 Solar evolution graph with UV and Total Yield Harvest -->\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;\nwhite-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: 180px; }\n<\/style>\n\n<div class=\"sc-solar-cockpit\">\n  <div class=\"sc-solar-label\">\n    <span>24h Solar<\/span>\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\">Yield: 0 Wh\/m\u00b2<\/span>\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  \/\/ Autohide tooltip on mobile (Logic from Rain Widget)\n  function setupAutoHide(chart, delay = 4000) {\n    const canvasEl = chart.canvas;\n    const clear = () => {\n      chart.tooltip.setActiveElements([], { x: 0, y: 0 });\n      chart.update();\n    };\n    canvasEl.addEventListener('touchstart', () => {\n      if (solarTimeout) clearTimeout(solarTimeout);\n      solarTimeout = setTimeout(clear, delay);\n    });\n  }\n\n  \/\/ Standalone Sun Data fetch to prevent blocking\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().toISOString().slice(0, 10);\n      const yesterdayStr = new Date(now - 86400000).toISOString().slice(0, 10);\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 (The Rain logic)\n      if (combined.length === 0) {\n        combined.push({ time: rolling24, solarRadiation: 0, uv: 0 });\n      }\n\n  \/\/ If the station is silent for more than 10 minutes, we do NOT bridge.\n\/\/ This leaves a clean \"blank\" space on the right of the graph.\nconst lastPoint = combined[combined.length - 1];\nconst isOffline = lastPoint && (now - lastPoint.time) > (10 * 60 * 1000);\n\nif (isOffline) {\n  \/\/ We can optionally update the badge to show the station is \"Asleep\"\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        \n        if (prev) {\n          const gapMs = curr.time - prev.time;\n          \n  \/\/ If gap > 10 mins, break the line and skip yield calculation for this period\n          if (gapMs > 10 * 60 * 1000) {\n            plotData.push({ x: prev.time + 1, y: null, uv: null });\n          } else {\n            \/\/ Only add to total yield if the station was active (small gap)\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\n      document.getElementById('sc-solar-total-badge').innerText = `Total: ${Math.round(totalWh)} Wh\/m\u00b2`;\n\n      \/\/ 4. CHART PLUGINS (Night Masking using Rain style logic)\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,\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,\n              pointRadius: 0,\n              yAxisID: 'yUV',\n              zIndex: 1\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) => formatTime(items[0].parsed.x),\n                label: (ctx) => {\n                  const d = plotData[ctx.dataIndex];\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: 800,\n              ticks: { font: { size: 9 }, callback: (v) => v + ' W' },\n              grid: { color: 'rgba(0,0,0,0.03)' }\n            },\n            yUV: {\n              position: 'right',\n              min: 0,\n              suggestedMax: 16,\n              ticks: { font: { size: 8, weight: '600' }, color: '#673ab7' },\n              grid: { display: false }\n            }\n          }\n        },\n        plugins: [nightMaskPlugin]\n      });\n\n    autoHideTooltip(solarChart);\n         \/\/ Auto hide Tooltip function\nfunction autoHideTooltip(chart, delay = 5000) {\n  let hideTimer;\n  const clearTooltip = () => {\n    if (chart) {\n      \/\/ Clears the highlight dot on the pressure 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\n  chart.canvas.addEventListener('touchstart', () => {\n    clearTimeout(hideTimer);\n  });\n\n  chart.canvas.addEventListener('touchend', () => {\n    clearTimeout(hideTimer);\n    hideTimer = setTimeout(clearTooltip, delay);\n  });\n}\n\n    } catch (e) { console.error(\"Solar Error:\", e); }\n  }\n\n\/\/ Start up Logic\n  updateSolar();\n  setInterval(updateSolar, 300000);\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;\">--\/--\/----<\/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\">--<\/span><span class=\"sc-h-unit\">km\/h<\/span>\n        <span class=\"v-sep\">\/<\/span>\n        <span class=\"hi-val\" id=\"h-gust-max\">--<\/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\">--<\/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)\">---<\/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\">--<\/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\">--<\/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\">--<\/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\">--<\/span><span class=\"sc-h-unit\">%<\/span>\n        <span class=\"v-sep\">-<\/span>\n        <span class=\"hi-val\" id=\"h-hum-max\">--<\/span><span class=\"sc-h-unit\">%<\/span>\n      <\/div>\n      <div class=\"sc-h-footer\"><span>Avg<\/span> <span id=\"h-hum-avg\">--<\/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\">--<\/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 --<\/div>\n      <div class=\"sc-h-footer\"><span>Peak<\/span> <span id=\"h-solar-peak\">--<\/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\">--<\/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\">--<\/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\">--<\/b><\/div>\n\n      <div class=\"est-row\"><span>25W portable solar panel:<\/span> <b id=\"est-25\">--<\/b><\/div>\n      <div class=\"est-row\"><span>100W e-bike solar panel:<\/span> <b id=\"est-100\">--<\/b><\/div>\n      <div class=\"est-row\"><span>450W home solar panel:<\/span> <b id=\"est-450\">--<\/b><\/div>\n      \n      <div id=\"solar-insight-box\">\n        <span id=\"insight-text\">Tap to cycle through real-life examples...<\/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\">--<\/span>\n        <span class=\"trend-arrow\" id=\"h-pres-icon\"><\/span>\n        <span id=\"h-pres-end\">--<\/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\">--<\/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  const stationStartDate = new Date(\"2026-02-07\");\n\/\/ THIS IS THE FIRST DAY ONLINE WITH JSON FILES\n  const getISO = (d) => d.toISOString().split('T')[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\n}));\n\n        if (!data || data.length === 0) return;\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\n        document.getElementById('h-wind-max').innerText = (json.stats.peak_24h_wind || 0).toFixed(1);\n        document.getElementById('h-gust-max').innerText = (json.stats.peak_24h_gust || 0).toFixed(1);\n        document.getElementById('h-wind-avg').innerText = (json.stats.wind_avg_24h || 0).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.toFixed(1);\n        document.getElementById('h-temp-max').innerText = json.stats.temp_max.toFixed(1);\n        document.getElementById('h-temp-avg').innerText = json.stats.temp_avg.toFixed(1) + '\u00b0';\n        \n        document.getElementById('h-hum-min').innerText = json.stats.hum_min;\n        document.getElementById('h-hum-max').innerText = json.stats.hum_max;\n        document.getElementById('h-hum-avg').innerText = 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.toFixed(1);\n        document.getElementById('h-rain-rate').innerText = json.stats.rain_max_rate.toFixed(1) + ' mm\/h';\n\n \/\/ --- PRESSURE TREND ---\n        const startP = json.hourly.pressure[0].hPa;\n        const endP = json.hourly.pressure[json.hourly.pressure.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\n        document.getElementById('h-title').innerText = \"Maiata DAILY Summary\";\n        document.getElementById('h-date').innerText = selectedDate.toLocaleDateString('pt-PT').replace(\/\\\/\/g, '-');\n        document.getElementById('h-solar-wh').innerText = Math.round(totalWh);\n        document.getElementById('h-sol-unit').innerText = \"Wh\/m\u00b2\";\n        document.getElementById('h-solar-peak').innerText = Math.round(maxSolar) + ' W\/m\u00b2';\n        document.getElementById('h-uv-peak').innerText = \"UV \" + (json.stats.uv_max || 0).toFixed(1);\n        document.getElementById('est-title').innerText = \"EST. GENERATION TODAY (Wh)\";\n\n\/\/ New Vevor solar panel estimation added\ndocument.getElementById('est-station').innerText = (currentView === 'day') ? \n    yields.yStation.toFixed(1) + ' Wh' : \n    (yields.yStation \/ 1000).toFixed(2) + ' kWh';\n\n        document.getElementById('est-25').innerText = Math.round(yields.y25) + ' Wh';\n        document.getElementById('est-100').innerText = Math.round(yields.y100) + ' Wh';\n        document.getElementById('est-450').innerText = Math.round(yields.y450) + ' Wh';\n        document.getElementById('pres-label').innerText = \"Pressure Trend\";\n        document.getElementById('pres-trend-box').style.display = \"flex\";\n      }\n      \n      else {\n \/\/ --- MONTHLY \/ YEARLY LOGIC (Direct from PHP Stats) ---\n        const stats = json.stats || {};\n        document.getElementById('h-title').innerText = currentView === 'month' ? \"MONTHLY TOTALS\" : \"YEARLY TOTALS\";\n        document.getElementById('h-date').innerText = currentView === 'month' ? selectedDate.toLocaleString('en-US', { month: 'long', year: 'numeric' }) : \"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 || 0).toFixed(1);\n        document.getElementById('h-gust-max').innerText = (stats.peak_24h_gust || 0).toFixed(1);\n        document.getElementById('h-wind-avg').innerText = (stats.wind_avg_24h || 0).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 || 0).toFixed(1);\n        document.getElementById('h-temp-max').innerText = (stats.temp_max || 0).toFixed(1);\n        document.getElementById('h-temp-avg').innerText = (stats.temp_avg || 0).toFixed(1) + '\u00b0';\n        \n    \/\/ 4. HUMIDITY & RAIN\n        document.getElementById('h-hum-min').innerText = stats.hum_min || \"--\";\n        document.getElementById('h-hum-max').innerText = stats.hum_max || \"--\";\n        document.getElementById('h-hum-avg').innerText = (stats.hum_avg || \"--\") + '%';\n        \n    \/\/ Rain\n        document.getElementById('h-rain-total').innerText = (json.stats.rain_total || 0).toFixed(1);\n        document.getElementById('h-rain-rate').innerText = (json.stats.rain_max_rate || 0).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 = Math.round(stats.pressure_min || 0) + \"\";\ndocument.getElementById('h-pres-icon').innerHTML = `<span style=\"color:rgba(171,171,171,0.5); margin: 0 4px;\">\/<\/span>`; \/\/ The separator now lives in the icon slot\n        document.getElementById('h-pres-end').innerText = Math.round(stats.pressure_max || 0);\n        document.getElementById('h-pres-delta').innerText = ((stats.pressure_max || 0) - (stats.pressure_min || 0)).toFixed(0) + ' hPa diff';\n      }\n    } catch (e) { \n      console.warn(\"No data found for this date:\", todayStr);\n      document.getElementById('h-date').innerText = \"No Data Available\";\n    }\n  }\n\n  updateHistory();\n  setInterval(() => { if(currentView === 'day') updateHistory(); }, 300000);\n})();\n<\/script>\n\n\n\n<!-- Day plus Precious selection, 7 days, 30 days and year Wind 9h to 21h -->\n\n<div class=\"sc-pro-cockpit wind-vault\" style=\"font-family: 'Inter', sans-serif; background: rgba(250, 250, 250,0.4); padding: 15px; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.05); margin-top: 20px;\">\n  \n  <div class=\"sc-header-flex\" style=\"display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px;\">\n    <div style=\"display: flex; flex-direction: column; gap: 8px;\">\n      <div id=\"sc-wind-time-nav\" style=\"display: flex; gap: 8px; align-items: center;\">\n        <button onclick=\"loadWindData('day')\" id=\"w-btn-day\" class=\"nav-btn active\">Today<\/button>\n        <button onclick=\"loadWindData('week')\" id=\"w-btn-week\" class=\"nav-btn\">7 Days<\/button>\n        <button onclick=\"loadWindData('month')\" id=\"w-btn-month\" class=\"nav-btn\">30 Days<\/button>\n        <button onclick=\"loadWindData('year')\" id=\"w-btn-year\" class=\"nav-btn\">Year<\/button>\n      <\/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 now = new Date();\n  if (now.getHours() < 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  const getISO = (d) => d.toISOString().split('T')[0];\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'});\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 = new Date(d.time).getHours();\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(activeDate).setHours(is24hMode ? 0 : 9, 0, 0, 0) : undefined,\nmax: currentTimeframe === 'day' ? new Date(activeDate).setHours(is24hMode ? 24 : 21, 0, 0, 0) : undefined,\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);\nif (currentTimeframe === 'day') return d.getHours() + ':00';\nif (isYear) return (d.getMonth() + 1) + '\/' + String(d.getFullYear()).slice(-2);\nreturn d.getDate() + '\/' + (d.getMonth() + 1);\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 });\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<!-- Brilliant Climate day picker, 7 days, 30 days and year, temp, hum, rain, solar and pressure evolution - NEW CloudBase added 19-02-2026 -->\n\n<!-- to be edited soon rain legend fine tuning black legend and max suggested rate and total\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\n<div id=\"sc-label-rate\" style=\"position: absolute; top: -5px; left: 0; font-size: 9px; font-weight: 800; color: #999; display: none; z-index: 5;\">RATE (mm\/h)<\/div>\n    <div id=\"sc-label-total\" style=\"position: absolute; top: -5px; right: 0; font-size: 9px; font-weight: 800; color: #999; display: none; z-index: 5;\">TOTAL (mm)<\/div>\n\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.toISOString().split('T')[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');\n    const nextBtn = document.getElementById('sc-next-day-btn');\n    const prevBtn = document.querySelector('.date-arrow:first-child');\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      \/\/ Precision Move: Target both buttons by ID\n      const prevBtn = document.getElementById('sc-prev-day-btn');\n      const nextBtn = document.getElementById('sc-next-day-btn');\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      document.getElementById('sc-display-date').innerText = activeDate.toLocaleDateString('pt-PT', {day:'2-digit', month:'2-digit'});\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        let lastLogged = 0;\n        rawData = dayRaw.filter(d => {\n\/\/ If viewing Cloud use 10 mins (600k ms), else stay at 5 mins (300k ms) or 1 min 60000\n    const interval = (currentType === 'cloud') ? 600000 : 60000;\n    \n    if (d.time - lastLogged >= interval) {\n        lastLogged = d.time;\n        return true;\n    }\n    return false;\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      } else {\n        let fileName = (timeframe === 'year') ? activeDate.getFullYear() + '.summary.json' : 'week.summary.json';\n        const res = await fetch(`${stationPath}${fileName}${cacheBust}`);\n        const json = await res.json();\n        rawData = json.days || [];\n      }\n      renderChart();\n    } catch (e) { \n        console.error(e);\n        if (climateChart) climateChart.destroy();\n    }\n  }\n\n  window.switchClimateType = function(type) {\n    currentType = type;\n    document.querySelectorAll('.sub-tab').forEach(t => t.classList.toggle('active', t.id === 'tab-'+type));\n    renderChart();\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 \/\/ Toggle the top HTML labels based on type\n    const labelRate = document.getElementById('sc-label-rate');\n    const labelTotal = document.getElementById('sc-label-total');\n    if (labelRate && labelTotal) {\n        const isRain = currentType === 'rain';\n        labelRate.style.display = isRain ? 'block' : 'none';\n        labelTotal.style.display = isRain ? 'block' : 'none';\n    }\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      Object.keys(grouped).forEach(date => {\n        const dayBlocks = grouped[date];\n        const baseTime = new Date(date.replace(\/-\/g, \"\/\")).getTime();\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), yAxisID: 'yRate', 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\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                day.hourly[cfg.hArray].forEach(p => chartData.push({ x: p.t, y: p[cfg.hKey] }));\n                return;\n            }\n            xVal = timeKey.includes('T') ? new Date(timeKey).getTime() : new Date(timeKey + \"T12:00:00Z\").getTime();\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), yAxisID: 'yRate', 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), yAxisID: 'y', 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),\nyAxisID: (currentType === 'rain' && currentTime === 'day') ? 'yRate' : 'y', \nspanGaps: false,\n  borderColor: cfg.color,\n  \/\/ We use a low opacity hex (15 or 10) for a gentle fill\n  backgroundColor: currentType === 'solar' ? cfg.color + '20' : cfg.color + '15', \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,\nmaintainAspectRatio: false,\nlayout: { padding: { top: 20 } },\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(activeDate).setHours(0, 0, 0, 0) : undefined,\n  max: currentTime === 'day' ? new Date(activeDate).setHours(24, 0, 0, 0) : undefined,\n  ticks: { \n\n\/\/ 3 hours * 60 min * 60 sec * 1000 ms\n         stepSize: currentTime === 'day' ? (3 * 60 * 60 * 1000) : undefined,\n\n    font: { size: 9 }, \n    callback: v => {\n      const d = new Date(v);\n      if (currentTime === 'day') return d.getHours() + ':00';\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: { \nposition: currentType === 'rain' ? 'right' : 'left', \/\/ Move to right for rain\n\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\ncolor: currentType === 'rain' ? '#3498db' : '#666', \/\/ Blue for Total\n\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\nreturn value;\n\n        }\n    }, \n    grid: { color: 'rgba(0,0,0,0.03)' } \n\n},\n\n\/\/ SECONDARY AXIS (Rate on Left, only for Rain)\nyRate: {\n    type: 'linear',\n    position: 'left',\n    display: currentType === 'rain',\n\n    beginAtZero: true,\n    suggestedMax: 10,\n    ticks: {\n        font: { size: 10 },\n        color: '#2ecc71', \/\/ Green for Rate\n        callback: (v) => v + '' \/\/ no \"mm\/h\" legend for graph space\n    },\n    grid: { display: false }\n}\n\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\/\/ Added hour12: false for 24h format\n                return (isYear || currentType === 'solar' || currentType === 'rain') && currentTime !== 'day'\n                  ? d.toLocaleDateString([], { day:'numeric', month:'short', year: 'numeric' })\n                  : d.toLocaleString([], { day:'numeric', month:'short', hour:'2-digit', minute:'2-digit', hour12: false });\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 -->\n<!-- added max daily values 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: '#2ecc71', 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.toISOString().split('T')[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');\n    const nextBtn = document.getElementById('sc-next-day-btn');\n    const prevBtn = document.querySelector('.date-arrow:first-child');\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      \/\/ Precision Move: Target both buttons by ID\n      const prevBtn = document.getElementById('sc-prev-day-btn');\n      const nextBtn = document.getElementById('sc-next-day-btn');\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      document.getElementById('sc-display-date').innerText = activeDate.toLocaleDateString('pt-PT', {day:'2-digit', month:'2-digit'});\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        let lastLogged = 0;\n        rawData = dayRaw.filter(d => {\n\/\/ If viewing Cloud use 10 mins (600k ms), else stay at 5 mins (300k ms) or 1 min 60000\n    const interval = (currentType === 'cloud') ? 600000 : 60000;\n    \n    if (d.time - lastLogged >= interval) {\n        lastLogged = d.time;\n        return true;\n    }\n    return false;\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      } else {\n        let fileName = (timeframe === 'year') ? activeDate.getFullYear() + '.summary.json' : 'week.summary.json';\n        const res = await fetch(`${stationPath}${fileName}${cacheBust}`);\n        const json = await res.json();\n        rawData = json.days || [];\n      }\n      renderChart();\n    } catch (e) { \n        console.error(e);\n        if (climateChart) climateChart.destroy();\n    }\n  }\n\n  window.switchClimateType = function(type) {\n    currentType = type;\n    document.querySelectorAll('.sub-tab').forEach(t => t.classList.toggle('active', t.id === 'tab-'+type));\n    renderChart();\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      Object.keys(grouped).forEach(date => {\n        const dayBlocks = grouped[date];\n        const baseTime = new Date(date.replace(\/-\/g, \"\/\")).getTime();\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: '#3498db', 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\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                day.hourly[cfg.hArray].forEach(p => chartData.push({ x: p.t, y: p[cfg.hKey] }));\n                return;\n            }\n            xVal = timeKey.includes('T') ? new Date(timeKey).getTime() : new Date(timeKey + \"T12:00:00Z\").getTime();\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: '#3498db', 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  borderColor: cfg.color,\n  \/\/ We use a low opacity hex (15 or 10) for a gentle fill\n  backgroundColor: currentType === 'solar' ? cfg.color + '20' : cfg.color + '15', \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(activeDate).setHours(0, 0, 0, 0) : undefined,\n  max: currentTime === 'day' ? new Date(activeDate).setHours(24, 0, 0, 0) : undefined,\n  ticks: { \n\n\/\/ 3 hours * 60 min * 60 sec * 1000 ms\n         stepSize: currentTime === 'day' ? (3 * 60 * 60 * 1000) : undefined,\n\n    font: { size: 9 }, \n    callback: v => {\n      const d = new Date(v);\n      if (currentTime === 'day') return d.getHours() + ':00';\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\/\/ Added hour12: false for 24h format\n                return (isYear || currentType === 'solar' || currentType === 'rain') && currentTime !== 'day'\n                  ? d.toLocaleDateString([], { day:'numeric', month:'short', year: 'numeric' })\n                  : d.toLocaleString([], { day:'numeric', month:'short', hour:'2-digit', minute:'2-digit', hour12: false });\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 -->\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.toISOString().split('T')[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      document.getElementById('sc-display-date').innerText = activeDate.toLocaleDateString('pt-PT', {day:'2-digit', month:'2-digit'});\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      Object.keys(grouped).forEach(date => {\n        const dayBlocks = grouped[date];\n        const baseTime = new Date(date.replace(\/-\/g, \"\/\")).getTime();\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\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                day.hourly[cfg.hArray].forEach(p => chartData.push({ x: p.t, y: p[cfg.hKey] }));\n                return;\n            }\n            xVal = timeKey.includes('T') ? new Date(timeKey).getTime() : new Date(timeKey + \"T12:00:00Z\").getTime();\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  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(activeDate).setHours(0, 0, 0, 0) : undefined,\n  max: currentTime === 'day' ? new Date(activeDate).setHours(24, 0, 0, 0) : undefined,\n  ticks: { \n\n\/\/ 3 hours * 60 min * 60 sec * 1000 ms\n         stepSize: currentTime === 'day' ? (3 * 60 * 60 * 1000) : undefined,\n\n    font: { size: 9 }, \n    callback: v => {\n      const d = new Date(v);\n      if (currentTime === 'day') return d.getHours() + ':00';\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\/\/ Added hour12: false for 24h format\n                return (isYear || currentType === 'solar' || currentType === 'rain') && currentTime !== 'day'\n                  ? d.toLocaleDateString([], { day:'numeric', month:'short', year: 'numeric' })\n                  : d.toLocaleString([], { day:'numeric', month:'short', hour:'2-digit', minute:'2-digit', hour12: false });\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<div style=\"height:22px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary><\/summary>\n<div align=\"left\">\n<div id=\"maiata\" style=\"height:100%:!important;width: 363px;overflow-x:auto;overflow-y:hidden; border: 0px;\">\n  <div id=\"WindguruScrollArea\" style=\"width: 377px; height: 100%;\">\n<div id=\"wgs_widget_5899_1728775056570\"><\/div>\n\n      <script>\n        (function() {\n          var loadWidget = function() {\n            \n\/\/ Create the script element dynamically\n            var s = document.createElement(\"script\");\n            s.src = \"https:\/\/www.windguru.cz\/js\/wgs_widget.php\";\n            s.onload = function() {\n          \n\/\/ Once the script is loaded, initialize the widget\n              if (typeof WgsWidget !== 'undefined') {\n                WgsWidget({\n                  id_station: 5899,\n                  wj: 'kmh',\n                  tj: 'c',\n                  avg_min: 0,\n                  tmprh: true,\n                  date_format: 'Y-m-d H:i ',\n                  divid: 'wgs_widget_5899_1728775056570',\n                  type: 'curr'\n                });\n              }\n            };\n            document.body.appendChild(s);\n          };\n\n\/\/ Only trigger after the window has fully loaded to keep initial page load fast\n          if (document.readyState === \"complete\") {\n            loadWidget();\n          } else {\n            window.addEventListener(\"load\", loadWidget, false);\n          }\n        })();\n      <\/script>\n    <\/div>\n  <\/div>\n<\/div>\n\n\n\n<div>\n  <div id=\"wgs_widget_5899_1732915829415\" style=\"margin-top:-1px;\"><\/div>\n\n  <script>\n    (function() {\n      var loadWindWidget = function() {\n        var s = document.createElement(\"script\");\n        s.src = \"https:\/\/www.windguru.cz\/js\/wgs_widget.php\";\n        s.onload = function() {\n          if (typeof WgsWidget !== 'undefined') {\n            WgsWidget({\n              id_station: 5899,\n              width: null,\n              height: 155,\n              hours: 2,\n              wj: 'kmh',\n              gustiness: false,\n              divid: 'wgs_widget_5899_1732915829415',\n              type: 'wind'\n            });\n          }\n        };\n        document.body.appendChild(s);\n      };\n\n      \/\/ Wait for the page to be completely finished\n      if (document.readyState === \"complete\") {\n        loadWindWidget();\n      } else {\n        window.addEventListener(\"load\", loadWindWidget, false);\n      }\n    })();\n  <\/script>\n<\/div>\n\n\n\n<div>\n  <div id=\"wgs_widget_5899_1732916186980\" style=\"margin-top:-1px;\"><\/div>\n\n  <script>\n    (function() {\n      var loadTempWidget = function() {\n        var s = document.createElement(\"script\");\n        s.src = \"https:\/\/www.windguru.cz\/js\/wgs_widget.php\";\n        s.onload = function() {\n          if (typeof WgsWidget !== 'undefined') {\n            WgsWidget({\n              id_station: 5899,\n              width: null,\n              height: 77,\n              hours: 8,\n              tj: 'c',\n              divid: 'wgs_widget_5899_1732916186980',\n              type: 'temp'\n            });\n          }\n        };\n        document.body.appendChild(s);\n      };\n\n      \/\/ Final safety net: Wait until the page is fully ready\n      if (document.readyState === \"complete\") {\n        loadTempWidget();\n      } else {\n        window.addEventListener(\"load\", loadTempWidget, false);\n      }\n    })();\n  <\/script>\n<\/div>\n\n\n\n<p class=\"has-text-align-center\"><small><em><sup>Wind in the last 2h - Temp &amp; Humidity in the last 8h<\/sup><\/em><\/small><\/p>\n<\/details>\n\n\n\n<div style=\"height:18px\" 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 &#038; Gust evolution (km\/h) 2h Wind Direction 12h &nbsp; Temp \u00baC Hum % Cloudbase [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"slim_seo":{"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\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/comments?post=1720"}],"version-history":[{"count":187,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages\/1720\/revisions"}],"predecessor-version":[{"id":10314,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages\/1720\/revisions\/10314"}],"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}]}}