PageSourceSearch

https://afghanistan.af/assets/js/main.js?v=1031

js afghanistan.af collected 2026-10-03 01:07:32 UTC 103,458 bytes, 1,790 lines download raw bytes

1/* Afghan Portal v2 — Main JS */
2
3// ── PANEL MANAGER ──────────────────────────────
4const Panel = {
5  open(id) {
6    document.getElementById('p-'+id)?.classList.add('open');
7    document.body.style.overflow = 'hidden';
8    if (id === 'news')       News.load();
9    if (id === 'prayer')     Prayer.load();
10    if (id === 'weather')    loadWeather();
11  if (id === 'jobs')       Jobs.load();
12    if (id === 'flights')    setTimeout(()=>FL.tab(document.getElementById('tab-dep'),'dep'),100);
13    if (id === 'airquality') loadAQI();
14    if (id === 'quran')      Quran.loadAyah();
15    if (id === 'dict')       setTimeout(()=>document.getElementById('dict-input')?.focus(),200);
16    if (id === 'calendar')   setTimeout(()=>showCalTab(document.querySelector('#cal-tabs .ctab'),'national'),100);
17    if (id === 'distance')   setTimeout(()=>Dist.init(),100);
18    if (id === 'compass')    setTimeout(Compass.drawBg, 50);
19  if (id === 'map')        setTimeout(() => MapAF.init(), 100);
20    if (id === 'unitconv')   UnitConv.init();
21  },
22  close(id) {
23    document.getElementById('p-'+id)?.classList.remove('open');
24    document.body.style.overflow = '';
25    if (id === 'compass') Compass.stop();
26  }
27};
28// close on backdrop click
29document.addEventListener('click', e => {
30  if (e.target.classList.contains('ov')) {
31    e.target.classList.remove('open');
32    document.body.style.overflow = '';
33  }
34});
35// expose globally for onclick attributes
36window.openP  = id => Panel.open(id);
37window.closeP = id => Panel.close(id);
38
39// ── CLOCK (ساعت کابل UTC+4:30) ──────────────────
40const p2 = n => String(n).padStart(2,'0');
41function tickKabul() {
42  const now = new Date();
43  // UTC milliseconds + 4:30 hours
44  const kabulMs = now.getTime() + (4 * 60 + 30) * 60 * 1000;
45  const k = new Date(kabulMs);
46  const el = document.getElementById('clk');
47  if (el) el.textContent = p2(k.getUTCHours())+':'+p2(k.getUTCMinutes())+':'+p2(k.getUTCSeconds());
48}
49tickKabul();
50setInterval(tickKabul, 1000);
51
52// ── AJAX ───────────────────────────────────────
53async function api(act, data = {}) {
54  const fd = new FormData();
55  fd.append('act', act);
56  Object.entries(data).forEach(([k,v]) => fd.append(k, v));
57  const r = await fetch('api.php', { method:'POST', body:fd });
58  return r.json();
59}
60
61// ── FX CONVERTER ──────────────────────────────
62window.calcFx = () => {
63  const a = parseFloat(document.getElementById('fxa').value)||0;
64  const r = parseFloat(document.getElementById('fxf').value)||1;
65  document.getElementById('fxr').textContent = (a*r).toLocaleString('fa',{maximumFractionDigits:0})+' افغانی';
66};
67window.setFx  = v => { document.getElementById('fxa').value = v; calcFx(); };
68window.calcPFx = () => {
69  const a = parseFloat(document.getElementById('pfxa').value)||0;
70  const r = parseFloat(document.getElementById('pfxf').value)||1;
71  document.getElementById('pfxr').textContent = (a*r).toLocaleString('fa',{maximumFractionDigits:0})+' افغانی';
72};
73window.setPFx = v => { document.getElementById('pfxa').value = v; calcPFx(); };
74
75// ── NEWS ───────────────────────────────────────
76const News = {
77  loaded: false,
78  async load() {
79    if (this.loaded) return;
80    const wrap = document.getElementById('news-wrap');
81    if (!wrap) return;
82    wrap.innerHTML = '<div style="padding:30px;text-align:center;color:var(--g);font-weight:700;font-size:14px">⏳ در حال بارگذاری اخبار...</div>';
83    try {
84      const d = await api('news');
85      if (!d.ok || !d.items?.length) throw 0;
86      this.render(d.items, wrap);
87      this.loaded = true;
88    } catch {
89      wrap.innerHTML = `<div style="padding:24px;text-align:center">
90        <div style="font-size:13px;color:var(--t2);margin-bottom:12px">خطا در بارگذاری اخبار</div>
91        <a href="https://shahed.news" target="_blank" style="background:var(--g);color:#fff;padding:9px 20px;border-radius:8px;font-size:13px;font-weight:800;text-decoration:none">مشاهده در shahed.news ←</a>
92      </div>`;
93    }
94  },
95  render(items, wrap) {
96    const td = ts => {
97      const d = Math.floor(Date.now()/1000 - ts);
98      if (d <   60) return d+'ث قبل';
99      if (d < 3600) return Math.floor(d/60)+'د قبل';
100      if (d <86400) return Math.floor(d/3600)+'س قبل';
101      return Math.floor(d/86400)+'ر قبل';
102    };
103    const catColor = c => {
104      const m = {'سیاسی':'#dc2626','اقتصادی':'#16a34a','اجتماعی':'#2563eb','ورزشی':'#d97706','تکنالوژی':'#7c3aed','بین‌الملل':'#0891b2'};
105      for (const k in m) if (c?.includes(k)) return m[k];
106      return '#1D9E75';
107    };
108
109    const main = items[0];
110    const rest = items.slice(1);
111
112    wrap.innerHTML = `
113    <div style="padding:14px;display:flex;flex-direction:column;gap:10px">
114
115      <!-- خبر اصلی -->
116      <div onclick="window.open('${main.link}','_blank')" style="background:linear-gradient(160deg,#0a3d2b,#1D9E75);border-radius:14px;padding:18px;
116cursor:pointer;position:relative;overflow:hidden;min-height:130px;display:flex;flex-direction:column;justify-content:flex-end">
117        <div style="position:absolute;top:0;right:0;font-size:80px;opacity:.08;line-height:1;padding:5px">📰</div>
118        ${main.cat ? `<div style="font-size:10px;font-weight:800;background:rgba(255,255,255,.2);color:#fff;padding:3px 10px;border-radius:20px;display:inline-block;margin-bottom:8px;width:fit-content">${main.cat}</div>` : ''}
119        <div style="font-size:16px;font-weight:900;color:#fff;line-height:1.5;margin-bottom:6px">${main.title}</div>
120        <div style="font-size:11px;color:rgba(255,255,255,.65);display:flex;align-items:center;gap:5px">
121          <span>📰 خبرگزاری شاهد</span><span>·</span><span>${td(main.ts)}</span>
122        </div>
123      </div>
124
125      <!-- خبرهای دوم و سوم -->
126      <div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">
127        ${rest.slice(0,2).map(n=>`
128        <div onclick="window.open('${n.link}','_blank')" style="background:#fff;border:1.5px solid var(--bdr);border-radius:12px;padding:13px;cursor:pointer;transition:.15s;display:flex;flex-direction:column;justify-content:space-between;min-height:100px" onmouseover="this.style.borderColor='var(--g)';this.style.background='var(--gl)'" onmouseout="this.style.borderColor='var(--bdr)';this.style.background='#fff'">
129          <div style="font-size:13px;font-weight:800;color:var(--t1);line-height:1.5;margin-bottom:8px">${n.title}</div>
130          <div style="font-size:10px;color:var(--t3);font-weight:600">${td(n.ts)} · شاهد</div>
131        </div>`).join('')}
132      </div>
133
134      <!-- لیست خبرها -->
135      <div style="display:flex;flex-direction:column;gap:8px">
136        ${rest.slice(2).map((n,i)=>`
137        <div onclick="window.open('${n.link}','_blank')" style="background:#fff;border:1px solid var(--bdr);border-radius:10px;padding:13px 14px;cursor:pointer;display:flex;align-items:flex-start;gap:12px;transition:.15s" onmouseover="this.style.borderColor='var(--g)';this.style.background='#f0faf5'" onmouseout="this.style.borderColor='var(--bdr)';this.style.background='#fff'">
138          <div style="width:26px;height:26px;background:var(--gl);border-radius:7px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:900;color:var(--gd);flex-shrink:0">${i+1}</div>
139          <div style="flex:1;min-width:0">
140            <div style="font-size:13px;font-weight:800;color:var(--t1);line-height:1.5;margin-bottom:4px">${n.title}</div>
141            <div style="font-size:11px;color:var(--t3);font-weight:600">${td(n.ts)} · خبرگزاری شاهد</div>
142          </div>
143          <div style="color:var(--t3);font-size:16px;flex-shrink:0">←</div>
144        </div>`).join('')}
145      </div>
146
147      <!-- لینک به سایت شاهد -->
148      <a href="https://shahed.news" target="_blank" style="display:block;text-align:center;background:var(--gl);color:var(--gd);padding:11px;border-radius:10px;font-size:13px;font-weight:800;border:1.5px solid var(--gm);text-decoration:none">
149        مشاهده همه اخبار در shahed.news ←
150      </a>
151
152    </div>`;
153  }
154};
155
156// ── AI ─────────────────────────────────────────
157const AI = {
158  async ask(q, sys='') {
159    const ld  = document.getElementById('aild');
160    const ans = document.getElementById('aans');
161    ld.style.display='flex'; ans.style.display='none';
162    try {
163      const d = await api('ai', { q, sys });
164      ans.textContent = d.t || 'خطا';
165    } catch {
166      ans.textContent = 'خطا در اتصال';
167    }
168    ld.style.display='none'; ans.style.display='block';
169  }
170};
171window.doAI   = () => { const q=document.getElementById('aiInp').value.trim(); if(q) AI.ask(q); };
172window.aiChip = q  => { document.getElementById('aiInp').value=q; AI.ask(q); };
173window.aiAsk  = q  => { Panel.open('ai'); document.getElementById('aiInp').value=q; AI.ask(q); };
174document.addEventListener('keydown', e => {
175  if (e.target.id==='aiInp' && e.key==='Enter' && !e.shiftKey) { e.preventDefault(); doAI(); }
176});
177
178// ── TRANSLATE ──────────────────────────────────
179let tlF='dari', tlT='english';
180const TL_NAMES = {dari:'دری',pashto:'پشتو',english:'English',arabic:'العربية'};
181window.setFL = (btn,l) => { btn.closest('.ctabs').querySelectorAll('.ctab').forEach(b=>b.classList.remove('on')); btn.classList.add('on'); tlF=l; };
182window.setTL = (btn,l) => { btn.closest('.ctabs').querySelectorAll('.ctab').forEach(b=>b.classList.remove('on')); btn.classList.add('on'); tlT=l; };
183window.doTrans = async () => {
184  const text = document.getElementById('trIn').value.trim();
185  if (!text) return;
186  const ld=document.getElementById('trld'), out=document.getElementById('trout');
187  ld.style.display='flex'; out.style.display='none';
188  try {
189    const d = await api('tr', {text, from:tlF, to:tlT});
190    out.textContent = d.t;
191  } catch { out.textContent='خطا'; }
192  ld.style.display='none'; out.style.display='block';
193};
194
195// ── CALCULATOR ─────────────────────────────────
196const Calc = (() => {
197  const fa = n => n.toString().replace(/\d/g, d=>'Û°Û±Û²Û³Û´ÛµÛ¶Û·Û¸Û¹'[d]);
198  let expr='', cur='0', op='', prev='', isNew=true;
199  const upd = () => {
200    document.getElementById('cval').textContent  = fa(cur);
201    document.getElementById('cexpr').textContent = expr ? fa(expr) : '';
202  };
203  const calc = (a,b,o) => ({'+':a+b,'-':a-b,'*':a*b,'/':b?a/b:0}[o]??b);
204  const sym  = o => ({'+':' + ','-':' − ','*':' × ','/':' ÷ '}[o]||o+' ');
205  return {
206    num(n) { cur = isNew ? n : (cur==='0'?n:cur+n); isNew=false; upd(); },
207    dot()  { if(isNew){cur='0.';isNew=false;}else if(!cur.includes('.'))cur+='.'; upd(); },
208    op(o)  { if(prev&&!isNew){const r=calc(+prev,+cur,op);cur=String(Math.round(r*1e10)/1e10);expr=fa(cur)+sym(o);}else expr=fa(cur)+sym(o);prev=cur;op=o;isNew=true;upd(); },
209    eq()   { if(!prev||!op)return;const r=calc(+prev,+cur,op);expr=fa(prev)+sym(op)+fa(cur)+' =';cur=String(Math.round(r*1e10)/1e10);prev='';op='';isNew=true;upd(); },
210    ac()   { cur='0';prev='';op='';expr='';isNew=true;upd(); },
211    sign() { cur=String(-parseFloat(cur)||0);upd(); },
212    pct()  { cur=String(parseFloat(cur)/100);upd(); },
213  };
214})();
215window.cNum=n=>Calc.num(n);
215 window.cDot=()=>Calc.dot(); window.cOp=o=>Calc.op(o);
216window.cEq=()=>Calc.eq();   window.cAC=()=>Calc.ac();   window.cSign=()=>Calc.sign(); window.cPct=()=>Calc.pct();
217
218// ── UNIT CONVERTER ─────────────────────────────
219const UnitConv = (() => {
220  const CATS = {
221    length: {l:'طول',u:[['متر','m',1],['کیلومتر','km',1000],['سانتی‌متر','cm',.01],['میلی‌متر','mm',.001],['مایل','mi',1609.34],['فوت','ft',.3048],['اینچ','in',.0254],['من (افغانی)','man',4.5],['جریب','jerib',~0]]},
222    weight: {l:'وزن',u:[['کیلوگرام','kg',1],['گرام','g',.001],['تن','t',1000],['پوند','lb',.453592],['اونس','oz',.028349],['من (افغانی)','man',4.5],['سیر','sir',.281]]},
223    area:   {l:'مساحت',u:[['متر مربع','m²',1],['کیلومتر مربع','km²',1e6],['هکتار','ha',1e4],['جریب (افغانی)','jerib',2000],['فوت مربع','ft²',.092903],['مایل مربع','mi²',2.59e6]]},
224    volume: {l:'حجم',u:[['لیتر','L',1],['میلی‌لیتر','mL',.001],['متر مکعب','m³',1000],['گالن','gal',3.78541],['پاینت','pt',.473176]]},
225    speed:  {l:'سرعت',u:[['کیلومتر/ساعت','km/h',1],['متر/ثانیه','m/s',3.6],['مایل/ساعت','mph',1.60934],['نات','kn',1.852],['ماخ','Mach',1234.8]]},
226    temp:   {l:'دما',u:[['سلسیوس','°C',0],['فارنهایت','°F',0],['کلوین','K',0]]},
227  };
228  let cur = 'length';
229  const convTemp = (v,f,t) => {
230    let c = f==='°F'?(v-32)*5/9:f==='K'?v-273.15:v;
231    return t==='°F'?c*9/5+32:t==='K'?c+273.15:c;
232  };
233  const fmt = n => Math.abs(n)>=.001&&Math.abs(n)<1e9?+n.toPrecision(6):n.toExponential(3);
234
235  return {
236    init() { this.setCat(document.querySelector('#uc-cats .ctab'), 'length'); },
237    setCat(btn, cat) {
238      btn?.closest('.ctabs')?.querySelectorAll('.ctab').forEach(b=>b.classList.remove('on'));
239      btn?.classList.add('on'); cur = cat;
240      const u = CATS[cat].u;
241      ['uc-from','uc-to'].forEach((id,i) => {
242        document.getElementById(id).innerHTML = u.map((x,j)=>`<option value="${j}">${x[0]} (${x[1]})</option>`).join('');
243        if(i===1) document.getElementById(id).selectedIndex=1;
244      });
245      this.calc();
246    },
247    calc() {
248      const cat = CATS[cur];
249      const v = parseFloat(document.getElementById('uc-val').value)||0;
250      const fu = cat.u[+document.getElementById('uc-from').value||0];
251      const tu = cat.u[+document.getElementById('uc-to').value||0];
252      const res = cur==='temp' ? convTemp(v,fu[1],tu[1]) : v*fu[2]/tu[2];
253      document.getElementById('uc-res').textContent = fmt(res)+' '+tu[1];
254      const rows = cat.u.slice(0,6).map(u => {
255        const r = cur==='temp'?convTemp(v,fu[1],u[1]):v*fu[2]/u[2];
256        return `<div class="mi"><div class="min">${u[0]}</div><div class="miv" style="font-size:13px">${fmt(r)} ${u[1]}</div></div>`;
257      }).join('');
258      document.getElementById('uc-table').innerHTML = `<div class="fl" style="margin-bottom:6px">جدول سریع:</div><div class="mkt" style="padding:0">${rows}</div>`;
259    }
260  };
261})();
262window.selUCat = (btn,cat) => UnitConv.setCat(btn,cat);
263window.calcUC  = () => UnitConv.calc();
264
265// ── DATE CONVERTER ─────────────────────────────
266const DateConv = (() => {
267  const SH = ['حمل','ثور','جوزا','سرطان','اسد','سنبله','میزان','عقرب','قوس','جدی','دلو','حوت'];
268  const MI = ['January','February','March','April','May','June','July','August','September','October','November','December'];
269  const HJ = ['محرم','صفر','ربیع الاول','ربیع الثانی','جمادی الاول','جمادی الثانی','رجب','شعبان','رمضان','شوال','ذی القعده','ذی الحجه'];
270  const WD = ['یکشنبه','دوشنبه','سه‌شنبه','چهارشنبه','پنج‌شنبه','جمعه','شنبه'];
271
272  const sh2jd = (y,m,d) => {
273    const ep=1948320.5, cy=Math.floor((y-474)/2820)*2820+474;
274    return d+(m<=6?31*(m-1):30*(m-1)+6)+Math.floor((682*cy-110)/2816)+(cy-1)*365+Math.floor(cy/4)-Math.floor(cy/128)+Math.floor(cy/2816)+ep;
275  };
276  const jd2mi = jd => {
277    const l=Math.floor(jd+.5)+68569,n=Math.floor(4*l/146097),l2=l-Math.floor((146097*n+3)/4);
278    const y=Math.floor(4000*(l2+1)/1461001),l3=l2-Math.floor(1461*y/4)+31;
279    const m=Math.floor(80*l3/2447),d=l3-Math.floor(2447*m/80);
280    return {y:100*(n-49)+y+Math.floor(m/14), m:m+2-12*Math.floor(m/14), d};
281  };
282  const mi2jd = (y,m,d) => 367*y-Math.floor(7*(y+Math.floor((m+9)/12))/4)+Math.floor(275*m/9)+d+1721013.5;
283  const jd2sh = jd => {
284    const ep=1948320.5,dep=Math.floor(jd+.5)-ep,cyc=Math.floor(dep/1029983),cy=dep%1029983;
285    const yr=cy===1029982?2820:Math.floor((2816*cy+1031337)/1028522),y=yr+2820*cyc+474;
286    let rem=dep-sh2jd(yr+474,1,1)+1,m=1,d=rem;
287    const days=[31,31,31,31,31,31,30,30,30,30,30,29];
288    for(let i=0;i<12;i++){if(d<=days[i])break;d-=days[i];m++;}
289    return{y,m,d};
290  };
291  const mi2hj_approx = (gy,gm,gd) => {
292    const jd=mi2jd(gy,gm,gd);
293    const l=Math.floor(jd+.5)-1948440+10632, n=Math.floor((l-1)/10631), l2=l-10631*n+354;
294    const j=Math.floor((10985-l2)/5316)*Math.floor(50*l2/17719)-Math.floor(l2/5670)*Math.floor(43*l2/15238);
295    const l3=l2-Math.floor((30-j)/15)*Math.floor(17719*j/50)+Math.floor(j/16)*Math.floor(15238*j/43);
296    const hd=Math.floor(l3*30/1015), hm=Math.min(12,hd<15?1:Math.ceil((hd-14.5)/29.5));
297    return {y:30*n+j-1, m:hm, d:Math.max(1,hd-29*(hm-1))};
298  };
299  const show = (boxId,val,sub='') => {
300    const box=document.getElementById(boxId);
301    box.querySelector('.res-val').textContent=val;
302    box.querySelector('.res-sub').textContent=sub;
303    box.style.display='block';
304  };
305  const v = id => parseInt(document.getElementById(id).value)||0;
306
307  return {
308    switchTab(btn,tab) {
309      btn.closest('.ctabs').querySelectorAll('.ctab').forEach(b=>b.classList.remove('on')); btn.classList.add('on');
310      ['sh2mi','mi2sh','sh2hj','hj2sh'].forEach(t=>document.getElementById('dc-'+t).style.display='none');
311      document.getElementById('dc-'+tab).style.display='block';
312    },
313    sh2mi() { const mi=jd2mi(sh2jd(v('sh-y'),v('sh-m'),v('sh-d'))); show('dc-res1',`${mi.d} ${MI[mi.m-1]} ${mi.y}`,WD[new Date(mi.y,mi.m-1,mi.d).getDay()]); },
314    mi2sh() { const sh=jd2sh(mi2jd(v('mi-y'),v('mi-m'),v('mi-d'))); show('dc-res2',`${sh.d} ${SH[sh.m-1]} ${sh.y}`); },
315    sh2hj() { const mi=jd2mi(sh2jd(v('sh2-y'),v('sh2-m'),v('sh2-d'))); const hj=mi2hj_approx(mi.y,mi.m,mi.d); show('dc-res3',`${hj.d} ${HJ[hj.m-1]} ${hj.y}`); },
316    hj2sh() { const jd=Math.floor((11*v('hj-y')+3)/30)+354*v('hj-y')+30*v('hj-m')-Math.floor((v('hj-m')-1)/2)+v('hj-d')+1948440-385; const sh=jd2sh(jd+.5); show('dc-res4',`${sh.d} ${SH[sh.m-1]} ${sh.y}`); },
317  };
318})();
319window.selDcTab = (btn,tab) => DateConv.switchTab(btn,tab);
320window.convSh2Mi = () => DateConv.sh2mi();
321window.convMi2Sh = () => DateConv.mi2sh();
322window.convSh2Hj = () => DateConv.sh2hj();
323window.convHj2Sh = () => DateConv.hj2sh();
324
325// ── PRAYER (AlAdhan.com API) ──────────────────
326const Prayer = {
327  curLat: 34.5553, curLng: 69.2075,
328  curCity: 'کابل',
329  curMaz: 'hanafi',
330  NAMES: ['اذان صبح', 'طلوع آفتاب', 'ظهر', 'عصر', 'مغرب', 'عشا'],
331  KEYS:  ['Fajr', 'Sunrise', 'Dhuhr', 'Asr', 'Maghrib', 'Isha'],
332
333  async load() {
334    document.getElementById('prgrid').innerHTML =
335      '<div style="grid-column:span 3;text-align:center;padding:20px;color:var(--g);font-weight:700">⏳ در حال دریافت اوقات دقیق...</div>';
336
337    // AlAdhan API
338    // method=1: University of Islamic Sciences Karachi (مناسب افغانستان)
339    // method=7: Institute of Geophysics, University of Tehran (شیعه)
340    // school=1: Hanafi (برای عصر), school=0: Standard
341    const method = this.curMaz === 'hanafi' ? 1 : 7;
342    const school = this.curMaz === 'hanafi' ? 1 : 0;
343
344    const today = new Date();
345    const dd = String(today.getDate()).padStart(2,'0');
346    const mm = String(today.getMonth()+1).padStart(2,'0');
347    const yyyy = today.getFullYear();
348    const dateStr = `${dd}-${mm}-${yyyy}`;
349
350    const url = `https://api.aladhan.com/v1/timings/${dateStr}?latitude=${this.curLat}&longitude=${this.curLng}&method=${method}&school=${school}&timezonestring=Asia/Kabul`;
351
352    try {
353      const r = await fetch(url);
354      const d = await r.json();
355      if (d.code !== 200) throw new Error('API error');
356
357      const timings = d.data.timings;
358      const hijriDate = d.data.date.hijri;
359      const gregDate  = d.data.date.gregorian;
360
361      // آپدیت تاریخ
362      const dateEl = document.getElementById('pr-date');
363      if (dateEl) dateEl.textContent = `${hijriDate.day}
363 ${hijriDate.month.ar} ${hijriDate.year} · ${gregDate.date}`;
364
365      // پیدا کردن نماز بعدی
366      const nowMs = Date.now();
367      const kabulOffset = 4.5 * 60; // UTC+4:30
368      const nowMin = ((nowMs / 60000) % (24*60) + kabulOffset) % (24*60);
369
370      const times = this.KEYS.map(k => {
371        const t = timings[k]?.replace(' (AFT)', '') || '--';
372        if (t === '--') return -1;
373        const [h,m] = t.split(':').map(Number);
374        return h*60 + m;
375      });
376
377      let nextIdx = times.findIndex(t => t > nowMin);
378      if (nextIdx === -1) nextIdx = 0;
379
380      // رندر کارت‌ها
381      document.getElementById('prgrid').innerHTML = this.NAMES.map((name, i) => {
382        const t = this.KEYS[i] ? timings[this.KEYS[i]]?.replace(' (AFT)','') || '--' : '--';
383        const isNext = i === nextIdx && t !== '--';
384        const isSunrise = i === 1;
385        return `<div class="pri${isNext ? ' now' : ''}" style="${isSunrise ? 'opacity:.7' : ''}">
386          <div class="prn">${name}</div>
387          <div class="prt" style="font-size:${t.length > 5 ? '15px' : '19px'}">${t}</div>
388          ${isNext ? '<div class="prx">⏰ بعدی</div>' : ''}
389        </div>`;
390      }).join('');
391
392      // نماز بعدی
393      const nextEl = document.getElementById('pr-next');
394      const nextLbl = document.getElementById('pr-next-label');
395      if (nextEl && nextLbl && nextIdx >= 0) {
396        const nextTime = this.KEYS[nextIdx] ? timings[this.KEYS[nextIdx]]?.replace(' (AFT)','') : '';
397        nextEl.style.display = 'block';
398        nextLbl.textContent = `⏰ ${this.NAMES[nextIdx]}: ${nextTime}`;
399      }
400
401    } catch(e) {
402      document.getElementById('prgrid').innerHTML =
403        '<div style="grid-column:span 3;text-align:center;padding:20px;color:var(--r);font-size:13px;font-weight:700">خطا در دریافت اوقات — دوباره تلاش کنید</div>';
404      console.warn('Prayer API error:', e);
405    }
406  }
407};
408
409window.setPrayerCity = (btn, cityName, lat, lng) => {
410  btn.closest('.ctabs').querySelectorAll('.ctab').forEach(b => b.classList.remove('on'));
411  btn.classList.add('on');
412  Prayer.curLat = lat; Prayer.curLng = lng; Prayer.curCity = cityName;
413  document.getElementById('pr-city-name').textContent = cityName;
414  Prayer.load();
415};
416
417window.setPrayerMaz = (maz) => {
418  Prayer.curMaz = maz;
419  const hBtn = document.getElementById('maz-hanafi');
420  const sBtn = document.getElementById('maz-shia');
421  const label = document.getElementById('pr-maz-label');
422  if (maz === 'hanafi') {
423    hBtn.style.cssText = 'flex:1;padding:9px;background:var(--g);color:#fff;border:none;border-radius:9px;font-size:13px;font-weight:800;cursor:pointer';
424    sBtn.style.cssText = 'flex:1;padding:9px;background:#f5f7fb;color:var(--t2);border:1.5px solid var(--bdr);border-radius:9px;font-size:13px;font-weight:800;cursor:pointer';
425    if (label) label.textContent = 'حنفی';
426  } else {
427    sBtn.style.cssText = 'flex:1;padding:9px;background:var(--g);color:#fff;border:none;border-radius:9px;font-size:13px;font-weight:800;cursor:pointer';
428    hBtn.style.cssText = 'flex:1;padding:9px;background:#f5f7fb;color:var(--t2);border:1.5px solid var(--bdr);border-radius:9px;font-size:13px;font-weight:800;cursor:pointer';
429    if (label) label.textContent = 'جعفری';
430  }
431  Prayer.load();
432};
433
434
435// ── WEATHER (Open-Meteo API - رایگان بدون key) ─────
436const WX_CODES = {
437  0:'☀️ آفتابی',1:'🌤 کمی ابری',2:'⛅ نیمه‌ابری',3:'☁️ ابری',
438  45:'🌫 مه‌آلود',48:'🌫 مه یخ‌زده',51:'🌦 نم‌نم باران',53:'🌧 باران',55:'🌧 باران شدید',
439  61:'🌧 باران سبک',63:'🌧 باران متوسط',65:'🌧 باران سنگین',
440  71:'🌨 برف سبک',73:'❄️ برف',75:'❄️ برف سنگین',
441  80:'🌦 رگبار',81:'⛈ رگبار شدید',82:'⛈ رگبار طوفانی',
442  95:'⛈ طوفان',96:'⛈ طوفان با تگرگ',99:'⛈ طوفان شدید',
443};
444const DAYS_FA = ['یکشنبه','دوشنبه','سه‌شنبه','چهارشنبه','پنج‌شنبه','جمعه','شنبه'];
445
446async function loadWeather() {
447  const sel = document.getElementById('wx-city-select');
448  if (!sel) return;
449  const [lat, lng, cityName] = sel.value.split(',');
450
451  // آپدیت نام شهر
452  const nameEl = document.getElementById('wx-city-name');
453  if (nameEl) nameEl.textContent = cityName + '، افغانستان';
454
455  // آیکون loading
456  const iconEl = document.getElementById('wx-icon-main');
457  if (iconEl) iconEl.textContent = '⏳';
458
459  try {
460    const url = `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lng}&current=temperature_2m,relative_humidity_2m,apparent_temperature,weather_code,wind_speed_10m&daily=weather_
460code,temperature_2m_max,temperature_2m_min&timezone=Asia%2FKabul&forecast_days=7`;
461    const r = await fetch(url);
462    const d = await r.json();
463
464    const cur = d.current;
465    const daily = d.daily;
466
467    // آپدیت کارت اصلی
468    const temp = Math.round(cur.temperature_2m);
469    const feels = Math.round(cur.apparent_temperature);
470    const humid = cur.relative_humidity_2m;
471    const wind = Math.round(cur.wind_speed_10m);
472    const code = cur.weather_code;
473    const wxInfo = WX_CODES[code] || '🌡 نامشخص';
474    const [icon, desc] = wxInfo.split(' ');
475
476    document.getElementById('wx-temp-main').textContent = temp + '°';
477    document.getElementById('wx-desc-main').textContent = wxInfo;
478    document.getElementById('wx-humidity').textContent = '💧 ' + humid + '٪';
479    document.getElementById('wx-wind').textContent = '💨 ' + wind + ' ک/س';
480    document.getElementById('wx-feels').textContent = '🌡 احساس: ' + feels + '°';
481    if (iconEl) iconEl.textContent = icon || '🌡';
482
483    // رنگ کارت بر اساس دما
484    const card = document.getElementById('wx-main-card');
485    if (card) {
486      if (temp >= 30) card.style.background = 'linear-gradient(135deg,#b45309,#f59e0b)';
487      else if (temp >= 20) card.style.background = 'linear-gradient(135deg,#1d4ed8,#60a5fa)';
488      else if (temp >= 10) card.style.background = 'linear-gradient(135deg,#0d7c5e,#34d399)';
489      else card.style.background = 'linear-gradient(135deg,#1e3a5f,#6b8ccc)';
490    }
491
492    // پیش‌بینی ۷ روز
493    const forecastEl = document.getElementById('wx-forecast');
494    if (forecastEl && daily) {
495      forecastEl.innerHTML = daily.time.map((date, i) => {
496        const d2 = new Date(date);
497        const dayName = i === 0 ? 'امروز' : i === 1 ? 'فردا' : DAYS_FA[d2.getDay()];
498        const wx2 = WX_CODES[daily.weather_code[i]] || '🌡';
499        const [ic2] = wx2.split(' ');
500        const max = Math.round(daily.temperature_2m_max[i]);
501        const min = Math.round(daily.temperature_2m_min[i]);
502        return `<div style="display:flex;align-items:center;justify-content:space-between;padding:9px 12px;background:#f9fafb;border-radius:9px;border:1px solid var(--bdr)">
503          <div style="font-size:13px;font-weight:800;color:var(--t1);width:80px">${dayName}</div>
504          <div style="font-size:22px">${ic2}</div>
505          <div style="font-size:12px;color:var(--t3);flex:1;text-align:center">${wx2.replace(ic2,'').trim()}</div>
506          <div style="text-align:left">
507            <span style="font-size:14px;font-weight:900;color:#b91c1c">${max}°</span>
508            <span style="font-size:12px;color:var(--t3);margin-right:6px"> / ${min}°</span>
509          </div>
510        </div>`;
511      }).join('');
512    }
513
514  } catch(e) {
515    document.getElementById('wx-icon-main').textContent = '⚠️';
516    document.getElementById('wx-desc-main').textContent = 'خطا در دریافت اطلاعات';
517    console.warn('Weather error:', e);
518  }
519}
520
521// ── FLIGHTS ──────────────────────────────────────
522const FL = {
523  cur: 'KBL',
524  curTab: 'dep',
525  timer: null,
526  countdown: null,
527
528  AIRPORTS: {
529    KBL: {name:'فرودگاه بین‌المللی کابل',    iata:'KBL', icao:'OAKB', lat:34.5659, lng:69.2123},
530    HEA: {name:'فرودگاه بین‌المللی هرات',    iata:'HEA', icao:'OAHR', lat:34.2100, lng:61.5281},
531    MZR: {name:'فرودگاه بین‌المللی مزارشریف',iata:'MZR', icao:'OAMS', lat:36.7051, lng:67.2097},
532    KDH: {name:'فرودگاه بین‌المللی قندهار',  iata:'KDH', icao:'OAKN', lat:31.5058, lng:65.8478},
533  },
534
535  // برنامه پروازهای معمول
536  SCHEDULE: {
537    KBL: {
538      dep: [
539        {time:'06:30',flight:'FZ581', dest:'دبی (DXB)',       airline:'FlyDubai',       days:'روزانه',    status:'به موقع'},
540        {time:'07:15',flight:'AF610', dest:'دبی (DXB)',       airline:'Ariana Afghan',  days:'روزانه',    status:'به موقع'},
541        {time:'08:00',flight:'TK711', dest:'استانبول (IST)',  airline:'Turkish Airlines',days:'یک‌شنبه,سه‌شنبه,پنج‌شنبه',status:'به موقع'},
542        {time:'09:30',flight:'PK201', dest:'اسلام‌آباد (ISB)',airline:'PIA',            days:'روزانه',    status:'به موقع'},
543        {time:'10:00',flight:'W5001', dest:'تهران (IKA)',     airline:'Mahan Air',      days:'شنبه,دوشنبه,چهارشنبه',status:'به موقع'},
544        {time:'11:30',flight:'AI245', dest:'دهلی (DEL)',      airline:'Air India',      days:'دوشنبه,چهارشنبه,جمعه',status:'به موقع'},
545        {time:'13:00',flight:'FZ583', dest:'دبی (DXB)',       airline:'FlyDubai',       days:'روزانه',    status:'به موقع'},
546        {time:'14:30',flight:'EP401', dest:'مشهد (MHD)',      airline:'Iran Air',       days:'سه‌شنبه,جمعه',status:'به موقع'},
547        {time:'15:00',flight:'GF130', dest:'بحرین (BAH)',     airline:'Gulf Air',       days:'سه‌شنبه,شنبه',status:'به موقع'},
548        {time:'16:45',flight:'QR596', dest:'دوحه (DOH)',      airline:'Qatar Airways',  days:'روزانه',    status:'به موقع'},
549        {time:'18:00',flight:'EK610', dest:'دبی (DXB)',       airline:'Emirates',       days:'روزانه',    status:'به موقع'},
550        {time:'20:30',flight:'EY295', dest:'ابوظبی (AUH)',    airline:'Etihad',         days:'چهارشنبه,جمعه,یکشنبه',status:'به موقع'},
551      ],
552      arr: [
553        {time:'05:45',flight:'FZ580', from:'دبی (DXB)',       airline:'FlyDubai',       days:'روزانه',    status:'به موقع'},
554        {time:'06:30',flight:'TK710', from:'استانبول (IST)',  airline:'Turkish Airlines',days:'دوشنبه,چهارشنبه,جمعه',status:'به موقع'},
555        {time:'08:15',flight:'PK200', from:'اسلام‌آباد (ISB)',airline:'PIA',            days:'روزانه',    status:'به موقع'},
556        {time:'09:00',flight:'W5002', from:'تهران (IKA)',     airline:'Mahan Air',      days:'شنبه,دوشنبه,چهارشنبه',status:'به موقع'},
557        {time:'10:30',flight:'AI244', from:'دهلی (DEL)',      airline:'Air India',      days:'دوشنبه,چهارشنبه,جمعه',status:'به موقع'},
558        {time:'12:00',flight:'FZ582', from:'دبی (DXB)',       airline:'FlyDubai',       days:'روزانه',    status:'به موقع'},
559        {time:'13:30',flight:'EP400', from:'مشهد (MHD)',      airline:'Iran Air',       days:'سه‌شنبه,جمعه',status:'به موقع'},
560        {time:'15:45',flight:'QR597', from:'دوحه (DOH)',      airline:'Qatar Airways',  days:'روزانه',    status:'به موقع'},
561        {time:'17:00',flight:'EK611', from:'دبی (DXB)',       airline:'Emirates',       days:'روزانه',    status:'به موقع'},
562        {time:'19:30',flight:'EY296', from:'ابوظبی (AUH)',    airline:'Etihad',         days:'چهارشنبه,جمعه,یکشنبه',status:'به موقع'},
563      ]
564    },
565    HEA: {
566      dep: [
567        {time:'07:00',flight:'FZ591', dest:'دبی (DXB)',    airline:'FlyDubai',     days:'روزانه',          status:'به موقع'},
568        {time:'09:30',flight:'AF510', dest:'کابل (KBL)',   airline:'Ariana Afghan',days:'روزانه',          status:'به موقع'},
569        {time:'11:00',flight:'KM101', dest:'کابل (KBL)',   airline:'Kam Air',      days:'روزانه',          status:'به موقع'},
570        {time:'14:00',flight:'EP501', dest:'مشهد (MHD)',   airline:'Iran Air',     days:'دوشنبه,چهارشنبه,جمعه',status:'به موقع'},
571        {time:'16:30',flight:'FZ593', dest:'دبی (DXB)',    airline:'FlyDubai',     days:'سه‌شنبه,پنج‌شنبه',status:'به موقع'},
572      ],
573      arr: [
574        {time:'06:00',flight:'FZ590', from:'دبی (DXB)',    airline:'FlyDubai',     days:'روزانه',          status:'به موقع'},
575        {time:'08:30',flight:'AF511', from:'کابل (KBL)',   airline:'Ariana Afghan',days:'روزانه',          status:'به موقع'},
576        {time:'13:00',flight:'EP500', from:'مشهد (MHD)',   airline:'Iran Air',     days:'دوشنبه,چهارشنبه,جمعه',status:'به موقع'},
577        {time:'15:30',flight:'FZ592', from:'دبی (DXB)',    airline:'FlyDubai',     days:'سه‌شنبه,پنج‌شنبه',status:'به موقع'},
578      ]
579    },
580    MZR: {
581      dep: [
582        {time:'08:00',flight:'AF310', dest:'کابل (KBL)',   airline:'Ariana Afghan',days:'روزانه',          status:'به موقع'},
583        {time:'10:30',flight:'FZ595', dest:'دبی (DXB)',    airline:'FlyDubai',     days:'سه‌شنبه,پنج‌شنبه,شنبه',status:'به موقع'},
584        {time:'13:00',flight:'KM201', dest:'کابل (KBL)',   airline:'Kam Air',      days:'روزانه',          status:'به موقع'},
585      ],
586      arr: [
587        {time:'07:00',flight:'AF311', from:'کابل (KBL)',   airline:'Ariana Afghan',days:'روزانه',          status:'به موقع'},
588        {time:'09:30',flight:'FZ594', from:'دبی (DXB)',    airline:'FlyDubai',     days:'سه‌شنبه,پنج‌شنبه,شنبه',status:'به موقع'},
589        {time:'12:00',flight:'KM202', from:'کابل (KBL)',   airline:'Kam Air',      days:'روزانه',          status:'به موقع'},
590      ]
591    },
592    KDH: {
593      dep: [
594        {time:'09:00',flight:'AF410', dest:'کابل (KBL)',   airline:'Ariana Afghan',days:'روزانه',          status:'به موقع'},
595        {time:'12:00',flight:'FZ597', dest:'دبی (DXB)',    airline:'FlyDubai',     days:'دوشنبه,چهارشنبه,جمعه',status:'به موقع'},
596      ],
597      arr: [
598        {time:'08:00',flight:'AF411', from:'کابل (KBL)',   airline:'Ariana Afghan',days:'روزانه',          status:'به موقع'},
599        {time:'11:00',flight:'FZ596', from:'دبی (DXB)',    airline:'FlyDubai',     days:'دوشنبه,چهارشنبه,جمعه',status:'به موقع'},
600      ]
601    },
602  },
603
604  select(btn, iata) {
605    this.cur = iata;
606    document.querySelectorAll('.fl-ap-btn').forEach(b => b.classList.remove('fl-active'));
607    btn.classList.add('fl-active');
608    const ap = this.AIRPORTS[iata];
609    document.getElementById('fl-ap-name').textContent = ap.name;
610    document.getElementById('fl-iata').textContent = ap.iata;
611    document.getElementById('fl-icao').textContent = ap.icao;
612    this.showTab(this.curTab);
613  },
614
615  tab(btn, t) {
616    this.curTab = t;
617    ['dep','arr','live'].forEach(id => {
618      const b = document.getElementById('tab-' + id);
619      if (!b) return;
620      const active = id === t;
621      b.style.background = active ? 'var(--g)' : '#f5f7fb';
622      b.style.color = active ? '#fff' : 'var(--t2)';
623      b.style.border = active ? 'none' : '1.5px solid var(--bdr)';
624    });
625    this.showTab(t);
626  },
627
628  showTab(t) {
629    if (t === 'live') { this.loadLive(); return; }
630    const data = this.SCHEDULE[this.cur];
631    if (!data) return;
632    const flights = t === 'dep' ? data.dep : data.arr;
633    const isDep = t === 'dep';
634
635    // ساعت کابل
636    const now = new Date(Date.now() + 4.5*3600000);
637    const nowMin = now.getUTCHours()*60 + now.getUTCMinutes();
638
639    document.getElementById('fl-content').innerHTML = `
640      <div style="background:#f0f4ff;border-radius:9px;padding:9px 12px;margin-bottom:10px;font-size:11px;color:#1d4ed8;font-weight:700;border:1px solid #bfdbfe">
641        ⚠️ برنامه معمول — ممکن است در ایام خاص تغییر کند
642      </div>
643      ${flights.map(f => {
644        const [h,m] = f.time.split(':').map(Number);
645        const flMin = h*60+m;
646        const diff = flMin - nowMin;
647        const soon = diff > 0 && diff < 90;
648        const past = diff < 0 && diff > -60;
649        let badge = '';
650        if (soon) badge = `<span style="background:#fef9c3;color:#854d0e;font-size:10px;font-weight:800;padding:2px 7px;border-radius:4px;margin-right:6px">${diff}د دیگر</span>`;
651        else if (past) badge = `<span style="background:#fee2e2;color:#b91c1c;font-size:10px;font-weight:800;padding:2px 7px;border-radius:4px;margin-right:6px">پرواز کرد</span>`;
652        return `
653        <div style="background:#fff;border-radius:10px;padding:12px 14px;border:1.5px solid ${soon?'var(--a)':past?'#fecaca':'var(--bdr)'};margin-bottom:8px;${soon?'box-shadow:0 0 0 3px rgba(217,119,6,.1)':''}">
654          <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px">
655            <div style="display:flex;align-items:center;gap:8px">
656              <div style="font-size:22px;font-weight:900;color:var(--gd);direction:ltr">${f.time}</div>
657              <div>
658                <div style="font-size:12px;font-weight:900;color:var(--t1)">${f.flight}</div>
659                <div style="font-size:10px;color:var(--t3)">${f.airline}</div>
660              </div>
661            </div>
662            <div style="text-align:left">
663              ${badge}
664              <span style="font-size:11px;font-weight:700;color:#15803d;background:#dcfce7;padding:2px 8px;border-radius:4px">${f.status}</span>
665            </div>
666          </div>
667          <div style="display:flex;align-items:center;gap:6px;font-size:12px;color:var(--t2)">
668            <span>${isDep?'🛫':'🛬'}</span>
669            <span style="font-weight:700;color:var(--t1)">${isDep?f.dest:f.from}</span>
670            <span style="margin-right:auto;font-size:10px;color:var(--t3)">📅 ${f.days}</span>
671          </div>
672        </div>`;
673      }).join('')}
674      <div style="text-align:center;font-size:11px;color:var(--t3);margin-top:5px">بر اساس ساعت کابل · ${now.getUTCHours()}:${String(now.getUTCMinutes()).padStart(2,'0')}</div>`;
675
676    document.getElementById('fl-live-badge').style.display = 'none';
677    document.getElementById('fl-update-time').textContent = '📋 برنامه معمول پروازی';
678  },
679
680  async loadLive() {
681    document.getElementById('fl-content').innerHTML =
682      '<div style="text-align:center;padding:20px;color:var(--g);font-weight:700">⏳ جستجوی پروازهای زنده...</div>';
683    document.getElementById('fl-live-badge').style.display = 'block';
684
685    const ap = this.AIRPORTS[this.cur];
686    const r = ap.lat*0.8, lo = ap.lng;
687    const url = `https://opensky-network.org/api/states/all?lamin=${ap.lat-0.8}&lamax=${ap.lat+0.8}&lomin=${ap.lng-0.8}&lomax=${ap.lng+0.8}`;
688
689    try {
690      const res = await fetch(url, {signal: AbortSignal.timeout(8000)});
691      if (!res.ok) throw new Error('no data');
692      const d = await res.json();
693      const states = (d.states||[]).filter(f=>!f[8]);
694
695      if (!states.length) {
696        document.getElementById('fl-content').innerHTML = `
697          <div style="text-align:center;padding:30px;background:#f9fafb;border-radius:12px;border:1px solid var(--bdr)">
698            <div style="font-size:48px;margin-bottom:10px">🌙</div>
699            <div style="font-size:15px;font-weight:800;color:var(--t1);margin-bottom:6px">هیچ هواپیمایی در آسمان نیست</div>
700            <div style="font-size:12px;color:var(--t2)">در حال حاضر هواپیمایی در محدوده ${ap.name} شناسایی نشد</div>
701          </div>`;
702        document.getElementById('fl-update-time').textContent = '🔴 زنده · بدون پرواز فعال';
703        return;
704      }
705
706      const DIRS = ['شمال','شمال‌شرق','شرق','جنوب‌شرق','جنوب','جنوب‌غرب','غرب','شمال‌غرب'];
707      const dir = deg => DIRS[Math.round((deg||0)/45)%8];
708
709      document.getElementById('fl-content').innerHTML = `
710        <div style="background:#f0fdf4;border-radius:9px;padding:9px 12px;margin-bottom:10px;border:1px solid var(--gm);display:flex;align-items:center;gap:8px">
711          <span style="font-size:16px">✈️</span>
712          <div style="font-size:13px;font-weight:800;color:var(--gd)">${states.length} هواپیما در آسمان ${ap.name}</div>
713          <div style="margin-right:auto;font-size:10px;font-weight:700;background:#ef4444;color:#fff;padding:2px 8px;border-radius:20px">● LIVE</div>
714        </div>
715        ${states.slice(0,10).map(f => {
716          const cs = (f[1]||'---').trim() || '---';
717          const country = f[2] || 'نامشخص';
718          const alt = f[7] ? Math.round(f[7]) + 'م' : '--';
719          const spd = f[9] ? Math.round(f[9]*3.6) + ' ک/س' : '--';
720          const hdg = f[10] ? dir(f[10]) : '--';
721          const vrt = f[11] ? (f[11]>0?'↑':'↓')+Math.abs(Math.round(f[11]))+'م/ث' : '--';
722          return `
723          <div style="background:#fff;border-radius:10px;padding:12px 14px;border:1.5px solid var(--bdr);margin-bottom:8px">
724            <div style="display:flex;align-items:center;gap:10px;margin-bottom:7px">
725              <div style="font-size:28px;display:inline-block;transform:rotate(${f[10]||0}deg)">✈️</div>
726              <div style="flex:1">
727                <div style="font-size:15px;font-weight:900;color:var(--t1);direction:ltr">${cs}</div>
728                <div style="font-size:11px;color:var(--t3)">${country}</div>
729              </div>
730              <div style="font-size:10px;font-weight:800;background:#fee2e2;color:#b91c1c;padding:3px 8px;border-radius:20px">زنده</div>
731            </div>
732            <div style="display:grid;grid-template-columns:repeat(4,1fr);gap:6px">
733              <div style="background:#f5f7fb;border-radius:7px;padding:6px;text-align:center">
734                <div style="font-size:9px;color:var(--t3);margin-bottom:2px">ارتفاع</div>
735                <div style="font-size:12px;font-weight:800;color:var(--t1)">${alt}</div>
736              </div>
737              <div style="background:#f5f7fb;border-radius:7px;padding:6px;text-align:center">
738                <div style="font-size:9px;color:var(--t3);margin-bottom:2px">سرعت</div>
739                <div style="font-size:11px;font-weight:800;color:var(--t1)">${spd}</div>
740              </div>
741              <div style="background:#f5f7fb;border-radius:7px;padding:6px;text-align:center">
742                <div style="font-size:9px;color:var(--t3);margin-bottom:2px">جهت</div>
743                <div style="font-size:11px;font-weight:800;color:var(--t1)">${hdg}</div>
744              </div>
745              <div style="background:#f5f7fb;border-radius:7px;padding:6px;text-align:center">
746                <div style="font-size:9px;color:var(--t3);margin-bottom:2px">صعود</div>
747                <div style="font-size:11px;font-weight:800;color:var(--t1)">${vrt}</div>
748              </div>
749            </div>
750          </div>`;
751        }).join('')}`;
752
753      // آپدیت هر ۳۰ ثانیه
754      if (this.timer) clearTimeout(this.timer);
755      this.timer = setTimeout(() => { if(this.curTab==='live') this.loadLive(); }, 30000);
756
757      // شمارنده
758      let secs = 30;
759      if (this.countdown) clearInterval(this.countdown);
760      this.countdown = setInterval(() => {
761        const el = document.getElementById('fl-update-time');
762        if (!el || this.curTab!=='live') { clearInterval(this.countdown); return; }
763        el.textContent = `🔴 زنده · آپدیت در ${secs} ثانیه`;
764        if (--secs < 0) clearInterval(this.countdown);
765      }, 1000);
766
767    } catch(e) {
768      document.getElementById('fl-content').innerHTML = `
769        <div style="text-align:center;padding:20px;background:#fff9e6;border-radius:10px;border:1px solid #fde68a">
770          <div style="font-size:13px;font-weight:800;color:#92400e;margin-bottom:8px">OpenSky در دسترس نیست</div>
771          <div style="font-size:12px;color:#92400e">برنامه معمول را مشاهده کنید</div>
772          <button onclick="FL.tab(document.getElementById('tab-dep'),'dep')" style="margin-top:10px;background:var(--g);color:#fff;border:none;border-radius:8px;padding:8px 16px;font-size:12px;font-weight:800;cursor:pointer;font-family:inherit">مشاهده برنامه ←</button>
773        </div>`;
774      document.getElementById('fl-live-badge').style.display = 'none';
775    }
776  },
777
778  reload() { this.showTab(this.curTab); }
779};
780
781
782// ── AIR QUALITY (Open-Meteo - رایگان بدون key) ──
783const AQI_LEVELS = [
784  {max:50,   label:'خوب',          emoji:'😊', color:'#00e400', bg:'linear-gradient(135deg,#00c853,#69f0ae)', advice:'کیفیت هوا رضایت‌بخش است. فعالیت در فضای باز برای Ù
784‡Ù…Ù‡ مناسب است.'},
785  {max:100,  label:'متوسط',        emoji:'😐', color:'#ffff00', bg:'linear-gradient(135deg,#f9a825,#ffee58)', advice:'افراد حساس (آسم، بیماری قلبی) ممکن است احساس ناراحتی کنند.'},
786  {max:150,  label:'ناسالم',       emoji:'😷', color:'#ff7e00', bg:'linear-gradient(135deg,#e65100,#ff9800)', advice:'فعالیت طولانی در فضای باز را محدود کنید. کودکان و سالمندان توجه ویژه داشته باشند.'},
787  {max:200,  label:'بسیار ناسالم', emoji:'🤢', color:'#ff0000', bg:'linear-gradient(135deg,#b71c1c,#f44336)', advice:'از فعالیت در فضای باز خودداری کنید. از ماسک استفاده کنید.'},
788  {max:300,  label:'خطرناک',       emoji:'☠️', color:'#8f3f97', bg:'linear-gradient(135deg,#4a148c,#9c27b0)', advice:'هشدار بهداشتی جدی. همه باید از تماس با هوای آزاد پرهیز کنند.'},
789  {max:9999, label:'بحرانی',       emoji:'🚨', color:'#7e0023', bg:'linear-gradient(135deg,#3e0000,#7e0023)', advice:'وضعیت اضطراری. در خانه بمانید، درها و پنجره‌ها را ببندید.'},
790];
791
792async function loadAQI() {
793  const sel = document.getElementById('aqi-city-select');
794  if (!sel) return;
795  const [lat, lng, city] = sel.value.split(',');
796
797  document.getElementById('aqi-city-label').textContent = city + '، افغانستان';
798  document.getElementById('aqi-value').textContent = '...';
799  document.getElementById('aqi-label').textContent = 'در حال دریافت...';
800  document.getElementById('aqi-emoji').textContent = '⏳';
801
802  try {
803    // Open-Meteo Air Quality API
804    const url = `https://air-quality-api.open-meteo.com/v1/air-quality?latitude=${lat}&longitude=${lng}&current=pm2_5,pm10,nitrogen_dioxide,ozone,sulphur_dioxide,carbon_monoxide,european_aqi&timezone=Asia%2FKabul`;
805    const r = await fetch(url);
806    const d = await r.json();
807    const cur = d.current;
808
809    // محاسبه AQI از PM2.5 (دقیق‌ترین شاخص)
810    const pm25 = cur.pm2_5 || 0;
811    const pm10 = cur.pm10 || 0;
812    const eu_aqi = cur.european_aqi || 0;
813
814    // تبدیل PM2.5 به AQI استاندارد US
815    const aqi = calcAQI_PM25(pm25);
816
817    // پیدا کردن سطح
818    const level = AQI_LEVELS.find(l => aqi <= l.max) || AQI_LEVELS[AQI_LEVELS.length-1];
819
820    // آپدیت کارت اصلی
821    const card = document.getElementById('aqi-main-card');
822    card.style.background = level.bg;
823
824    document.getElementById('aqi-value').textContent = Math.round(aqi);
825    document.getElementById('aqi-label').textContent = level.label;
826    document.getElementById('aqi-emoji').textContent = level.emoji;
827    document.getElementById('aqi-advice').textContent = level.advice;
828
829    // آلاینده‌ها
830    const pollutants = [
831      {name:'PM2.5', val: pm25.toFixed(1), unit:'µg/m³', icon:'🔴', desc:'ذرات ریز'},
832      {name:'PM10',  val: pm10.toFixed(1), unit:'µg/m³', icon:'🟠', desc:'ذرات درشت'},
833      {name:'NO₂',   val: (cur.nitrogen_dioxide||0).toFixed(1), unit:'µg/m³', icon:'🟡', desc:'دی‌اکسید نیتروژن'},
834      {name:'O₃',    val: (cur.ozone||0).toFixed(1), unit:'µg/m³', icon:'🟢', desc:'ازون'},
835      {name:'SO₂',   val: (cur.sulphur_dioxide||0).toFixed(1), unit:'µg/m³', icon:'🟣', desc:'دی‌اکسید گوگرد'},
836      {name:'CO',    val: ((cur.carbon_monoxide||0)/1000).toFixed(2), unit:'mg/m³', icon:'⚫', desc:'مونوکسید کربن'},
837    ];
838
839    document.getElementById('aqi-pollutants').innerHTML = pollutants.map(p => `
840      <div style="background:#fff;border-radius:10px;padding:11px 12px;border:1px solid var(--bdr)">
841        <div style="display:flex;align-items:center;gap:6px;margin-bottom:5px">
842          <span style="font-size:14px">${p.icon}</span>
843          <div>
844            <div style="font-size:12px;font-weight:900;color:var(--t1)">${p.name}</div>
845            <div style="font-size:10px;color:var(--t3)">${p.desc}</div>
846          </div>
847        </div>
848        <div style="font-size:18px;font-weight:900;color:${level.color}">${p.val}</div>
849        <div style="font-size:10px;color:var(--t3)">${p.unit}</div>
850      </div>`).join('');
851
852  } catch(e) {
853    document.getElementById('aqi-value').textContent = '؟';
854    document.getElementById('aqi-label').textContent = 'خطا در دریافت';
855    document.getElementById('aqi-emoji').textContent = '⚠️';
856    console.warn('AQI error:', e);
857  }
858}
859
860function calcAQI_PM25(pm25) {
861  // جدول تبدیل PM2.5 به AQI استاندارد US EPA
862  const table = [
863    [0,12,0,50],[12.1,35.4,51,100],[35.5,55.4,101,150],
864    [55.5,150.4,151,200],[150.5,250.4,201,300],[250.5,350.4,301,400],[350.5,500.4,401,500]
865  ];
866  for (const [cl,ch,al,ah] of table) {
867    if (pm25 >= cl && pm25 <= ch) {
868      return ((ah-al)/(ch-cl))*(pm25-cl)+al;
869    }
870  }
871  return pm25 > 500 ? 500 : 0;
872}
873
874// ── QURAN ─────────────────────────────────────
875const SURAH_NAMES = {
876  '001':'الفاتحه','002':'البقره','003':'آل عمران','036':'یس',
877  '055':'الرحمن','056':'الواقعه','067':'الملک','073':'المزمل',
878  '078':'النبأ','087':'الأعلى','093':'الضحى','094':'الشرح',
879  '099':'الزلزله','112':'الاخلاص','113':'الفلق','114':'الناس',
880};
881
882const Quran = {
883  async loadAyah() {
884    try {
885      const r = await fetch(
886        'https://api.alquran.cloud/v1/ayah/2:255/editions/quran-simple,fa.hedayatifar',
887        {signal: AbortSignal.timeout(8000)}
888      );
889      const d = await r.json();
890      if (d.code !== 200) throw new Error();
891      document.getElementById('quran-arabic').textContent = d.data[0].text;
892      document.getElementById('quran-dari').textContent = d.data[1].text;
893      document.getElementById('quran-ref').textContent =
894        `سوره ${d.data[0].surah.name} · آیه ${d.data[0].numberInSurah}`;
895    } catch(e) {
896      // fallback - آیه‌الکرسی متن ثابت
897      document.getElementById('quran-arabic').textContent =
898        'اللَّهُ لَا إِلَٰهَ إِلَّا هُوَ الْحَيُّ الْقَيُّومُ';
899      document.getElementById('quran-dari').textContent =
900        'خداوند است که جز او معبودی نیست، زنده و پاینده است.';
901      document.getElementById('quran-ref').textContent = 'سوره البقره · آیه ۲۵۵ (آیة‌الکرسی)';
902    }
903  }
904};
905
906window.loadQuranAudio = () => {
907  const sel = document.getElementById('quran-surah-sel');
908  if (!sel) return;
909  const n = sel.value; // 3-digit like "036"
910  const surahNum = parseInt(n, 10);
911  const name = SURAH_NAMES[n] || 'سوره';
912  const audio = document.getElementById('quran-audio');
913  const status = document.getElementById('quran-status');
914
915  if (status) status.textContent = `⏳ سوره ${name} — در حال بارگذاری...`;
916  audio.pause();
917
918  // منابع مختلف به ترتیب اولویت
919  const sources = [
920    // cdn.islamic.network - معتبرترین CDN قرآن
921    `https://cdn.islamic.network/quran/audio-surah/128/ar.abdulbasitmurattal/${surahNum}.mp3`,
922    // everyayah - معروف و پایدار
923    `https://everyayah.com/data/Abdul_Basit_Murattal_192kbps/${n}001.mp3`,
924    // backup
925    `https://download.quranicaudio.com/quranic-audio/abdulbasit/complete/${n}.mp3`,
926  ];
927
928  let idx = 0;
929  const tryNext = () => {
930    if (idx >= sources.length) {
931      if (status) status.innerHTML =
932        `⚠️ مشکل در بارگذاری · <a href="https://quran.com/1" target="_blank" style="color:var(--g);font-weight:800">گوش دادن در Quran.com ←</a>`;
933      return;
934    }
935    audio.src = sources[idx++];
936    audio.load();
937  };
938
939  audio.oncanplaythrough = () => {
940    if (status) status.textContent = `✅ سوره ${name} — آماده پخش`;
941  };
942
943  audio.onplaying = () => {
944    if (status) status.textContent = `🔊 در حال پخش: سوره ${name}`;
945  };
946
947  audio.onended = () => {
948    if (status) status.textContent = `✅ پخش سوره ${name} به پایان رسید`;
949  };
950
951  audio.onerror = () => {
952    if (status) status.textContent = `⏳ تلاش با منبع ${idx + 1}...`;
953    tryNext();
954  };
955
956  tryNext();
957};
958
959
960// ── JOBS ──────────────────────────────────────
961const Jobs = {
962  async load() {
963    const wrap = document.getElementById('jobs-wrap');
964    const total = document.getElementById('jobs-total');
965    if (!wrap) return;
966    wrap.innerHTML = '<div style="text-align:center;padding:20px;color:var(--g);font-weight:700">⏳ در حال بارگذاری...</div>';
967    try {
968      const d = await api('jobs');
969      if (!d.jobs || !d.jobs.length) throw new Error();
970      if (total) total.textContent = d.jobs.length + ' منبع کاریابی';
971      wrap.innerHTML = d.jobs.map(j => `
972        <a href="${j.u}" target="_blank" style="display:flex;align-items:center;gap:11px;background:#fff;border-radius:11px;padding:13px 14px;border:1.5px solid var(--bdr);margin-bottom:8px;text-decoration:none">
973          <div style="width:44px;height:44px;border-radius:12px;background:linear-gradient(135deg,#1e3a5f,#2563eb);display:flex;align-items:center;justify-content:center;font-size:22px;flex-shrink:0">💼</div>
974          <div style="flex:1;min-width:0">
975            <div style="font-size:14px;font-weight:800;color:var(--gd);margin-bottom:3px">${j.t}</div>
976            <div style="font-size:11px;color:var(--t2)">${j.o} · ${j.l}</div>
977          </div>
978          <div style="display:flex;flex-direction:column;align-items:flex-end;
978gap:5px;flex-shrink:0">
979            ${j.badge ? `<span style="font-size:10px;font-weight:800;background:var(--gl);color:var(--gd);padding:2px 8px;border-radius:5px">${j.badge}</span>` : ''}
980            <span style="font-size:11px;color:var(--g);font-weight:700">مشاهده ←</span>
981          </div>
982        </a>`).join('');
983    } catch(e) {
984      wrap.innerHTML = `
985        <div style="text-align:center;padding:16px;background:#f9fafb;border-radius:10px;border:1px solid var(--bdr)">
986          <div style="font-size:13px;color:var(--t2);margin-bottom:10px">برای مشاهده وظایف به سایت‌های زیر مراجعه کنید:</div>
987          <a href="https://jobs.af" target="_blank" style="display:block;background:var(--gl);color:var(--gd);padding:10px;border-radius:8px;font-size:13px;font-weight:800;text-decoration:none;margin-bottom:7px">🔥 jobs.af ←</a>
988          <a href="https://acbar.org/jobs" target="_blank" style="display:block;background:#eff6ff;color:#1d4ed8;padding:10px;border-radius:8px;font-size:13px;font-weight:800;text-decoration:none">🌐 acbar.org ←</a>
989        </div>`;
990    }
991  }
992};
993
994// ── DICTIONARY (دری افغانستان) ──────────────────
995const DICT = {
996  lang: 'dari',
997
998  // دری افغانستان ↔ انگلیسی
999  // شامل: زندگی روزمره، بازار، صحت، آموزش، احوال‌پرسی
1000  dari_en: {
1001    // احوال‌پرسی و ادب
1002    'سلام':        {m:['Hello','Hi'], n:'تحیه معمول دری', ex:'سلام، چطور استید؟'},
1003    'السلام علیکم':{m:['Peace be upon you'], n:'تحیه اسلامی', ex:'السلام علیکم — وعلیکم السلام'},
1004    'چطور استید':  {m:['How are you?'], n:'احوال‌پرسی رسمی', ex:'سلام، چطور استید؟ — خوب استم، ممنون'},
1005    'خوب استم':    {m:['I am fine','I am well'], n:'جواب احوال‌پرسی', ex:'خوب استم، شکر'},
1006    'خداحافظ':     {m:['Goodbye','Farewell'], n:'خداحافظی', ex:'خداحافظ، مراقب خود باشید'},
1007    'بخیر':        {m:['Well','Fine','Goodbye'], n:'خداحافظی دوستانه', ex:'بخیر، تا دیدار'},
1008    'ممنون':       {m:['Thank you','Thanks'], n:'سپاسگزاری عامیانه', ex:'ممنون از مرحمت شما'},
1009    'تشکر':        {m:['Thank you','Thanks'], n:'سپاسگزاری رسمی‌تر', ex:'تشکر از کمک تان'},
1010    'خوش آمدید':  {m:['Welcome'], n:'خوشامدگویی', ex:'به افغانستان خوش آمدید'},
1011    'بفرمایید':    {m:['Please (come in/sit/eat)'], n:'دعوت مؤدبانه', ex:'بفرمایید، بنشینید'},
1012    'نوش جان':    {m:['Enjoy your meal','Bon appétit'], n:'گفته می‌شود وقت خوردن', ex:'نوش جان باشد'},
1013    'مرحمت':       {m:['Kindness','Generosity','Please'], n:'ادب و احترام', ex:'مرحمت کنید'},
1014    'انشاءالله':  {m:['God willing','If God wills'], n:'اظهار امید', ex:'فردا می‌آیم، انشاءالله'},
1015    'ماشاءالله':  {m:['What God has willed (expression of admiration)'], n:'تعریف و تحسین', ex:'ماشاءالله، بسیار خوب کردید'},
1016
1017    // خانواده
1018    'پدر':   {m:['Father'], ex:'پدرم معلم است'},
1019    'مادر':  {m:['Mother'], ex:'مادرم پخت‌وپز خوبی دارد'},
1020    'برادر': {m:['Brother'], ex:'برادرم در کابل کار می‌کند'},
1021    'خواهر': {m:['Sister'], ex:'خواهرم محصل است'},
1022    'پسر':   {m:['Son','Boy'], ex:'پسرم ده ساله است'},
1023    'دختر':  {m:['Daughter','Girl'], ex:'دخترم مکتب می‌رود'},
1024    'کاکا':  {m:["Uncle (father's brother)"], n:'برادر پدر', ex:'کاکایم در هرات زندگی می‌کند'},
1025    'ماما':  {m:["Uncle (mother's brother)"], n:'برادر مادر', ex:'مامایم دوکاندار است'},
1026    'ملا':   {m:['Mullah (religious leader)','Mister (informal title)'], n:'عنوان دینی یا احترامی', ex:'ملا صاحب درس می‌دهد'},
1027    'خانم':  {m:['Lady','Wife','Mrs.'], n:'عنوان احترامی برای زن', ex:'خانم صاحب، چطور استید؟'},
1028    'صاحب': {m:['Sir','Mr.','Owner'], n:'عنوان احترامی', ex:'استاد صاحب'},
1029
1030    // زندگی روزمره
1031    'نان':    {m:['Bread','Food (generic)'], n:'در دری افغانستان "نان" به معنای غذا هم هست', ex:'نان خوردید؟ (آیا غذا خوردید؟)'},
1032    'چلو':    {m:['Plain boiled rice'], n:'برنج ساده', ex:'چلو کباب غذای محبوب است'},
1033    'قابلی':  {m:['Qabuli (Afghan national dish - rice with meat)'], n:'غذای ملی افغانستان', ex:'قابلی پلو با کشمش و هویج'},
1034    'منتو':   {m:['Mantu (Afghan dumplings)'], n:'دامپلینگ افغانی با گوشت', ex:'منتوی کابلی بسیار خوشمزه است'},
1035    'بولانی': {m:['Bolani (stuffed flatbread)'], n:'نان پر شده با سبزی', ex:'بولانی کچالو می‌خورید؟'},
1036    'کاچی':   {m:['Porridge (celebratory dish)'], n:'حلیم برای تقریبات', ex:'کاچی نذری'},
1037    'چای':    {m:['Tea'], n:'نوشیدنی اصلی افغانستان', ex:'یک پیاله چای سبز'},
1038    'قوری':   {m:['Teapot'], ex:'قوری چای را بیار'},
1039    'پیاله':  {m:['Cup','Bowl'], ex:'یک پیاله چای'},
1040    'دیگ':    {m:['Large cooking pot'], ex:'دیگ پلو'},
1041    'آب':     {m:['Water'], ex:'یک گیلاس آب'},
1042    'دوکان':  {m:['Shop','Store'], n:'مغازه', ex:'به دوکان رفتم'},
1043    'بازار':  {m:['Market','Bazaar'], ex:'بازار مندوی در کابل'},
1044    'خریداری':{m:['Shopping','Purchase'], ex:'به بازار خریداری رفتم'},
1045    'قیمت':   {m:['Price','Cost'], ex:'قیمت این چند است؟'},
1046    'ارزان':  {m:['Cheap','Inexpensive'], ex:'این بازار ارزان است'},
1047    'گران':   {m:['Expensive'], ex:'بسیار گران است'},
1048    'پول':    {m:['Money'], ex:'پول ندارم'},
1049    'افغانی': {m:['Afghani (currency)'], n:'واحد پول افغانستان', ex:'یک صد افغانی'},
1050    'خرد':    {m:['Change (small money)','Small'], ex:'خرد ندارید؟'},
1051    'موترسایکل':{m:['Motorcycle'], n:'واژه رایج در افغانستان', ex:'با موترسایکل آمدم'},
1052    'موتر':   {m:['Car','Vehicle'], n:'به جای "ماشین"', ex:'موتر نو خریدم'},
1053    'سرک':    {m:['Road','Street'], n:'به جای "جاده"', ex:'سرک مسدود است'},
1054    'کوچه':   {m:['Alley','Lane'], ex:'در همین کوچه زندگی می‌کنیم'},
1055    'منزل':   {m:['House','Floor (of building)'], ex:'منزل اول'},
1056    'حویلی':  {m:['Courtyard house','Compound'], n:'خانه با حیاط', ex:'حویلی بزرگ'},
1057
1058    // آموزش
1059    'مکتب':      {m:['School'], n:'به جای "مدرسه"', ex:'به مکتب رفتم'},
1060    'لیسه':      {m:['High school'], n:'مقطع دبیرستان', ex:'لیسه عالی کابل'},
1061    'پوهنتون':   {m:['University'], n:'به جای "دانشگاه"', ex:'پوهنتون کابل'},
1062    'پوهنحی':    {m:['Faculty','College (within university)'], n:'دانشکده', ex:'پوهنحی طب'},
1063    'استاد':     {m:['Teacher','Professor','Master'], n:'عنوان احترامی معلم', ex:'استاد صاحب درس می‌دهد'},
1064    'محصل':      {m:['University student'], n:'دانشجو', ex:'محصل سال سوم هستم'},
1065    'شاگرد':     {m:['Student (school)','Pupil'], n:'دانش‌آموز', ex:'شاگرد خوب'},
1066    'کانکور':    {m:['University entrance exam'], n:'کنکور افغانستان', ex:'امتحان کانکور دادم'},
1067    'نمره':      {m:['Grade','Score','Mark'], n:'به جای "نمره"', ex:'نمره خوب گرفتم'},
1068    'سند':       {m:['Certificate','Document'], ex:'سند فراغت'},
1069
1070    // صحت
1071    'داکتر':      {m:['Doctor','Physician'], n:'به جای "دکتر"', ex:'داکتر صاحب'},
1072    'شفاخانه':   {m:['Hospital'], n:'به جای "بیمارستان"', ex:'شفاخانه دولتی'},
1073    'درمانگاه':  {m:['Clinic'], ex:'درمانگاه ولسوالی'},
1074    'دوا':        {m:['Medicine','Drug'], n:'به جای "دارو"', ex:'دوا خوردم'},
1075    'داروخانه':  {m:['Pharmacy'], ex:'از داروخانه دوا بگیرید'},
1076    'دردمند':    {m:['Patient','Sick person'], ex:'دردمند بستری است'},
1077    'مریض':      {m:['Sick','Ill','Patient'], ex:'مریض هستم'},
1078    'صحت':       {m:['Health'], ex:'صحت مند باشید'},
1079    'نرس':       {m:['Nurse'], ex:'نرس صاحب'},
1080
1081    // کار و مشاغل
1082    'وظیفه':     {m:['Job','Duty','Position'], n:'به جای "شغل"', ex:'وظیفه‌ام در دولت است'},
1083    'مامور':     {m:['Government employee','Official'], ex:'مامور دولتی'},
1084    'دهقان':     {m:['Farmer'], ex:'دهقانان زمین می‌کارند'},
1085    'تاجر':      {m:['Merchant','Trader'], ex:'تاجر بازار'},
1086    'کارمند':    {m:['Employee','Staff'], ex:'کارمند شرکت'},
1087    'مزد':       {m:['Wage','Salary'], n:'به جای "حقوق"', ex:'مزد ماهانه'},
1088    'زحمت':      {m:['Hardship','Trouble','Effort'], ex:'زحمت می‌کشید'},
1089
1090    // جغرافیا
1091    'ولایت':     {m:['Province'], n:'به جای "استان"', ex:'ولایت هرات'},
1092    'ولسوالی':   {m:['District'], n:'به جای "شهرستان"', ex:'ولسوالی پغمان'},
1093    'قریه':      {m:['Village'], n:'به جای "روستا"', ex:'قریه ما'},
1094    'شهر':       {m:['City'], ex:'شهر کابل'},
1095
1096    // امثال و اصطلاحات دری
1097    'چشم':       {m:['Yes (I will do it)','Eye'], n:'پاسخ موافقت', ex:'چشم، همین حالا می‌روم'},
1098    'باشه':      {m:['OK','Alright'], n:'موافقت غیررسمی', ex:'باشه، فردا می‌آیم'},
1099    'خو':        {m:['Well (filler word)','Sleep'], n:'کلمه پرکاربرد در گفتار', ex:'خو، چه کار کنیم؟'},
1100    'حالا':      {m:['Now','Right now'], ex:'حالا می‌روم'},
1101    'نزدیک':     {m:['Near','Close'], ex:'نزدیک است'},
1102    'دور':       {m:['Far'], ex:'بسیار دور است'},
1103    'زیاد':      {m:['Much','Many','Too much'], ex:'زیاد کار دارم'},
1104    'کم':        {m:['Little','Few','Less'], ex:'کمی آب'},
1105    'بسیار':     {m:['Very','Much','A lot'], n:'به جای "خیلی"', ex:'بسیار خوب'},
1106    'لطفاً':     {m:['Please'], ex:'لطفاً صبر کنید'},
1107    'مشکل':      {m:['Problem','Difficult'], ex:'مشکل ندارد'},
1108    'خطر':       {m:['Danger','Risk'], ex:'خطرناک است'},
1109    'آهسته':     {m:['Slowly','Quietly'], ex:'آهسته صحبت کنید'},
1110    'زود':       {m:['Quickly','Soon'], ex:'زود بیایید'},
1111  },
1112
1113  // انگلیسی → دری افغانستان
1114  en_dari: {
1115    'hello':        {m:['سلام','درود'], ex:'Hello! — سلام!'},
1116    'hi':           {m:['سلام','هی'], ex:'Hi there — سلام'},
1117    'how are you':  {m:['چطور استید؟','حال تان چطور است؟']},
1118    'i am fine':    {m:['خوب استم','صحتمند استم']},
1119    'thank you':    {m:['ممنون','تشکر'], ex:'Thank you very much — بسیار ممنون'},
1120    'thanks':       {m:['ممنون','مرسی']},
1121    'please':       {m:['لطفاً','مرحمت کنید']},
1122    'welcome':      {m:['خوش آمدید','خوش آمدی']},
1123    'goodbye':      {m:['خداحافظ','بخیر'], ex:'Goodbye — خداحافظ'},
1124    'yes':          {m:['بله','آری','چشم'], ex:'Yes — بله / چشم'},
1125    'no':           {m:['نه','خیر']},
1126    'ok':           {m:['باشه','خوب است','قبول']},
1127    'sorry':        {m:['معذرت می‌خواهم','ببخشید']},
1128    'excuse me':    {m:['ببخشید','اجازه بدهید']},
1129    'water':        {m:['آب']},
1130    'food':         {m:['نان','غذا'], n:'در دری افغانستان "نان" هم معنی غذا می‌دهد'},
1131    'bread':        {m:['نان'], n:'نان تنوری، نان لواش'},
1132    'tea':          {m:['چای'], ex:'Green tea — چای سبز'},
1133    'rice':         {m:['برنج','پلو'], ex:'Qabuli rice — قابلی پلو'},
1134    'house':        {m:['خانه','منزل','حویلی']},
1135    'school':       {m:['مکتب'], n:'در افغانستان "مکتب" گفته می‌شود نه مدرسه'},
1136    'university':   {m:['پوهنتون'], n:'به جای دانشگاه'},
1137    'teacher':      {m:['استاد','معلم'], n:'"استاد صاحب" عنوان احترامی'},
1138    'student':      {m:['محصل (پوهنتون)','شاگرد (مکتب)']},
1139    'doctor':       {m:['داکتر'], n:'در افغانستان "داکتر" گفته می‌شود'},
1140    'hospital':     {m:['شفاخانه'], n:'به جای بیمارستان'},
1141    'medicine':     {m:['دوا'], n:'به جای دارو'},
1142    'market':       {m:['بازار'], ex:'Grand Bazaar — بازار بزرگ'},
1143    'shop':         {m:['دوکان'], n:'مغازه'},
1144    'money':        {m:['پول']},
1145    'price':        {m:['قیمت','نرخ']},
1146    'cheap':        {m:['ارزان']},
1147    'expensive':    {m:['گران']},
1148    'car':          {m:['موتر'], n:'در افغانستان "موتر" گفته می‌شود'},
1149    'road':         {m:['سرک'], n:'به جای جاده'},
1150    'village':      {m:['قریه'], n:'به جای روستا'},
1151    'province':     {m:['ولایت'], n:'استان را در افغانستان ولایت می‌گویند'},
1152    'district':     {m:['ولسوالی']},
1153    'job':          {m:['وظیفه','کار'], n:'وظیفه = پست رسمی'},
1154    'employee':     {m:['کارمند','مامور']},
1155    'farmer':       {m:['دهقان']},
1156    'very':         {m:['بسیار'], n:'در افغانستان "بسیار" رایج‌تر از "خیلی" است'},
1157    'much':         {m:['بسیار','زیاد']},
1158    'little':       {m:['کم','اندک']},
1159    'now':          {m:['حالا','اکنون']},
1160    'quickly':      {m:['زود','تیز']},
1161    'slowly':       {m:['آهسته']},
1162    'near':         {m:['نزدیک']},
1163    'far':          {m:['دور']},
1164    'big':          {m:['بزرگ','کلان'], n:'"کلان" هم رایج است'},
1165    'small':        {m:['کوچک','خورد'], n:'"خورد" هم استفاده می‌شود'},
1166    'good':         {m:['خوب']},
1167    'bad':          {m:['بد']},
1168    'hot':          {m:['گرم']},
1169    'cold':         {m:['سرد']},
1170    'red':          {m:['سرخ'], n:'در افغانستان "سرخ" رایج‌تر است'},
1171    'green':        {m:['سبز']},
1172    'blue':         {m:['آبی']},
1173    'white':        {m:['سفید']},
1174    'black':        {m:['سیاه']},
1175    'one':          {m:['یک']},
1176    'two':          {m:['دو']},
1177    'three':        {m:['سه']},
1178    'four':         {m:['چهار']},
1179    'five':         {m:['پنج']},
1180    'six':          {m:['شش']},
1181    'seven':        {m:['هفت']},
1182    'eight':        {m:['هشت']},
1183    'nine':         {m:['نه']},
1184    'ten':          {m:['ده']},
1185    'hundred':      {m:['صد']},
1186    'thousand':     {m:['هزار']},
1187    'monday':       {m:['دوشنبه']},
1188    'tuesday':      {m:['سه‌شنبه']},
1189    'wednesday':    {m:['چهارشنبه']},
1190    'thursday':     {m:['پنج‌شنبه']},
1191    'friday':       {m:['جمعه'], ex:'Holy Friday — جمعه مبارک'},
1192    'saturday':     {m:['شنبه']},
1193    'sunday':       {m:['یکشنبه']},
1194    'spring':       {m:['بهار']},
1195    'summer':       {m:['تابستان']},
1196    'autumn':       {m:['خزان'], n:'در افغانستان "خزان" گفته می‌شود نه پاییز'},
1197    'winter':       {m:['زمستان']},
1198    'afghanistan':  {m:['افغانستان']},
1199    'kabul':        {m:['کابل']},
1200    'herat':        {m:['هرات']},
1201    'kandahar':     {m:['قندهار']},
1202  },
1203
1204  // پشتو → دری
1205  pashto_dari: {
1206    'سلام':    {m:['سلام'], n:'مشترک'},
1207    'مننه':    {m:['ممنون','تشکر'], ex:'مننه — ممنون'},
1208    'هو':      {m:['بله','آری']},
1209    'نه':      {m:['نه','خیر']},
1210    'اوبه':    {m:['آب']},
1211    'ډوډۍ':   {m:['نان','غذا']},
1212    'کور':     {m:['خانه','منزل']},
1213    'ښوونځی':  {m:['مکتب']},
1214    'پوهنتون': {m:['پوهنتون'], n:'مشترک'},
1215    'روغتون':  {m:['شفاخانه']},
1216    'بازار':   {m:['بازار'], n:'مشترک'},
1217    'پیسې':   {m:['پول']},
1218    'کار':     {m:['کار','وظیفه']},
1219    'کتاب':    {m:['کتاب'], n:'مشترک'},
1220    'ملګری':  {m:['دوست']},
1221    'کورنۍ':  {m:['خانواده']},
1222    'پلار':    {m:['پدر']},
1223    'مور':     {m:['مادر']},
1224    'وروره':   {m:['برادر']},
1225    'خور':     {m:['خواهر']},
1226    'ښه':      {m:['خوب']},
1227    'بد':      {m:['بد'], n:'مشترک'},
1228    'لوی':     {m:['بزرگ','کلان']},
1229    'وړوکی':  {m:['کوچک','خورد']},
1230    'ګرم':    {m:['گرم']},
1231    'سوړ':    {m:['سرد']},
1232    'سرک':    {m:['سرک','جاده'], n:'مشترک'},
1233    'ولایت':  {m:['ولایت'], n:'مشترک'},
1234    'ولسوالي':{m:['ولسوالی'], n:'مشترک'},
1235    'قریه':   {m:['قریه'], n:'مشترک'},
1236    'داکتر':  {m:['داکتر'], n:'مشترک'},
1237    'نرس':    {m:['نرس'], n:'مشترک'},
1238    'موتر':   {m:['موتر'], n:'مشترک'},
1239    'چای':    {m:['چای'], n:'مشترک'},
1240    'استاد':  {m:['استاد'], n:'مشترک'},
1241    'شاگرد':  {m:['شاگرد'], n:'مشترک'},
1242    'زیات':   {m:['زیاد','بسیار']},
1243    'لږ':     {m:['کم','اندک']},
1244    'ژر':     {m:['زود','تیز']},
1245    'ورو':    {m:['آهسته']},
1246    'اوس':    {m:['حالا','اکنون']},
1247    'نږدې':  {m:['نزدیک']},
1248    'لرې':   {m:['دور']},
1249    'سپین':   {m:['سفید']},
1250    'تور':    {m:['سیاه']},
1251    'سور':    {m:['سرخ']},
1252    'شین':    {m:['سبز','آبی']},
1253    'یو':     {m:['یک']},
1254    'دوه':    {m:['دو']},
1255    'درې':   {m:['سه']},
1256    'څلور':  {m:['چهار']},
1257    'پنځه':  {m:['پنج']},
1258    'شپږ':   {m:['شش']},
1259    'اووه':   {m:['هفت']},
1260    'اته':    {m:['هشت']},
1261    'نهه':    {m:['نه']},
1262    'لس':     {m:['ده']},
1263    'سل':     {m:['صد']},
1264    'زر':     {m:['هزار']},
1265  }
1266};
1267
1268window.setDictLang = (btn, lang) => {
1269  DICT.lang = lang;
1270  btn.closest('.ctabs').querySelectorAll('.ctab').forEach(b=>b.classList.remove('on'));
1271  btn.classList.add('on');
1272  document.getElementById('dict-input').value = '';
1273  document.getElementById('dict-results').innerHTML =
1274    '<div style="text-align:center;padding:20px;color:var(--t3)">کلمه‌ای بنویسید...</div>';
1275};
1276
1277window.searchDict = () => {
1278  const q = document.getElementById('dict-input').value.trim().toLowerCase();
1279  const res = document.getElementById('dict-results');
1280  if (!q || q.length < 1) {
1281    res.innerHTML = '<div style="text-align:center;padding:20px;color:var(--t3)">کلمه‌ای بنویسید...</div>';
1282    return;
1283  }
1284  const db = DICT.lang==='dari' ? DICT.dari_en : DICT.lang==='english' ? DICT.en_dari : DICT.pashto_dari;
1285  const matches = Object.entries(db).filter(([k,v]) =>
1286    k.toLowerCase().includes(q) || q.includes(k.toLowerCase())
1287  );
1288
1289  if (!matches.length) {
1290    res.innerHTML = `
1291      <div style="background:#f9fafb;border-radius:10px;padding:16px;border:1px solid var(--bdr);text-align:center">
1292        <div style="font-size:24px;margin-bottom:8px">🔍</div>
1293        <div style="font-size:13px;color:var(--t2);margin-bottom:12px">«${q}» در دیکشنری یافت نشد</div>
1294        <button onclick="dictAI('${q}')" style="background:var(--g);color:#fff;border:none;border-radius:9px;padding:9px 20px;font-size:13px;font-weight:800;cursor:pointer;font-family:inherit">
1295          🤖 ترجمه با هوش مصنوعی
1296        </button>
1297      </div>`;
1298    return;
1299  }
1300
1301  res.innerHTML = matches.slice(0,8).map(([word, data]) => {
1302    const meanings = data.m || [];
1303    const note = data.n || '';
1304    const example = data.ex || '';
1305    return `
1306    <div style="background:#fff;border-radius:12px;padding:14px 15px;border:1.5px solid var(--bdr);margin-bottom:9px;box-shadow:0 1px 3px rgba(0,0,0,.04)">
1307      <div style="font-size:20px;font-weight:900;color:var(--gd);margin-bottom:8px">${word}</div>
1308      <div style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:${note||example?'8px':'0'}">
1309        ${meanings.map(m=>`<span style="background:var(--gl);color:var(--gd);padding:4px 12px;border-radius:6px;font-size:13px;font-weight:700">${m}</span>`).join('')}
1310      </div>
1311      ${note ? `<div style="font-size:11px;color:#0369a1;background:#eff6ff;padding:5px 10px;border-radius:6px;margin-bottom:5px">💡 ${note}</div>` : ''}
1312      ${example ? `<div style="font-size:12px;color:var(--t2);font-style:italic;margin-top:4px">مثال: ${example}</div>` : ''}
1313    </div>`;
1314  }).join('');
1315};
1316
1317window.dictAI = async (word) => {
1318  const res = document.getElementById('dict-results');
1319  res.innerHTML = '<div style="text-align:center;padding:16px;color:var(--g);font-weight:700">⏳ در حال ترجمه...</div>';
1320  try {
1321    const langMap = {dari:'از دری افغانستان به انگلیسی', english:'از انگلیسی به دری افغانستان', pashto:'از پشتو به دری افغانستان'};
1322    const sys = `تو متخصص زبان دری افغانستان هستی. ترجمه دقیق با ادبیات دری افغانستان بده.
1323واژگان رایج افغانستان را استفاده کن: مکتب (نه مدرسه)، پوهنتون (نه دانشگاه)، شفاخانه (نه بیمارستان)، داکتر (نه دکتر)، موتر (نه ماشین)، سرک (نه جاده)، بسیار (نه خیلی)، سرخ (نه قرمز).
1324پاسخ کوتاه و عملی باشد.`;
1325    const d = await api('ai', {
1326      q:`${langMap[DICT.lang]}: "${word}"
1327ترجمه + یک مثال جمله کوتاه`,
1328      sys
1329    });
1330    res.innerHTML = `
1331      <div style="background:var(--gl);border-radius:12px;padding:15px;border:1.5px solid var(--gm)">
1332        <div style="font-size:12px;font-weight:800;color:var(--t3);margin-bottom:8px">🤖 ترجمه هوش مصنوعی</div>
1333        <div style="font-size:14px;line-height:1.9;color:var(--t1)">${d.t.replace(/\n/g,'<br>')}</div>
1334      </div>`;
1335  } catch(e) {
1336    res.innerHTML = '<div style="text-align:center;padding:14px;color:var(--r);font-size:13px">خطا در ترجمه — دوباره تلاش کنید</div>';
1337  }
1338};
1339
1340
1341// ── PHONE DETECTOR ────────────────────────────
1342// منبع: Wikipedia - Telephone numbers in Afghanistan (2025)
1343window.detectPhone = () => {
1344  const raw = document.getElementById('phone-input').value.replace(/[\s\-\(\)]/g,'');
1345  const res = document.getElementById('phone-result');
1346  if (raw.length < 7) { res.innerHTML=''; return; }
1347
1348  // نرمال‌سازی شماره
1349  let clean = raw;
1350  if (clean.startsWith('+93')) clean = '0' + clean.slice(3);
1351  else if (clean.startsWith('0093')) clean = '0' + clean.slice(4);
1352  else if (clean.startsWith('93') && clean.length === 11) clean = '0' + clean.slice(2);
1353  if (!clean.startsWith('0')) clean = '0' + clean;
1354
1355  const p3 = clean.slice(0,3); // 3 رقم اول
1356
1357  // کدهای دقیق بر اساس Wikipedia - Telephone numbers in Afghanistan
1358  const operators = [
1359    {
1360      name:'Afghan Wireless (AWCC)',
1361      en:'Afghan Wireless Communication Company',
1362      color:'#2563eb', bg:'#dbeafe',
1363      emoji:'📶',
1364      prefixes:['070','071'],
1365      note:'اولین اپراتور افغانستان، تأسیس ۲۰۰۲'
1366    },
1367    {
1368      name:'Roshan (TDCA)',
1369      en:'Telecom Development Company Afghanistan',
1370      color:'#d97706', bg:'#fef3c7',
1371      emoji:'🌟',
1372      prefixes:['072','079'],
1373      note:'بزرگترین سرمایه‌گذار خصوصی افغانستان'
1374    },
1375    {
1376      name:'Etisalat Afghanistan',
1377      en:'Emirates Telecom Group - Afghanistan',
1378      color:'#16a34a', bg:'#dcfce7',
1379      emoji:'🟢',
1380      prefixes:['073','078'],
1381      note:'شبکه پیشرفته ۴G'
1382    },
1383    {
1384      name:'Afghan Telecom / Salaam',
1385      en:'Afghan Telecom (State-owned)',
1386      color:'#7c3aed', bg:'#ede9fe',
1387      emoji:'🔵',
1388      prefixes:['074','075'],
1389      note:'اپراتور دولتی افغانستان'
1390    },
1391    {
1392      name:'ATOMA (سابقاً MTN)',
1393      en:'ATOMA - formerly MTN Afghanistan',
1394      color:'#dc2626', bg:'#fee2e2',
1395      emoji:'🔴',
1396      prefixes:['076','077'],
1397      note:'از اپریل ۲۰۲۵ به ATOMA تغییر نام داد'
1398    },
1399  ];
1400
1401  const found = operators.find(op => op.prefixes.includes(p3));
1402
1403  if (!found) {
1404    // بررسی لندلاین
1405    const areaCode = clean.slice(1,3);
1406    const areas = {'20':'کابل','40':'هرات','50':'مزارشریف','30':'قندهار','60':'جلال‌آباد'};
1407    if (areas[areaCode] && clean.length >= 9) {
1408      res.innerHTML = `
1409        <div style="border-radius:12px;padding:14px;border:2px solid #64748b;background:#f8fafc">
1410          <div style="font-size:12px;color:var(--t3);margin-bottom:5px">خط ثابت</div>
1411          <div style="font-size:20px;font-weight:900;direction:ltr;letter-spacing:2px;margin-bottom:8px">${clean}</div>
1412          <div style="font-size:13px;font-weight:800;color:#334155">📞 خط ثابت ${areas[areaCode]}</div>
1413        </div>`;
1414      return;
1415    }
1416    res.innerHTML = `<div style="background:#f8fafc;border-radius:10px;padding:14px;border:1px solid var(--bdr);text-align:center;font-size:13px;color:var(--t2)">⚠️ شماره شناسایی نشد — لطفاً با 07 شروع کنید</div>`;
1417    return;
1418  }
1419
1420  const fmt = clean.replace(/^(\d{4})(\d{3})(\d{3})$/, '$1 $2 $3') || clean;
1421  res.innerHTML = `
1422    <div style="border-radius:13px;padding:16px;border:2px solid ${found.color};background:${found.bg}">
1423      <div style="font-size:11px;font-weight:700;color:${found.color};margin-bottom:6px;opacity:.8">شماره شناسایی‌شده</div>
1424      <div style="font-size:26px;font-weight:900;color:#0f172a;direction:ltr;letter-spacing:3px;margin-bottom:12px">${fmt}</div>
1425      <div style="display:flex;align-items:center;gap:12px">
1426        <div style="width:48px;height:48px;border-radius:14px;background:${found.color};display:flex;align-items:center;justify-content:center;font-size:24px;flex-shrink:0">${found.emoji}</div>
1427        <div>
1428          <div style="font-size:16px;font-weight:900;color:${found.color}">${found.name}</div>
1429          <div style="font-size:11px;color:#64748b;margin-top:2px">${found.en}</div>
1430          <div style="font-size:10px;color:#94a3b8;margin-top:3px">ℹ️ ${found.note}</div>
1431        </div>
1432      </div>
1433      <div style="margin-top:10px;background:rgba(0,0,0,.06);border-radius:8px;padding:7px 11px;font-size:11px;color:#475569">
1434        پریفیکس‌های این اپراتور: <strong>${found.prefixes.join('xx، ')}xx</strong>
1435      </div>
1436    </div>`;
1437};
1438
1439// ── CALENDAR 1405 ─────────────────────────────
1440const EVENTS = {
1441  national: [
1442    {month:'حمل',    day:1,  name:'عید نوروز',              type:'تعطیل رسمی', emoji:'🌸'},
1443    {month:'ثور',    day:8,  name:'روز ثور (۱۳۵۷)',         type:'تاریخی',     emoji:'📅'},
1444    {month:'جوزا',   day:3,  name:'استرداد استقلال',         type:'تعطیل رسمی', emoji:'🇦🇫'},
1445    {month:'اسد',    day:18, name:'استقلال افغانستان ۱۲۹۸', type:'تعطیل رسمی', emoji:'🎖️'},
1446    {month:'میزان',  day:7,  name:'شهادت استاد ربانی',       type:'تاریخی',     emoji:'🕊️'},
1447    {month:'عقرب',   day:13, name:'مجاهدین و جهاد',          type:'تاریخی',     emoji:'📅'},
1448  ],
1449  religious: [
1450    {month:'حمل',    day:28, name:'عید فطر ۱۴۴۷',           type:'تعطیل رسمی', emoji:'🌙'},
1451    {month:'جوزا',   day:5,  name:'عید قربان ۱۴۴۷',         type:'تعطیل رسمی', emoji:'🐑'},
1452    {month:'جوزا',   day:10, name:'عاشورا ۱۴۴۸',            type:'مذهبی',      emoji:'🕌'},
1453    {month:'سنبله',  day:3,  name:'میلاد النبی ﷺ',           type:'تعطیل رسمی', emoji:'☪️'},
1454    {month:'حمل',    day:1,  name:'نوروز',                   type:'تعطیل رسمی', emoji:'🌷'},
1455  ]
1456};
1457
1458const MONTHS_FA = ['حمل','ثور','جوزا','سرطان','اسد','سنبله','میزان','عقرب','قوس','جدی','دلو','حوت'];
1459
1460window.showCalTab = (btn, tab) => {
1461  btn.closest('.ctabs').querySelectorAll('.ctab').forEach(b=>b.classList.remove('on'));
1462  btn.classList.add('on');
1463  const el = document.getElementById('cal-content');
1464
1465  if (tab === 'upcoming') {
1466    // محاسبه ساده ماه شمسی فعلی
1467    el.innerHTML = `
1468      <div style="background:var(--gl);border-radius:10px;padding:12px;border:1px solid var(--gm);margin-bottom:10px;text-align:center">
1469        <div style="font-size:13px;font-weight:800;color:var(--gd)">مناسبت‌های زودهنگام سال ۱۴۰۵</div>
1470      </div>
1471      ${[...EVENTS.national,...EVENTS.religious].map(e=>`
1472      <div style="background:#fff;border-radius:10px;padding:11px 13px;border:1px solid var(--bdr);margin-bottom:7px;display:flex;align-items:center;gap:10px">
1473        <div style="font-size:28px">${e.emoji}</div>
1474        <div style="flex:1">
1475          <div style="font-size:14px;font-weight:800;color:var(--t1)">${e.name}</div>
1476          <div style="font-size:11px;color:var(--t3);margin-top:2px">${e.day}
1476 ${e.month} Û±Û´Û°Ûµ</div>
1477        </div>
1478        <div style="font-size:10px;font-weight:800;padding:3px 8px;border-radius:5px;background:${e.type==='تعطیل رسمی'?'#dcfce7':'#f0f4ff'};color:${e.type==='تعطیل رسمی'?'#15803d':'#1d4ed8'}">${e.type}</div>
1479      </div>`).join('')}`;
1480    return;
1481  }
1482
1483  const events = EVENTS[tab] || [];
1484  el.innerHTML = events.map(e=>`
1485    <div style="background:#fff;border-radius:10px;padding:12px 14px;border:1.5px solid var(--bdr);margin-bottom:8px;display:flex;align-items:center;gap:12px">
1486      <div style="font-size:32px">${e.emoji}</div>
1487      <div style="flex:1">
1488        <div style="font-size:14px;font-weight:900;color:var(--t1)">${e.name}</div>
1489        <div style="font-size:12px;color:var(--t3);margin-top:3px">📅 ${e.day} ${e.month} ۱۴۰۵</div>
1490      </div>
1491      <div style="font-size:11px;font-weight:800;padding:4px 9px;border-radius:6px;background:${e.type==='تعطیل رسمی'?'#dcfce7':'#dbeafe'};color:${e.type==='تعطیل رسمی'?'#15803d':'#1d4ed8'};white-space:nowrap">${e.type}</div>
1492    </div>`).join('');
1493};
1494
1495// ── DISTANCE ──────────────────────────────────
1496const CITIES = ['کابل','هرات','مزارشریف','قندهار','جلال‌آباد','کندز','بامیان','غزنی','لشکرگاه'];
1497const DIST_TABLE = {
1498  'کابل':        {'کابل':0,    'هرات':1177,'مزارشریف':425,'قندهار':492,'جلال‌آباد':148,'کندز':330,'بامیان':186,'غزنی':145,'لشکرگاه':636},
1499  'هرات':        {'کابل':1177,'هرات':0,   'مزارشریف':565,'قندهار':697,'جلال‌آباد':1325,'کندز':895,'بامیان':857,'غزنی':892,'لشکرگاه':617},
1500  'مزارشریف':   {'کابل':425, 'هرات':565,'مزارشریف':0,  'قندهار':917,'جلال‌آباد':573,'کندز':152,'بامیان':251,'غزنی':570,'لشکرگاه':1061},
1501  'قندهار':      {'کابل':492, 'هرات':697,'مزارشریف':917,'قندهار':0,  'جلال‌آباد':640,'کندز':822,'بامیان':678,'غزنی':347,'لشکرگاه':145},
1502  'جلال‌آباد':  {'کابل':148, 'هرات':1325,'مزارشریف':573,'قندهار':640,'جلال‌آباد':0, 'کندز':478,'بامیان':334,'غزنی':293,'لشکرگاه':784},
1503  'کندز':        {'کابل':330, 'هرات':895,'مزارشریف':152,'قندهار':822,'جلال‌آباد':478,'کندز':0,  'بامیان':403,'غزنی':475,'لشکرگاه':966},
1504  'بامیان':      {'کابل':186, 'هرات':857,'مزارشریف':251,'قندهار':678,'جلال‌آباد':334,'کندز':403,'بامیان':0,  'غزنی':226,'لشکرگاه':822},
1505  'غزنی':        {'کابل':145, 'هرات':892,'مزارشریف':570,'قندهار':347,'جلال‌آباد':293,'کندز':475,'بامیان':226,'غزنی':0,  'لشکرگاه':492},
1506  'لشکرگاه':    {'کابل':636, 'هرات':617,'مزارشریف':1061,'قندهار':145,'جلال‌آباد':784,'کندز':966,'بامیان':822,'غزنی':492,'لشکرگاه':0},
1507};
1508
1509const Dist = {
1510  init() {
1511    const fromSel = document.getElementById('dist-from');
1512    const toSel = document.getElementById('dist-to');
1513    if (!fromSel || !toSel) return;
1514    CITIES.forEach((c,i) => {
1515      fromSel.innerHTML += `<option value="${c}">${c}</option>`;
1516      toSel.innerHTML += `<option value="${c}" ${i===1?'selected':''}>${c}</option>`;
1517    });
1518    this.calc();
1519  },
1520  calc() {
1521    const from = document.getElementById('dist-from').value;
1522    const to = document.getElementById('dist-to').value;
1523    const res = document.getElementById('dist-result');
1524    if (from === to) {
1525      res.innerHTML = '<div style="text-align:center;padding:14px;color:var(--t3)">مبدا و مقصد یکی است</div>';
1526      return;
1527    }
1528    const km = DIST_TABLE[from]?.[to] || DIST_TABLE[to]?.[from] || 0;
1529    const hours = Math.floor(km/65);
1530    const mins = Math.round((km/65-hours)*60);
1531    res.innerHTML = `
1532      <div style="background:linear-gradient(135deg,var(--gd),var(--g));border-radius:14px;padding:18px;color:#fff;text-align:center;margin-bottom:14px">
1533        <div style="font-size:13px;opacity:.8;margin-bottom:6px">${from} ← → ${to}</div>
1534        <div style="font-size:48px;font-weight:900;line-height:1">
1534${km.toLocaleString('fa')}</div>
1535        <div style="font-size:14px;opacity:.9;margin-top:4px">کیلومتر</div>
1536        <div style="display:flex;justify-content:center;gap:20px;margin-top:12px;font-size:12px;opacity:.85">
1537          <div>🚗 تقریباً ${hours>0?hours+'س ':''} ${mins}د</div>
1538          <div>✈️ حدود ${Math.round(km/700*60)}د پرواز</div>
1539        </div>
1540      </div>`;
1541  }
1542};
1543window.calcDistance = () => Dist.calc();// ── LIVE RATES ───────────────────────────────────
1544// نرخ‌های live — global تا converter همیشه از اینها استفاده کند
1545window.LIVE_RATES = {usd:71.5, eur:78, gbp:91, sar:19, aed:19.5, pkr:0.25, irr:0.0017};
1546
1547const LiveRates = {
1548
1549  async fetchCrypto() {
1550    try {
1551      const r = await fetch(
1552        'https://api.coingecko.com/api/v3/simple/price?ids=pax-gold,bitcoin,ethereum&vs_currencies=usd&include_24hr_change=true',
1553        {headers:{'Accept':'application/json'}, signal: AbortSignal.timeout(10000)}
1554      );
1555      if (!r.ok) throw new Error(r.status);
1556      const d = await r.json();
1557
1558      const set = (id, priceId, priceKey, changeKey) => {
1559        if (!d[priceId]) return;
1560        const price = d[priceId][priceKey];
1561        const change = d[priceId][changeKey] || 0;
1562        const up = change >= 0;
1563        const fmt = p => p >= 1000 ? '$' + p.toLocaleString('en',{maximumFractionDigits:0})
1564                                   : '$' + p.toLocaleString('en',{maximumFractionDigits:2});
1565        const priceEl = document.getElementById(id+'-price');
1566        const changeEl = document.getElementById(id+'-change');
1567        const row = document.getElementById(id+'-row');
1568        if (priceEl) { priceEl.textContent = fmt(price); priceEl.className = 'fx-price '+(up?'fx-up':'fx-dn'); }
1569        if (changeEl) { changeEl.textContent = (up?'+':'')+change.toFixed(2)+'Ùª'; changeEl.className = 'fx-change '+(up?'fx-up':'fx-dn'); }
1570        if (row) { row.classList.add('flash'); setTimeout(()=>row.classList.remove('flash'),600); }
1571      };
1572
1573      set('gold','pax-gold','usd','usd_24h_change');
1574      set('btc','bitcoin','usd','usd_24h_change');
1575      set('eth','ethereum','usd','usd_24h_change');
1576
1577      const el = document.getElementById('crypto-update');
1578      const n = new Date();
1579      if (el) el.textContent = `آپدیت ${String(n.getHours()).padStart(2,'0')}:${String(n.getMinutes()).padStart(2,'0')}`;
1580
1581    } catch(e) {
1582      const el = document.getElementById('crypto-update');
1583      if (el) el.textContent = 'خطا در اتصال';
1584    }
1585  },
1586
1587  async fetchExchange() {
1588    try {
1589      const r = await fetch('https://open.er-api.com/v6/latest/USD', {signal: AbortSignal.timeout(8000)});
1590      const d = await r.json();
1591      if (!d.rates) return;
1592      const afn = d.rates.AFN || 71.5;
1593
1594      // ذخیره نرخ‌های live در متغیر global
1595      window.LIVE_RATES = {
1596        usd: afn,
1597        eur: afn / d.rates.EUR,
1598        gbp: afn / d.rates.GBP,
1599        sar: afn / d.rates.SAR,
1600        aed: afn / d.rates.AED,
1601        pkr: afn / d.rates.PKR,
1602        irr: afn / d.rates.IRR,
1603      };
1604
1605      // آپدیت ردیف‌های نرخ اسعار
1606      document.querySelectorAll('.fx-row[data-cur]').forEach(row => {
1607        const cur = row.dataset.cur;
1608        const rate = window.LIVE_RATES[cur];
1609        if (!rate) return;
1610        const priceEl = row.querySelector('.fx-price');
1611        if (priceEl) {
1612          priceEl.textContent = rate.toFixed(1) + ' افغانی';
1613          priceEl.className = 'fx-price fx-up';
1614          row.classList.add('flash');
1615          setTimeout(()=>row.classList.remove('flash'),600);
1616        }
1617      });
1618
1619      // آپدیت همه option های هر دو converter با نرخ live
1620      ['fxf','pfxf'].forEach(selId => {
1621        const sel = document.getElementById(selId);
1622        if (!sel) return;
1623        Array.from(sel.options).forEach(opt => {
1624          const cur = opt.dataset?.cur;
1625          if (cur && window.LIVE_RATES[cur]) {
1626            opt.value = window.LIVE_RATES[cur];
1627          }
1628        });
1629      });
1630      // محاسبه مجدد هر دو converter با نرخ جدید
1631      calcFx();
1632      calcPFx();
1633
1634      const upEl = document.getElementById('fx-update');
1635      const n = new Date();
1636      if (upEl) upEl.textContent = `آپدیت ${String(n.getHours()).padStart(2,'0')}
1636:${String(n.getMinutes()).padStart(2,'0')}`;
1637
1638    } catch(e) {
1639      const upEl = document.getElementById('fx-update');
1640      if (upEl) upEl.textContent = 'آفلاین';
1641    }
1642  },
1643
1644  init() {
1645    this.fetchExchange();
1646    setTimeout(()=>this.fetchCrypto(), 800);
1647    setInterval(()=>this.fetchExchange(), 5*60*1000);
1648    setInterval(()=>this.fetchCrypto(), 90*1000);
1649  }
1650};
1651
1652document.addEventListener('DOMContentLoaded', () => LiveRates.init());
1653// ── MAP (Leaflet.js) ──────────────────────────
1654const MapAF = {
1655  map: null,
1656  loaded: false,
1657
1658  PROVINCES: {
1659    'kabul':    {n:'کابل',       cap:'کابل',        pop:'۴.۶ میلیون', area:'۴,۵۲۵ km²',   lat:34.5553, lng:69.2075},
1660    'herat':    {n:'هرات',       cap:'هرات',        pop:'۲.۱ میلیون', area:'۵۴,۷۷۸ km²',  lat:34.3529, lng:62.2040},
1661    'balkh':    {n:'بلخ',        cap:'مزارشریف',    pop:'۱.۴ میلیون', area:'۱۷,۲۴۹ km²',  lat:36.7069, lng:67.1128},
1662    'kandahar': {n:'قندهار',     cap:'قندهار',      pop:'۱.۳ میلیون', area:'۵۴,۰۲۲ km²',  lat:31.6289, lng:65.7372},
1663    'nangarhar':{n:'ننگرهار',    cap:'جلال‌آباد',  pop:'۱.۷ میلیون', area:'۷,۷۲۷ km²',   lat:34.4415, lng:70.4360},
1664    'helmand':  {n:'هلمند',      cap:'لشکرگاه',     pop:'۱.۱ میلیون', area:'۵۸,۵۸۴ km²',  lat:31.5232, lng:64.1240},
1665    'ghazni':   {n:'غزنی',       cap:'غزنی',        pop:'۱.۳ میلیون', area:'۲۲,۹۱۵ km²',  lat:33.5534, lng:68.4139},
1666    'badakhshan':{n:'بدخشان',    cap:'فیض‌آباد',   pop:'۱.۰ میلیون', area:'۴۴,۰۵۹ km²',  lat:37.4406, lng:70.7944},
1667    'kunduz':   {n:'کندز',       cap:'کندز',        pop:'۱.۱ میلیون', area:'۸,۰۴۰ km²',   lat:36.7276, lng:68.8681},
1668    'faryab':   {n:'فاریاب',     cap:'میمنه',       pop:'۱.۱ میلیون', area:'۲۰,۲۹۳ km²',  lat:35.9166, lng:64.0833},
1669    'bamyan':   {n:'بامیان',     cap:'بامیان',      pop:'۴۶۰ هزار',   area:'۱۷,۴۱۴ km²',  lat:34.8269, lng:67.8197},
1670    'baghlan':  {n:'بغلان',      cap:'پل‌خمری',    pop:'۱.۰ میلیون', area:'۲۱,۱۱۸ km²',  lat:36.1322, lng:68.7208},
1671    'takhar':   {n:'تخار',       cap:'تالقان',      pop:'۱.۰ میلیون', area:'۱۲,۳۳۳ km²',  lat:36.7358, lng:69.5141},
1672    'ghor':     {n:'غور',        cap:'چغچران',      pop:'۷۵۰ هزار',   area:'۳۶,۴۷۹ km²',  lat:34.0167, lng:64.9000},
1673    'farah':    {n:'فراه',       cap:'فراه',        pop:'۵۵۰ هزار',   area:'۴۸,۴۷۱ km²',  lat:32.3742, lng:62.1168},
1674    'uruzgan':  {n:'ارزگان',     cap:'ترین‌کوت',   pop:'۴۸۰ هزار',   area:'۱۲,۶۴۰ km²',  lat:32.9269, lng:65.8786},
1675    'zabul':    {n:'زابل',       cap:'قلات',        pop:'۳۸۰ هزار',   area:'۱۷,۳۴۳ km²',  lat:32.1107, lng:67.1892},
1676    'paktia':   {n:'پکتیا',      cap:'گردیز',       pop:'۵۸۰ هزار',   area:'۶,۴۳۲ km²',   lat:33.6102, lng:69.4391},
1677    'khost':    {n:'خوست',       cap:'خوست',        pop:'۶۵۰ هزار',   area:'۴,۱۵۲ km²',   lat:33.3338, lng:69.9196},
1678    'paktika':  {n:'پکتیکا',     cap:'شرن',         pop:'۵۵۰ هزار',   area:'۱۹,۴۸۲ km²',  lat:32.5000, lng:68.5000},
1679    'kunar':    {n:'کنر',        cap:'اسعدآباد',    pop:'۵۳۰ هزار',   area:'۴,۹۴۲ km²',   lat:34.8468, lng:71.0975},
1680    'laghman':  {n:'لغمان',      cap:'مهترلام',     pop:'۴۵۰ هزار',   area:'۳,۸۴۳ km²',   lat:34.6830, lng:70.1543},
1681    'nuristan': {n:'نورستان',    cap:'پارون',       pop:'۱۶۰ هزار',   area:'۹,۲۲۵ km²',   lat:35.3253, lng:70.9072},
1682    'kapisa':   {n:'کاپیسا',     cap:'محمودراقی',   pop:'۴۵۰ هزار',   area:'۱,۸۷۱ km²',   lat:35.0219, lng:69.6178},
1683    'parwan':   {n:'پروان',      cap:'چاریکار',     pop:'۷۰۰ هزار',   area:'۵,۹۷۴ km²',   lat:35.0122, lng:69.1369},
1684    'wardak':   {n:'وردک',       cap:'میدان‌شهر',  pop:'۶۵۰ هزار',   area:'۹,۰۲۸ km²',   lat:34.0047, lng:68.6479},
1685    'logar':    {n:'لوگر',       cap:'پل‌علم',     pop:'۴۵۰ هزار',   area:'۴,۴۰۰ km²',   lat:33.9830, lng:69.1931},
1686    'panjshir': {n:'پنجشیر',     cap:'بازارک',      pop:'۱۶۰ هزار',   area:'۳,۶۱۰ km²',   lat:35.1547, lng:69.7117},
1687    'jawzjan':  {n:'جوزجان',     cap:'شبرغان',      pop:'۶۰۰ هزار',   area:'۱۱,۷۹۸ km²',  lat:36.6551, lng:65.7958},
1688    'saripul':  {n:'سرپل',       cap:'سرپل',        pop:'۵۸۰ هزار',   area:'۱۵,۹۹۹ km²',  lat:36.2347, lng:65.9193},
1689    'samangan': {n:'سمنگان',     cap:'ایبک',        pop:'۴۵۰ هزار',   area:'۱۱,۲۶۲ km²',  lat:36.1554, lng:68.0267},
1690    'badghis':  {n:'بادغیس',     cap:'قلعه‌نو',    pop:'۵۳۰ هزار',   area:'۲۰,۵۹۱ km²',  lat:35.1671, lng:63.7692},
1691    'daykundi': {n:'دایکندی',    cap:'نیلی',        pop:'۴۵۰ هزار',   area:'۱۸,۰۸۸ km²',  lat:33.6844, lng:66.0203},
1692    'nimruz':   {n:'نیمروز',     cap:'زرنج',        pop:'۲۰۰ هزار',   area:'۴۱,۰۰۵ km²',  lat:30.9226, lng:62.0282},
1693  },
1694
1695  init() {
1696    if (this.loaded) return;
1697    this.loaded = true;
1698
1699    // Load Leaflet CSS + JS
1700    if (!document.getElementById('leaflet-css')) {
1701      const css = document.createElement('link');
1702      css.id = 'leaflet-css';
1703      css.rel = 'stylesheet';
1704      css.href = 'https://unpkg.com/[email protected]/dist/leaflet.css';
1705      document.head.appendChild(css);
1706    }
1707
1708    if (window.L) {
1709      this.initMap();
1710    } else {
1711      const script = document.createElement('script');
1712      script.src = 'https://unpkg.com/[email protected]/dist/leaflet.js';
1713      script.onload = () => this.initMap();
1714      document.head.appendChild(script);
1715    }
1716  },
1717
1718  initMap() {
1719    const L = window.L;
1720    if (this.map) { this.map.remove(); }
1721
1722    this.map = L.map('leaflet-map', {
1723      center: [33.9, 66.0],
1724      zoom: 5,
1725      zoomControl: true,
1726    });
1727
1728    // OpenStreetMap tiles
1729    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
1730      attribution: '© OpenStreetMap',
1731      maxZoom: 18,
1732    }).addTo(this.map);
1733
1734    // Province markers
1735    Object.entries(this.PROVINCES).forEach(([key, p]) => {
1736      const marker = L.circleMarker([p.lat, p.lng], {
1737        radius: 8,
1738        fillColor: '#1D9E75',
1739        color: '#fff',
1740        weight: 2,
1741        opacity: 1,
1742        fillOpacity: 0.85,
1743      }).addTo(this.map);
1744
1745      marker.bindTooltip(p.n, {
1746        permanent: false,
1747        direction: 'top',
1748        className: 'map-tooltip',
1749      });
1750
1751      marker.on('click', () => {
1752        this.showProvince(key, p);
1753        marker.setStyle({fillColor: '#0a3d2b', radius: 11});
1754        setTimeout(() => marker.setStyle({fillColor: '#1D9E75', radius: 8}), 2000);
1755      });
1756    });
1757
1758    // Afghanistan border - simple rectangle approximation
1759    const afBounds = [[29.5, 60.5], [38.5, 74.9]];
1760    L.rectangle(afBounds, {
1761      color: '#1D9E75',
1762      weight: 2,
1763      fillOpacity: 0,
1764      dashArray: '6,4',
1765    }).addTo(this.map);
1766  },
1767
1768  showProvince(key, p) {
1769    document.getElementById('map-city-info').textContent = `ولایت ${p.n}`;
1770    document.getElementById('map-province-card').style.display = 'block';
1771    document.getElementById('prov-name').textContent = `ولایت ${p.n}`;
1772    document.getElementById('prov-cap').textContent = `مرکز: ${p.cap}`;
1773    document.getElementById('prov-pop').textContent = p.pop;
1774    document.getElementById('prov-area').textContent = p.area;
1775    document.getElementById('prov-city').textContent = p.cap;
1776    this.map?.flyTo([p.lat, p.lng], 8, {duration: 1.2});
1777  }
1778};
1779
1780window.mapView = (target) => {
1781  if (!MapAF.map) return;
1782  if (target === 'kabul') {
1783    MapAF.map.flyTo([34.5553, 69.2075], 10, {duration: 1.2});
1784    MapAF.showProvince('kabul', MapAF.PROVINCES.kabul);
1785  } else {
1786    MapAF.map.flyTo([33.9, 66.0], 5, {duration: 1.2});
1787    document.getElementById('map-city-info').textContent = 'روی ولایت کلیک کنید';
1788    document.getElementById('map-province-card').style.display = 'none';
1789  }
1790};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.