Τι κουβέρτα χρειάζεται το άλογό σου απόψε?

3clink rel="stylesheet" href="https://nuvallo.de/apps/blog/assets/styles.css"3e 3cdiv class="decken-page"3e 3cstyle3e /* Page-specific styles */ .decken-page { background: var(--green-light); min-height: 100vh; } .decken-page main { max-width: 520px; padding: 1.5rem 1rem; margin: 0 auto; } .decken-page h1 { font-size: 1.6rem; margin-bottom: 0.5rem; text-align: center; } .decken-subtitle { text-align: center; color: var(--gray); margin-bottom: 2rem; font-size: 0.95rem; } /* Horse type selector */ .horse-types { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.75rem; margin-bottom: 1.5rem; } .horse-type-btn { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; padding: 1rem 0.5rem; border: 2px solid var(--gray-light); border-radius: var(--radius-md); background: var(--white); cursor: pointer; transition: all 0.2s; font-family: var(--font-body); font-size: 0.85rem; font-weight: 500; color: var(--black); } .decken-page .horse-type-btn:hover { border-color: var(--green-dark); } .decken-page .horse-type-btn.active, .decken-page button.horse-type-btn.active { border-color: var(--green-dark) !important; background: var(--green-dark) !important; color: var(--white) !important; box-shadow: 0 0 0 3px rgba(74, 144, 110, 0.35); } .decken-page .horse-type-btn.active .horse-type-label, .decken-page .horse-type-btn.active .horse-type-icon { color: var(--white) !important; } .horse-type-icon { font-size: 2.2rem; } .horse-type-label { text-align: center; line-height: 1.2; } /* Postal code input */ .plz-row { display: flex; gap: 0.5rem; margin-bottom: 1.5rem; } .plz-row input { flex: 1; padding: 0.85rem 1rem; border: 2px solid var(--gray-light); border-radius: var(--radius-md); font-size: 1.1rem; font-family: var(--font-body); text-align: center; letter-spacing: 2px; transition: border-color 0.2s; } .plz-row input:focus { outline: none; border-color: var(--green-dark); } .go-btn { padding: 0.85rem 2rem; border: none; border-radius: var(--radius-md); background: var(--green-dark); color: var(--white); font-family: var(--font-heading); font-weight: 600; font-size: 1.1rem; cursor: pointer; transition: background 0.2s; white-space: nowrap; } .go-btn:hover { background: #3d7e5e; } .go-btn:disabled { opacity: 0.5; cursor: default; } /* Loading */ .loading { text-align: center; padding: 2rem 0; display: none; } .loading.visible { display: block; } .spinner { display: inline-block; width: 36px; height: 36px; border: 3px solid var(--gray-light); border-top-color: var(--green-dark); border-radius: 50%; animation: spin 0.7s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } .loading p { margin-top: 0.75rem; color: var(--gray); font-size: 0.9rem; } /* Result card */ .result-card { display: none; animation: fadeUp 0.4s ease; } .result-card.visible { display: block; } @keyframes fadeUp { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } } .recommendation-box { background: var(--white); border-radius: var(--radius-lg); padding: 1.5rem; text-align: center; margin-bottom: 1rem; box-shadow: 0 2px 12px rgba(0,0,0,0.06); } .rec-icon { font-size: 3.5rem; margin-bottom: 0.5rem; } .rec-title { font-family: var(--font-heading); font-size: 1.3rem; font-weight: 600; margin-bottom: 0.25rem; } .rec-detail { color: var(--gray); font-size: 0.95rem; line-height: 1.5; } .rec-fill { display: flex; align-items: center; justify-content: center; gap: 0.5rem; margin: 0.75rem 0 0.25rem; } .rec-fill-label { font-size: 0.85rem; color: var(--gray); } .fill-bar { display: flex; gap: 3px; } .fill-dot { width: 18px; height: 18px; border-radius: 50%; background: var(--gray-light); transition: background 0.3s; } .fill-dot.filled { background: var(--green-dark); } /* Weather panel */ .weather-panel { background: var(--white); border-radius: var(--radius-lg); padding: 1.25rem; box-shadow: 0 2px 12px rgba(0,0,0,0.06); margin-bottom: 1rem; } .weather-panel h2 { font-size: 1rem; margin-bottom: 0.75rem; color: var(--black); text-align: center; } .weather-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.75rem; } .weather-item { display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem; background: var(--green-light); border-radius: var(--radius-sm); } .weather-item-icon { font-size: 1.5rem; flex-shrink: 0; } .weather-item-info { line-height: 1.3; } .weather-item-label { font-size: 0.75rem; color: var(--gray); } .weather-item-value { font-weight: 600; font-size: 0.95rem; } /* Embed / share section */ .embed-section { background: var(--white); border-radius: var(--radius-lg); padding: 1.25rem; box-shadow: 0 2px 12px rgba(0,0,0,0.06); text-align: center; margin-top: 1.5rem; } .embed-section h2 { font-size: 1rem; margin-bottom: 0.75rem; } .embed-code { width: 100%; padding: 0.6rem; border: 1px solid var(--gray-light); border-radius: var(--radius-sm); font-family: monospace; font-size: 0.8rem; resize: none; color: var(--gray); } .copy-btn { margin-top: 0.5rem; padding: 0.5rem 1.5rem; border: 1px solid var(--green-dark); border-radius: var(--radius-sm); background: var(--white); color: var(--green-dark); font-weight: 600; cursor: pointer; font-family: var(--font-body); transition: all 0.2s; } .copy-btn:hover { background: var(--green-light); } /* Error */ .error-msg { text-align: center; color: #d32f2f; font-size: 0.9rem; padding: 0.75rem; display: none; } .error-msg.visible { display: block; } /* Retry */ .retry-row { text-align: center; margin-top: 1rem; } .retry-btn { padding: 0.6rem 1.5rem; border: 2px solid var(--green-dark); border-radius: var(--radius-md); background: var(--white); color: var(--green-dark); font-family: var(--font-heading); font-weight: 600; cursor: pointer; transition: all 0.2s; } .retry-btn:hover { background: var(--green-light); } 3c/style3e 3cmain3e 3c!-- Input Form --3e 3csection id="form-section"3e 3c!-- 3ch13eΤι κουβέρτα χρειάζεται το άλογό σου απόψε;3c/h13e --3e 3cp style="font-weight:500; margin-bottom:0.5rem; text-align:center;"3eΕπίλεξε τύπο αλόγου:3c/p3e 3cdiv class="horse-types"3e 3cbutton class="horse-type-btn" data-type="jung" onclick="selectHorse(this)"3e 3cspan class="horse-type-icon"3e🐎3c/span3e 3cspan class="horse-type-label"3eΝεαρό άλογο3c/span3e 3c/button3e 3cbutton class="horse-type-btn" data-type="alt" onclick="selectHorse(this)"3e 3cspan class="horse-type-icon"3e🐴3c/span3e 3cspan class="horse-type-label"3eΗλικιωμένο άλογο3c/span3e 3c/button3e 3cbutton class="horse-type-btn" data-type="ekzem" onclick="selectHorse(this)"3e 3cspan class="horse-type-icon"3e✨3c/span3e 3cspan class="horse-type-label"3eΈκζεμα / λεπτό τρίχωμα3c/span3e 3c/button3e 3c/div3e 3cdiv class="plz-row"3e 3cinput type="text" id="plz-input" placeholder="Εισαγωγή Τ.Κ." maxlength="5" inputmode="numeric" pattern="[0-9]*"3e 3cbutton class="go-btn" id="go-btn" onclick="checkWeather()" disabled3eΠάμε!3c/button3e 3c/div3e 3c/section3e 3c!-- Loading --3e 3cdiv class="loading" id="loading"3e 3cdiv class="spinner"3e3c/div3e 3cp3eΛήψη δεδομένων καιρού…3c/p3e 3c/div3e 3c!-- Error --3e 3cdiv class="error-msg" id="error-msg"3e3c/div3e 3c!-- Result --3e 3cdiv class="result-card" id="result-card"3e 3cdiv class="recommendation-box"3e 3cdiv class="rec-icon" id="rec-icon"3e3c/div3e 3cdiv class="rec-title" id="rec-title"3e3c/div3e 3cdiv class="rec-fill"3e 3cspan class="rec-fill-label"3eΘερμότητα:3c/span3e 3cdiv class="fill-bar" id="fill-bar"3e3c/div3e 3c/div3e 3cdiv class="rec-detail" id="rec-detail"3e3c/div3e 3c/div3e 3cdiv class="weather-panel"3e 3ch23e🌡️ Ο καιρός απόψε3c/h23e 3cdiv class="weather-grid" id="weather-grid"3e3c/div3e 3c/div3e 3cdiv class="retry-row"3e 3cbutton class="retry-btn" onclick="resetForm()"3e↻ Νέος έλεγχος3c/button3e 3c/div3e 3c/div3e 3c!-- Embed --3e 3cdiv class="embed-section"3e 3ch23e🔗 Ενσωμάτωση στον ιστότοπό σου3c/h23e 3ctextarea class="embed-code" rows="5" readonly id="embed-code"3e3c/textarea3e 3cbutton class="copy-btn" onclick="copyEmbed()"3eΑντιγραφή κώδικα3c/button3e 3c/div3e 3c/main3e 3cscript3e (function() { var selectedHorse = null; var plzInput = document.getElementById('plz-input'); var goBtn = document.getElementById('go-btn'); // Set embed code — always point to nuvallo.de regardless of current host var nuvalloURL = 'https://nuvallo.de/pages/pferdedecken'; document.getElementById('embed-code').value = '\u0033ciframe src="' + nuvalloURL + '" width="100%" height="700" style="border:none;border-radius:12px;" loading="lazy"\u0033e\u0033c/iframe\u0033e\\n' + '\u0033cp style="text-align:center;font-size:0.85rem;margin-top:0.5rem;"\u003eΥπολογιστής κουβέρτας αλόγου από την \u0033ca href="' + nuvalloURL + '" target="_blank" rel="noopener"\u003enuvallo\u0033c/a\u0033e\u0033c/p\u0033e'; // PLZ validation plzInput.addEventListener('input', function() { this.value = this.value.replace(/\D/g, '').slice(0, 5); updateGoBtn(); }); plzInput.addEventListener('keydown', function(e) { if (e.key === 'Enter' && !goBtn.disabled) checkWeather(); }); window.selectHorse = function(btn) { document.querySelectorAll('.horse-type-btn').forEach(function(b) { b.classList.remove('active'); }); btn.classList.add('active'); selectedHorse = btn.getAttribute('data-type'); updateGoBtn(); }; function updateGoBtn() { goBtn.disabled = !(selectedHorse && plzInput.value.length === 5); } window.checkWeather = function() { var plz = plzInput.value; if (!selectedHorse || plz.length !== 5) return; show('loading'); hide('result-card'); hide('error-msg'); // Step 1: geocode the PLZ via Open-Meteo geocoding fetch('https://geocoding-api.open-meteo.com/v1/search?name=' + plz + '&count=1&language=de&country=DE') .then(function(r) { return r.json(); }) .then(function(geo) { if (!geo.results || !geo.results.length) throw new Error('Ο Τ.Κ. δεν βρέθηκε. Σε παρακαλώ, έλεγξε τα στοιχεία σου.'); var loc = geo.results[0]; // Step 2: fetch weather return fetch( 'https://api.open-meteo.com/v1/forecast?latitude=' + loc.latitude + '&longitude=' + loc.longitude + '&hourly=temperature_2m,relative_humidity_2m,wind_speed_10m,precipitation_probability,weather_code' + '&timezone=Europe/Berlin&forecast_days=2' ).then(function(r) { return r.json(); }).then(function(weather) { return { location: loc, weather: weather }; }); }) .then(function(data) { hide('loading'); renderResult(data.weather, data.location); }) .catch(function(err) { hide('loading'); var msg = document.getElementById('error-msg'); msg.textContent = err.message || 'Σφάλμα κατά τη λήψη των δεδομένων καιρού.'; show('error-msg'); }); }; function renderResult(weather, location) { // Get tonight's data: today 18:00 – tomorrow 06:00 var hours = weather.hourly.time; var today = new Date(hours[0]).toISOString().slice(0, 10); var tomorrow = new Date(new Date(today).getTime() + 86400000).toISOString().slice(0, 10); var nightIndices = []; for (var i = 0; i 3c hours.length; i++) { var dt = new Date(hours[i]); var day = dt.toISOString().slice(0, 10); var h = dt.getHours(); if ((day === today && h 3e= 18) || (day === tomorrow && h 3c= 6)) { nightIndices.push(i); } } if (!nightIndices.length) { // fallback: use last 6 hours of the first day for (var j = 18; j 3c 24 && j 3c hours.length; j++) nightIndices.push(j); } var temps = nightIndices.map(function(i) { return weather.hourly.temperature_2m[i]; }); var winds = nightIndices.map(function(i) { return weather.hourly.wind_speed_10m[i]; }); var humids = nightIndices.map(function(i) { return weather.hourly.relative_humidity_2m[i]; }); var rains = nightIndices.map(function(i) { return weather.hourly.precipitation_probability[i]; }); var codes = nightIndices.map(function(i) { return weather.hourly.weather_code[i]; }); var avgTemp = avg(temps); var minTemp = Math.min.apply(null, temps); var avgWind = avg(winds); var avgHumid = avg(humids); var maxRain = Math.max.apply(null, rains); var mainCode = codes[Math.floor(codes.length / 2)]; // Compute "feels like" (simplified wind chill) var feelsLike = avgTemp; if (avgTemp 3c= 10 && avgWind 3e 5) { feelsLike = 13.12 + 0.6215 * avgTemp - 11.37 * Math.pow(avgWind, 0.16) + 0.3965 * avgTemp * Math.pow(avgWind, 0.16); } // Recommendation logic var rec = recommend(feelsLike, minTemp, avgWind, maxRain, avgHumid, selectedHorse); // Render recommendation document.getElementById('rec-icon').textContent = rec.icon; document.getElementById('rec-title').textContent = rec.title; document.getElementById('rec-detail').textContent = rec.detail; // Fill bar (0–5 warmth) var bar = document.getElementById('fill-bar'); bar.innerHTML = ''; for (var d = 0; d 3c 5; d++) { var dot = document.createElement('span'); dot.className = 'fill-dot' + (d 3c rec.warmth ? ' filled' : ''); bar.appendChild(dot); } // Render weather grid var grid = document.getElementById('weather-grid'); grid.innerHTML = [ weatherItem('🌡️', 'Θερμοκρασία', round(minTemp) + '–' + round(Math.max.apply(null, temps)) + ' °C'), weatherItem('🌬️', 'Άνεμος', round(avgWind) + ' km/h'), weatherItem('💧', 'Πιθανότητα βροχής', maxRain + ' %'), weatherItem(weatherIcon(mainCode), 'Καιρός', weatherLabel(mainCode)) ].join(''); show('result-card'); } function recommend(feelsLike, minTemp, wind, rain, humidity, horse) { // Horse sensitivity offset: young = 0, old = -3, ekzem = -5 (feels colder) var offset = horse === 'alt' ? -3 : horse === 'ekzem' ? -5 : 0; var effective = feelsLike + offset; // Rain factor var needsRain = rain 3e 40; if (effective 3e 15) { return { icon: '☀️', title: 'Δεν χρειάζεται κουβέρτα', warmth: 0, detail: 'Ήπια νύχτα — το άλογό σου είναι μια χαρά χωρίς κουβέρτα.' + (needsRain ? ' Σε περίπτωση βροχής, προαιρετικά μια αδιάβροχη κουβέρτα.' : '') }; } if (effective 3e 10) { return { icon: needsRain ? '🌧️' : '🍃', title: needsRain ? 'Ελαφριά αδιάβροχη κουβέρτα' : 'Ελαφριά κουβέρτα μεταβατικής περιόδου', warmth: 1, detail: 'Δροσερή νύχτα (' + round(minTemp) + ' °C). Μια λεπτή κουβέρτα (0–50g) αρκεί.' + (needsRain ? ' Προτείνεται αδιάβροχη.' : '') }; } if (effective 3e 5) { return { icon: '🧥', title: needsRain ? 'Μεσαία αδιάβροχη κουβέρτα' : 'Μεσαία κουβέρτα (100g)', warmth: 2, detail: 'Ψυχρή νύχτα (' + round(minTemp) + ' °C). Μια γέμιση 100g το κρατάει ζεστό.' + (wind 3e 20 ? ' Προσοχή στον αέρα!' : '') }; } if (effective 3e -2) { return { icon: '❄️', title: 'Ζεστή χειμωνιάτικη κουβέρτα (200g)', warmth: 3, detail: 'Κρύα νύχτα (' + round(minTemp) + ' °C). Προτείνεται γέμιση 200g.' + (needsRain ? ' Σημαντικό να είναι αδιάβροχη!' : '') }; } if (effective 3e -10) { return { icon: '🥶', title: 'Χοντρή χειμωνιάτικη κουβέρτα (300g)', warmth: 4, detail: 'Πολύ κρύα νύχτα (' + round(minTemp) + ' °C). Τουλάχιστον 300g, πιθανώς με κάλυμμα λαιμού.' }; } return { icon: '🧊', title: 'Μέγιστη προστασία (400g+)', warmth: 5, detail: 'Ακραίο κρύο (' + round(minTemp) + ' °C)! Προτείνεται 400g με κάλυμμα λαιμού και εσωτερική κουβέρτα.' }; } function weatherItem(icon, label, value) { return '\u0033cdiv class="weather-item"\u0033e\u0033cspan class="weather-item-icon"\u0033e' + icon + '\u0033c/span\u0033e' + '\u0033cdiv class="weather-item-info"\u0033e\u0033cdiv class="weather-item-label"\u0033e' + label + '\u0033c/div\u0033e' + '\u0033cdiv class="weather-item-value"\u0033e' + value + '\u0033c/div\u0033e\u0033c/div\u0033e\u0033c/div\u0033e'; } function weatherIcon(code) { if (code 3c= 1) return '☀️'; if (code 3c= 3) return '⛅'; if (code 3c= 48) return '🌫️'; if (code 3c= 67) return '🌧️'; if (code 3c= 77) return '🌨️'; if (code 3c= 82) return '🌧️'; if (code 3c= 86) return '❄️'; return '⛈️'; } function weatherLabel(code) { if (code 3c= 1) return 'Αίθριος'; if (code 3c= 3) return 'Συννεφιά'; if (code 3c= 48) return 'Ομίχλη'; if (code 3c= 57) return 'Ψιχάλα'; if (code 3c= 67) return 'Βροχή'; if (code 3c= 77) return 'Χιόνι'; if (code 3c= 82) return 'Μπόρα'; if (code 3c= 86) return 'Χιονομπόρα'; return 'Καταιγίδα'; } function avg(arr) { return arr.reduce(function(a, b) { return a + b; }, 0) / arr.length; } function round(n) { return Math.round(n * 10) / 10; } function show(id) { document.getElementById(id).classList.add('visible'); } function hide(id) { document.getElementById(id).classList.remove('visible'); } window.resetForm = function() { hide('result-card'); hide('error-msg'); document.getElementById('form-section').scrollIntoView({ behavior: 'smooth' }); }; window.copyEmbed = function() { var ta = document.getElementById('embed-code'); ta.select(); document.execCommand('copy'); var btn = ta.nextElementSibling; btn.textContent = '✓ Αντιγράφηκε!'; setTimeout(function() { btn.textContent = 'Αντιγραφή κώδικα'; }, 2000); }; })(); 3c/script3e 3c/div3e