(function (){
'use strict';
var heroes=document.querySelectorAll('.awh-hero[data-awh]');
if(!heroes.length) return;
var reduceMotion=window.matchMedia&&window.matchMedia('(prefers-reduced-motion: reduce)').matches;
function classify(code, isDay){
var c=parseInt(code, 10);
if(c===0) return { k: isDay ? 'clear-day':'clear-night', t: isDay ? 'Clear':'Clear night' };
if(c===1||c===2) return { k: isDay ? 'partly-day':'partly-night', t: 'Partly cloudy' };
if(c===3) return { k: 'cloudy', t: 'Cloudy' };
if(c===45||c===48) return { k: 'fog', t: 'Fog' };
if(c >=51&&c <=57) return { k: 'rain', t: 'Drizzle' };
if(c >=61&&c <=67) return { k: 'rain', t: 'Rain' };
if(c >=71&&c <=77) return { k: 'snow', t: 'Snow' };
if(c >=80&&c <=82) return { k: 'rain', t: 'Showers' };
if(c >=85&&c <=86) return { k: 'snow', t: 'Snow showers' };
if(c >=95) return { k: 'storm', t: 'Thunderstorm' };
return { k: 'cloudy', t: '\u2014' };}
function fillCard(hero, cfg, data){
var cur=data.current, day=data.daily;
var isDay=cur.is_day===1;
var cond=classify(cur.weather_code, isDay);
var unit=cfg.units==='fahrenheit' ? '\u00B0F':'\u00B0C';
applyCondition(hero, cond.k);
try {
var saveKey='awh:last:' + (cfg.mode==='ip' ? 'ip':(cfg.city||(cfg.lat + ',' + cfg.lng)));
localStorage.setItem(saveKey, cond.k);
} catch (e){}
var q=function (s){ return hero.querySelector(s); };
if(q('.awh-card__cond')) q('.awh-card__cond').textContent=cond.t;
if(q('.awh-card__val')) q('.awh-card__val').textContent=Math.round(cur.temperature_2m);
if(q('.awh-card__unit')) q('.awh-card__unit').textContent=unit;
if(q('.awh-card__feels')) q('.awh-card__feels').textContent='Feels ' + Math.round(cur.apparent_temperature) + '\u00B0';
if(day&&q('.awh-card__hi')) q('.awh-card__hi').textContent='H ' + Math.round(day.temperature_2m_max[0]) + '\u00B0';
if(day&&q('.awh-card__lo')) q('.awh-card__lo').textContent='L ' + Math.round(day.temperature_2m_min[0]) + '\u00B0';
if(cfg.city&&q('.awh-card__city')) q('.awh-card__city').textContent=cfg.city;
if(q('.awh-card__time')){
try { q('.awh-card__time').textContent=new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });} catch (e){}}
hero.classList.add('awh-loaded');
startParticles(hero, cond.k);
}
function applyCondition(hero, key){
hero.setAttribute('data-condition', key);
hero.classList.add('awh-resolved');
}
function fetchWeather(hero, cfg, lat, lng, attempt){
attempt=attempt||1;
var maxAttempts=4;
var url='https://api.open-meteo.com/v1/forecast'
+ '?latitude=' + encodeURIComponent(lat)
+ '&longitude=' + encodeURIComponent(lng)
+ '&current=temperature_2m,apparent_temperature,is_day,weather_code'
+ '&daily=temperature_2m_max,temperature_2m_min'
+ '&temperature_unit=' + (cfg.units==='fahrenheit' ? 'fahrenheit':'celsius')
+ '&timezone=auto&forecast_days=1';
var controller=(typeof AbortController!=='undefined') ? new AbortController():null;
var timer=controller ? setTimeout(function (){ controller.abort(); }, 9000):null;
fetch(url, controller ? { signal: controller.signal }:undefined)
.then(function (r){
if(timer) clearTimeout(timer);
if(!r.ok){ throw new Error('HTTP ' + r.status); }
return r.json();
})
.then(function (d){
if(d&&d.current){
hero._awhData=d;
hero._awhFails=0;
fillCard(hero, cfg, d);
}else{
throw new Error('empty payload');
}})
.catch(function (){
if(timer) clearTimeout(timer);
if(attempt < maxAttempts){
var delay=Math.min(1000 * Math.pow(2, attempt - 1), 8000);
setTimeout(function (){ fetchWeather(hero, cfg, lat, lng, attempt + 1); }, delay);
}else if(hero._awhData){
fillCard(hero, cfg, hero._awhData);
}});
}
function pauseParticles(hero){
if(hero&&hero._awhRAF){ cancelAnimationFrame(hero._awhRAF); hero._awhRAF=null; hero._awhPaused=true; }}
function resumeParticles(hero){
if(hero&&hero._awhPaused&&hero._awhTick){ hero._awhPaused=false; hero._awhRAF=requestAnimationFrame(hero._awhTick); }}
function startParticles(hero, cond){
var canvas=hero.querySelector('.awh-canvas');
if(!canvas) return;
if(hero._awhRAF){ cancelAnimationFrame(hero._awhRAF); hero._awhRAF=null; }
hero._awhTick=null; hero._awhPaused=false;
var ctx=canvas.getContext('2d');
if(!ctx) return;
var mode=(cond==='rain'||cond==='storm') ? 'rain':(cond==='snow' ? 'snow':null);
var saveData=navigator.connection&&navigator.connection.saveData;
if(!mode||reduceMotion||saveData){ ctx.clearRect(0, 0, canvas.width, canvas.height); return; }
var dpr=Math.min(window.devicePixelRatio||1, 2);
function resize(){
canvas.width=hero.clientWidth * dpr;
canvas.height=hero.clientHeight * dpr;
}
resize();
var w=canvas.width, h=canvas.height;
var isSmall=hero.clientWidth < 700;
var base=mode==='rain' ? (isSmall ? 90:220):(isSmall ? 60:140);
var count=Math.min(base, Math.floor(w / (mode==='rain' ? 8:12)));
var P=[];
for (var i=0; i < count; i++){
P.push(mode==='rain'
? { x: Math.random() * w, y: Math.random() * h, l: (8 + Math.random() * 14) * dpr, v: (10 + Math.random() * 8) * dpr }
: { x: Math.random() * w, y: Math.random() * h, r: (1.4 + Math.random() * 2.2) * dpr, v: (1.1 + Math.random() * 1.6) * dpr, d: Math.random() * Math.PI * 2 });
}
var slant=2.2 * dpr;
function frame(){
ctx.clearRect(0, 0, w, h);
if(mode==='rain'){
ctx.strokeStyle='rgba(180,205,230,0.45)';
ctx.lineWidth=1.1 * dpr;
ctx.beginPath();
for (var i=0; i < P.length; i++){
var p=P[i];
ctx.moveTo(p.x, p.y);
ctx.lineTo(p.x + slant, p.y + p.l);
p.x +=slant; p.y +=p.v;
if(p.y > h){ p.y=-p.l; p.x=Math.random() * w; }}
ctx.stroke();
}else{
ctx.fillStyle='rgba(255,255,255,0.85)';
for (var j=0; j < P.length; j++){
var s=P[j];
s.d +=0.01;
s.x +=Math.sin(s.d) * 0.6 * dpr;
s.y +=s.v;
if(s.y > h){ s.y=-s.r; s.x=Math.random() * w; }
ctx.beginPath();
ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2);
ctx.fill();
}}
hero._awhRAF=requestAnimationFrame(frame);
}
hero._awhTick=frame;
if(hero._awhVisible!==false&&!document.hidden){
frame();
}else{
hero._awhPaused=true;
}
if(hero._awhResize) window.removeEventListener('resize', hero._awhResize);
hero._awhResize=function (){ resize(); w=canvas.width; h=canvas.height; };
window.addEventListener('resize', hero._awhResize);
}
var saveData=navigator.connection&&navigator.connection.saveData;
heroes.forEach(function (hero){
var cfg;
try { cfg=JSON.parse(hero.getAttribute('data-awh')); } catch (e){ return; }
hero._awhCfg=cfg;
hero._awhFails=0;
try {
var memKey='awh:last:' + (cfg.mode==='ip' ? 'ip':(cfg.city||(cfg.lat + ',' + cfg.lng)));
var remembered=localStorage.getItem(memKey);
if(remembered){ applyCondition(hero, remembered); }} catch (e){}
function loadOnce(lat, lng){ fetchWeather(hero, cfg, lat, lng); }
function loadByIP(){
var controller=(typeof AbortController!=='undefined') ? new AbortController():null;
var timer=controller ? setTimeout(function (){ controller.abort(); }, 6000):null;
fetch('https://ipapi.co/json/', controller ? { signal: controller.signal }:undefined)
.then(function (r){ if(timer) clearTimeout(timer); if(!r.ok) throw 0; return r.json(); })
.then(function (d){
if(d&&d.latitude!=null&&d.longitude!=null){
hero._awhLat=d.latitude;
hero._awhLng=d.longitude;
if(d.city){ cfg.city=d.city; var el=hero.querySelector('.awh-card__city'); if(el) el.textContent=d.city; }
loadOnce(d.latitude, d.longitude);
}else{ throw 0; }})
.catch(function (){
if(timer) clearTimeout(timer);
hero._awhLat=cfg.lat; hero._awhLng=cfg.lng;
loadOnce(cfg.lat, cfg.lng);
});
}
if(cfg.mode==='ip'){
loadByIP();
}else if(cfg.mode==='visitor'&&navigator.geolocation){
navigator.geolocation.getCurrentPosition(function (pos){ hero._awhLat=pos.coords.latitude; hero._awhLng=pos.coords.longitude; loadOnce(hero._awhLat, hero._awhLng); },
function (){ hero._awhLat=cfg.lat; hero._awhLng=cfg.lng; loadOnce(cfg.lat, cfg.lng); },
{ timeout: 8000, maximumAge: 600000 }
);
}else{
hero._awhLat=cfg.lat; hero._awhLng=cfg.lng;
loadOnce(cfg.lat, cfg.lng);
}
setInterval(function (){
if(document.hidden) return;
if(hero._awhLat!=null) fetchWeather(hero, cfg, hero._awhLat, hero._awhLng);
}, 600000);
setInterval(function (){
var t=hero.querySelector('.awh-card__time');
if(t){ try { t.textContent=new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });} catch (e){}}
}, 60000);
if('IntersectionObserver' in window){
var io=new IntersectionObserver(function (entries){
entries.forEach(function (en){
hero._awhVisible=en.isIntersecting;
if(en.isIntersecting){ resumeParticles(hero); }else{ pauseParticles(hero); }});
}, { threshold: 0.05 });
io.observe(hero);
}else{
hero._awhVisible=true;
}});
document.addEventListener('visibilitychange', function (){
heroes.forEach(function (hero){
if(document.hidden){ pauseParticles(hero); }
else if(hero._awhVisible!==false){ resumeParticles(hero); }});
});
})();