Τι κουβέρτα χρειάζεται το άλογό σου απόψε?
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