// /app/assets/js/app.js
// ======================================================
// Diminet Front SPA
// - Simple SPA for tabs: home / panel / help
// - Runs on current index.php layout
// - All sections are numbered for easier navigation
// ======================================================


// ===========================
// Theme (light / dark)
// ===========================
const THEME_KEY = "diminet_theme";
const APP_VERSION = window.__APP_VERSION__ || "dev";
const APP_VERSION_KEY = "diminet_app_version";

(function handleAppVersionChange() {
  try {
    const prev = localStorage.getItem(APP_VERSION_KEY);
    const curr = APP_VERSION;

    // اگر قبلاً نسخه‌ای ذخیره شده و با نسخه فعلی فرق داره
    if (prev && prev !== curr) {
      // ۱) همه localStorage رو خالی کن
      try {
        localStorage.clear();
      } catch (e) {}

      // ۲) sessionStorage رو هم (اگه استفاده کرده باشی)
      try {
        if (window.sessionStorage) {
          sessionStorage.clear();
        }
      } catch (e) {}

      // ۳) همه cacheهای PWA (service worker) رو پاک کن
      if ("caches" in window) {
        caches.keys().then((keys) => {
          keys.forEach((key) => {
            caches.delete(key);
          });
        });
      }

      // ۴) رفرش صفحه با استیت تمیز
      window.location.reload();
      return;
    }

    // بار اول یا وقتی نسخه تغییری نکرده
    localStorage.setItem(APP_VERSION_KEY, curr);
  } catch (e) {
    console.warn("version check failed", e);
  }
})();


function applyTheme(theme) {
  const root = document.documentElement;
  const btnIcon = document.getElementById("theme-toggle-icon");
  const btnLabel = document.getElementById("theme-toggle-label");

  if (!root) return;

  if (theme === "dark") {
    root.setAttribute("data-theme", "dark");
    btnIcon && (btnIcon.textContent = "🌞");
  } else {
    root.setAttribute("data-theme", "light");
    btnIcon && (btnIcon.textContent = "🌙");
  }
}

// تشخیص اولیه: اول localStorage، بعد تنظیمات سیستم
(function initTheme() {
  let saved = null;
  try {
    saved = localStorage.getItem(THEME_KEY);
  } catch (_) {}

  if (saved === "light" || saved === "dark") {
    applyTheme(saved);
    return;
  }

  const prefersDark = window.matchMedia &&
    window.matchMedia("(prefers-color-scheme: dark)").matches;

  applyTheme(prefersDark ? "dark" : "light");
})();




document.addEventListener("DOMContentLoaded", () => {
  // ====================================================
  // [1] Global Boot & State
  // ====================================================

  const appRoot = document.getElementById("app-root");
  const viewContainer = document.getElementById("view-container");
  const headerLoginBtn = document.getElementById("btn-header-login");
  const bottomNavButtons = document.querySelectorAll(".bottom-nav-item");
  const appMainEl = document.querySelector(".app-main");


  // [1.1] Boot from PHP + URL
  const urlParams = new URLSearchParams(location.search);
  const boot = window.__APP_BOOT__ || {};
  
    // [1.1.a] Initial plan filters from URL (for deep links / renew buttons)
  const initialPlanFromUrl = (function () {
    const initialTab = urlParams.get("tab");
    const shouldUse =
      !initialTab || initialTab === "home"; // فقط وقتی ورودی اول صفحه روی هومه

    if (!shouldUse) {
      return {
        panelKey: "",
        durationMonths: null,
      };
    }
    
   const planCode =
      urlParams.get("plan") ||
      urlParams.get("plan_code") ||
      urlParams.get("planCode") ||
      "";


    const panelKeyRaw =
      urlParams.get("panel_key") || urlParams.get("panelKey") || "";
    const panelKey = panelKeyRaw.trim();

    const durationRaw = urlParams.get("duration");
    let durationMonths = null;

    if (durationRaw != null && durationRaw !== "") {
      const n = parseInt(durationRaw, 10);
      if (!Number.isNaN(n) && n > 0) {
        // واحد: ماه
        durationMonths = Math.max(1, Math.min(36, n));
      }
    }

    return {
      panelKey,
      durationMonths,
      planCode,
};
  })();

  // فقط یک‌بار از این preselect استفاده می‌کنیم
  let initialPlanPreselectUsed = false;


    const hamburgerBtn = document.getElementById("btn-hamburger");
  const mobileDrawer = document.getElementById("mobileDrawer");
  const mobileDrawerBackdrop = document.getElementById("mobileDrawerBackdrop");

  // دکمه تغییر تم (رو هدر)
  syncThemeToggleIcon();




  const themeBtn = document.getElementById("btn-theme-toggle");
  if (themeBtn) {
    themeBtn.addEventListener("click", () => {
      const htmlEl = document.documentElement;
      const current = htmlEl.getAttribute("data-theme") || "light";
      const next = current === "dark" ? "light" : "dark";

      htmlEl.setAttribute("data-theme", next);
      try {
        localStorage.setItem("diminet-theme", next);
      } catch (e) {}

      syncThemeToggleIcon();
    });
  }
  // [1.2] Global in-memory state
  const state = {
      
    // [1.2.1] Auth & renew info
    isLoggedIn: !!boot.isLogged,
    renewUsername: urlParams.get("renew") || boot.renew_username || "",

    // [1.2.2] Current user
    user: {
      email: boot.email || "",
      siteUsername: "",
      activeCount: 0,
      expiredCount: 0,
      walletToman: 0,
      telegramLinked: false,
      telegramUsername: "",
      telegramBotUrl: "https://t.me/dimobybot",
      telegramSupportUrl: "https://t.me/dimynet",
      telegramChannelUrl: "https://t.me/diminett",
    },

    // [1.2.3] Data lists
    services: [], // from /panel_data.php
    payments: [], // from /panel_data.php

    // [1.2.4] Plans
    plans: [], // from /plans.php
    plansLoading: false,
    plansLoaded: false,
    plansError: null,

    // [1.2.5] Announcements & notifications
    announcements: [],
    notifications: [],
    notificationsUnread: 0,

    // [1.2.6] Refund flow
    refundTargetUsername: "",
    refundTargetServiceId: 0,
    
  };
  
  let triedAutoAnnouncementModal = false;
  let currentServerTypeFilter = null;
  let currentLocationFilter = null;
  let currentDurationFilter = null;


    const offlineOverlay = document.getElementById("offlineOverlay");
  let isOfflineLocked = false;
  let pendingAnnouncementId = null;
  let pendingAnnouncementData = null;



function syncThemeToggleIcon() {
  const htmlEl = document.documentElement;
  const isDark = htmlEl.getAttribute("data-theme") === "dark";

  const btn = document.getElementById("btn-theme-toggle");
  if (!btn) return;

  const iconEl = btn.querySelector(".theme-toggle-btn__icon");
  const labelEl = btn.querySelector(".theme-toggle-btn__label");

  if (iconEl) {
    // کلاس‌ها رو کامل ست کن تا قاطی نشه
    if (isDark) {
      iconEl.className = "fa-solid fa-sun theme-toggle-btn__icon";
    } else {
      iconEl.className = "fa-regular fa-moon theme-toggle-btn__icon";
    }
  }

  if (labelEl) {
    labelEl.textContent = isDark ? "روشن" : "تیره";
  }
}


  // [1.3] Modal IDs registry
  const modalIds = {
    login: "modal-login",
    payments: "modal-payments",
    "change-pass": "modal-change-pass",
    success: "modal-success",
    failed: "modal-failed",
    qr: "modal-qr",
    configs: "modal-configs",
    video: "modal-video",
    wallet: "modal-wallet",
    wallet_success: "modal-wallet-success",
    terms: "modal-terms",
    forgot: "modal-forgot",
    refund: "modal-refund",
    announcements: "modal-announcements",
    notifications: "modal-notifications",
    announcement_auto: "modal-announcement-auto",
        usage: "modal-usage",
    push_guide: "modal-push-guide",
    pwa_guide: "modal-pwa-guide",
    apn_guide: "modal-apn-guide",
  };

  // [1.4] Router state
  let currentRoute = getInitialRoute();

  // [1.5] PWA install prompt holder
  let deferredPwaPrompt = null;
  
  // [1.6] Service Worker registration (برای پوش)
  let swRegistration = null;
  
    // [1.7] Auto-announcement modal state
  let pendingAnnouncementModalId = null;
  
  let pendingExtraModalKey = null;




  // ====================================================
  // [2] Init flow
  // ====================================================

  (async function init() {
    updateHeaderButton();
    setupHeaderLogin();
    setupBottomNav();
    buildModals();
    setupNotificationsBell();
    setupPwaInstallButton();
    setupHamburgerMenu();
    

    // [2.1] Initial render
    renderRoute(currentRoute);

    // [2.2] Load async data in parallel
    await Promise.all([
      loadPlans(),
      state.isLoggedIn ? loadPanelData() : Promise.resolve(),
      loadAnnouncements(),
      state.isLoggedIn ? loadNotifications() : Promise.resolve(),
    ]);

    // [2.3] Re-render current route after data load
    renderRoute(currentRoute);

    // [2.4] Handle popup from boot / URL
    handleAutoPopupFromURL();
    
    maybeShowAnnouncementAutoModal();

    // [2.5] Init PWA & (future) push
    initPwaAndPush();
    
    // [2.6] Connectivity checks
    checkConnectivity();                  // یکبار همین الان
    setInterval(checkConnectivity, 3000); // هر 3 ثانیه
    
        // [2.7] وقتی تب دوباره فعال شد، اطلاعیه‌ها را ریفچ کن
    document.addEventListener("visibilitychange", () => {
      if (!document.hidden) {
        // تب برگشته روی فوکوس → از سرور اطلاعیه‌ها رو دوباره بگیر
        loadAnnouncements().catch(() => {});
            if (state.isLoggedIn) {
      // اعلانات شخصی کاربر را هم به‌روز کن
      loadNotifications().catch(() => {});
            }
      }
    });


  })();



  // ====================================================
  // [3] Router helpers
  // ====================================================

  function startRenewFlow(username) {
    if (!username) return;
    state.renewUsername = username;
    setRoute("home", { force: true });
  }

  function getInitialRoute() {
    const tab = urlParams.get("tab");
    if (tab === "panel" || tab === "help" || tab === "home") {
      return tab;
    }
    return "home";
  }

  function setRoute(route, opts = {}) {
    const force = !!opts.force;
    if (!force && route === currentRoute) return;

    // clear renew mode when leaving home
    if (state.renewUsername && route !== "home") {
      state.renewUsername = "";
    }

    currentRoute = route;
    updateNavActive();

    showLoader();
    setTimeout(() => {
      renderRoute(route);
      hideLoader();
      closeMobileDrawer();
      scrollToViewTop();
      updateUrl(route);
    }, 180);
  }

  function updateUrl(route) {
    const url = new URL(location.href);

    // remove renew + popup flags from query
    url.searchParams.delete("renew");
    url.searchParams.delete("show_popup");

    url.searchParams.delete("panel_key");
    url.searchParams.delete("duration");
    url.searchParams.delete("plan");

    // set current tab
    url.searchParams.set("tab", route);

    history.replaceState({}, "", url.toString());
  }

  function updateNavActive() {
    bottomNavButtons.forEach((btn) => {
      const r = btn.getAttribute("data-route");
      btn.classList.toggle("is-active", r === currentRoute);
    });
  }

  function showLoader() {
    document.body.classList.add("is-loading");
  }

function hideLoader() {
  if ("fonts" in document && document.fonts && document.fonts.ready) {
    document.fonts.ready
      .then(() => {
        document.body.classList.remove("is-loading");
      })
      .catch(() => {
        document.body.classList.remove("is-loading");
      });
  } else {
    document.body.classList.remove("is-loading");
  }
}

  function scrollToViewTop() {
    // سعی کن هرچی اسکرول‌کننده داریم ببری بالا
    const scrollers = [];

    // اگه ساختار اسکرول روی .app-main باشه
    if (appMainEl) {
      scrollers.push(appMainEl);
    }

    // خود صفحه (برای وقتی که body / html اسکرول می‌خوره)
    scrollers.push(document.documentElement, document.body);

    scrollers.forEach((el) => {
      if (!el) return;

      // اول با API مدرن
      if (typeof el.scrollTo === "function") {
        try {
          el.scrollTo({
            top: 0,
            left: 0,
            behavior: "smooth", // اگه نرم می‌خوای کنش smooth
          });
          return;
        } catch (e) {}
      }

      // بک‌آپ دستی
      try {
        el.scrollTop = 0;
      } catch (e) {}
    });
  }



  // ====================================================
  // [4] Generic utils
  // ====================================================

  function escapeHtml(str) {
    if (str == null) return "";
    return String(str)
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;");
  }

  // [4.1] Latin digits → Persian digits (for display)
  function toFaDigits(str) {
    const map = {
      0: "۰",
      1: "۱",
      2: "۲",
      3: "۳",
      4: "۴",
      5: "۵",
      6: "۶",
      7: "۷",
      8: "۸",
      9: "۹",
      ",": "٬",
      "/": "/",
    };
    return String(str).replace(/[0-9,\/]/g, (ch) =>
      Object.prototype.hasOwnProperty.call(map, ch) ? map[ch] : ch
    );
  }

  function applyFaDigits(root) {
    const target = root || document.body;
    if (!target) return;

    const digitsFa = "۰۱۲۳۴۵۶۷۸۹";

    const walker = document.createTreeWalker(
      target,
      NodeFilter.SHOW_TEXT,
      null
    );

    let node;
    while ((node = walker.nextNode())) {
      const parent = node.parentElement;
      if (!parent) continue;

      // skip inputs / textareas / elements explicitly marked to keep EN digits
      if (
        parent.closest("input, textarea") ||
        parent.closest("[data-keep-en-digits]")
      ) {
        continue;
      }

      const oldText = node.nodeValue;
      if (!oldText) continue;

      const newText = oldText.replace(/[0-9]/g, (d) => digitsFa[d]);
      if (newText !== oldText) {
        node.nodeValue = newText;
      }
    }
  }

  function clampNumber(num, min, max) {
    num = Number(num) || 0;
    return Math.min(max, Math.max(min, num));
  }
  

  
    // ====================================================
  // [X] Mobile hamburger menu & drawer
  // ====================================================


  function closeMobileDrawer() {
  if (!mobileDrawer || !mobileDrawerBackdrop || !hamburgerBtn) return;
  mobileDrawer.classList.remove("is-open");
  mobileDrawerBackdrop.classList.remove("is-open");
  hamburgerBtn.classList.remove("is-open");
  document.body.classList.remove("drawer-open");
}


function setupHamburgerMenu() {
  if (!hamburgerBtn || !mobileDrawer || !mobileDrawerBackdrop) return;

  // باز / بسته کردن خود منو
  hamburgerBtn.addEventListener("click", () => {
    const isOpen = mobileDrawer.classList.toggle("is-open");
    mobileDrawerBackdrop.classList.toggle("is-open", isOpen);
    hamburgerBtn.classList.toggle("is-open", isOpen);
    document.body.classList.toggle("drawer-open", isOpen);
  });

  mobileDrawerBackdrop.addEventListener("click", () => {
    closeMobileDrawer();
  });

  document.addEventListener("keydown", (e) => {
    if (e.key === "Escape") {
      closeMobileDrawer();
    }
  });

  // اکشن‌های مخصوص لینک‌های میانبر داخل منو
  mobileDrawer
    .querySelectorAll("[data-drawer-action]")
    .forEach((link) => {
      link.addEventListener("click", (evt) => {
        evt.preventDefault();
        const action = link.getAttribute("data-drawer-action");
        handleDrawerAction(action);
      });
    });
}
  
  
  function handleDrawerAction(action) {
  if (!action) return;

  switch (action) {
    case "payments": {
      if (!state.isLoggedIn) {
        // اگر لاگین نیست، منو رو ببند و مدال لاگین باز کن
        closeMobileDrawer();
        openModal("login");
        return;
      }
      // لاگین هست → تاریخچه پرداخت‌ها
      fillPaymentsModalTable();
      closeMobileDrawer();
      openModal("payments");
      break;
    }

    case "telegram-bot": {
      const url = state.user.telegramBotUrl || "https://t.me/dimobybot";
      closeMobileDrawer();
      window.open(url, "_blank");
      break;
    }

    case "change-pass": {
      if (!state.isLoggedIn) {
        closeMobileDrawer();
        openModal("login");
        return;
      }
      closeMobileDrawer();
      openModal("change-pass");
      break;
    }

    case "telegram-channel": {
      const url =
        state.user.telegramChannelUrl || "https://t.me/diminett";
      closeMobileDrawer();
      window.open(url, "_blank");
      break;
    }

    default:
      break;
  }
}



  // ====================================================
  // [5] Price helpers
  // ====================================================

  // [5.1] amount_rial → short "thousand Toman" style ("۱/۵")
  function priceMainBlock(amount_rial) {
    const rial = Number(amount_rial) || 0;
    const toman = rial / 10;
    const thousand = toman / 1000;

    const intPart = Math.floor(thousand);
    const decPart = thousand - intPart;

    let mainNum;
    if (decPart === 0) {
      mainNum = String(intPart);
    } else {
      const oneDecimal = Math.round(decPart * 10);
      mainNum = intPart + "/" + oneDecimal;
    }
    return toFaDigits(mainNum);
  }

  function priceOldBlock(amount_old_rial) {
    const v = Number(amount_old_rial) || 0;
    if (v <= 0) return "";
    return priceMainBlock(v);
  }

  function discountPercent(old_rial, now_rial) {
    const old = Number(old_rial) || 0;
    const now = Number(now_rial) || 0;
    if (old <= 0 || old <= now) return "";
    const diff = old - now;
    const pct = (diff / old) * 100;
    const pctRnd = Math.floor(pct);
    return toFaDigits(pctRnd);
  }

  // [5.2] Rial → full Toman label, e.g. "۱۵۰٬۰۰۰ تومان"
  function formatTomanFullFa(amount_rial) {
    const rial = Number(amount_rial) || 0;
    if (!rial) return "";
    const toman = Math.floor(rial / 10);
    const en = toman.toLocaleString("en-US");
    const fa = toFaDigits(en);
    return fa + " تومان";
  }

  // [5.3] Toman → full label
  function formatTomanFullFaFromToman(amount_toman) {
    const toman = Number(amount_toman) || 0;
    const en = toman.toLocaleString("en-US");
    const fa = toFaDigits(en);
    return fa + " تومان";
  }


// xui = تک سرور  | marzban = مولتی سرور
function normalizeTextForDetect(value) {
  return (value || "").toString().toLowerCase();
}


// 🔹 همه‌ی لوکیشن‌ها از روی panel_key (برای ساخت چند پرچم مولتی)
function detectAllLocationsFromPanelKey(panelKey) {
  const v = normalizeTextForDetect(panelKey);
  if (!v) return [];

  // هر کدی که داری اینجا اضافه کن
  const knownCodes = ["tr", "de", "us", "fi"];

  const result = [];
  knownCodes.forEach((code) => {
    if (v.includes(code)) {
      result.push(code);
    }
  });

  // اگر هیچ‌کدوم نبود و تو panel_key چیزی مثل "multi" نوشتی
  if (!result.length && v.includes("multi")) {
    result.push("multi");
  }

  return result;
}


// 🔹 تشخیص نوع سرور از روی panel_key
function detectServerTypeFromPanelKey(panelKey) {
  const v = normalizeTextForDetect(panelKey);

  if (!v) return "single";

  if (v.includes("mrz") || v.includes("marz") || v.includes("marzban")) {
    return "multi";
  }

  if (v.includes("xui") || v.includes("x-ui")) {
    return "single";
  }

  // پیش‌فرض
  return "single";
}

// 🔹 تشخیص لوکیشن از روی panel_key
function detectLocationFromPanelKey(panelKey) {
  const v = normalizeTextForDetect(panelKey);
  if (!v) return "multi";

  const hasTR = v.includes("tr");
  const hasDE = v.includes("de");
  const hasUS = v.includes("us");
  const hasFI = v.includes("fi");

  const hitsCount = [hasTR, hasDE, hasUS, hasFI].filter(Boolean).length;

  // اگه بیش از یک لوکیشن پیدا شد → یعنی مولتی لوکیشن
  if (hitsCount > 1) {
    return "multi";
  }

  // فقط وقتی دقیقا یه لوکیشن پیدا شده، تک‌سرور حسابش کن
  if (hasTR) return "tr";
  if (hasDE) return "de";
  if (hasUS) return "us";
  if (hasFI) return "fi";

  // پیش‌فرض
  return "multi";
}


// 🔹 تشخیص نوع مسیر (مستقیم / تانل) از روی panel_key
// st  → direct
// tu  → tunnel
function detectTunnelModeFromPanelKey(panelKey) {
  const v = normalizeTextForDetect(panelKey);
  if (!v) return "none";

  // با جداکننده‌های غیرحرفی/عددی تیکه‌تیکه می‌کنیم
  const tokens = v.split(/[^a-z0-9]+/).filter(Boolean);

  if (tokens.includes("tu")) return "tunnel";
  if (tokens.includes("st")) return "direct";

  return "none";
}

// 🔹 خروجی HTML تگ مسیر برای پلن/سرویس (استپ ۳، کارت سرویس‌ها)
function renderTunnelTag(mode) {
  if (mode === "direct") {
    return `
      <span class="plan-meta-tag plan-meta-tag--direct">
        مستقیم
      </span>
    `;
  }
  if (mode === "tunnel") {
    return `
      <span class="plan-meta-tag plan-meta-tag--tunnel">
        <i class="fa-solid fa-bolt" aria-hidden="true"></i>
        <span>VIP تانل</span>
      </span>
    `;
  }
  return "";
}

// 🔹 خروجی کوچک‌تر برای استپ ۱ روی دکمه‌های انتخاب سرور
function renderTunnelBadgeSmall(mode) {
  if (mode === "direct") {
    return `
      <div class="plans-step__option-extra">
        <span class="plan-meta-tag plan-meta-tag--direct">
          مستقیم
        </span>
      </div>
    `;
  }
  if (mode === "tunnel") {
    return `
      <div class="plans-step__option-extra">
        <span class="plan-meta-tag plan-meta-tag--tunnel">
          <i class="fa-solid fa-bolt" aria-hidden="true"></i>
          <span>VIP تانل</span>
        </span>
      </div>
    `;
  }
  return "";
}



// 🔹 اطلاعات لوکیشن + آدرس پرچم
function getLocationMeta(locationCode) {
  const code = (locationCode || "").toLowerCase();

  switch (code) {
    case "tr":
      return {
        code: "tr",
        name: "ترکیه",
        flagUrl: "/assets/tr.png",
      };
    case "de":
      return {
        code: "de",
        name: "آلمان",
        flagUrl: "/assets/de.png",
      };
    case "us":
      return {
        code: "us",
        name: "آمریکا",
        flagUrl: "/assets/us.png",
      };
    case "fi":
      return {
        code: "fi",
        name: "فنلاند",
        flagUrl: "/assets/fi.png",
      };
    case "multi":
    default:
      return {
        code: "multi",
        name: "مولتی سرور",
        flagUrl: "",
      };
  }
}
 
function detectPlanDurationMonths(row) {
  const dRaw =
    row.duration_days ??
    row.durationDays ??
    0;

  const days = Number(dRaw) || 0;
  if (!days) {
    return 1; // پیش‌فرض ۱ ماهه، اگر دیتای روز خالی بود
  }

  // هر ۳۰ روز = ۱ ماه
  const months = Math.floor(days / 30);

  return months > 0 ? months : 1;
}

function getPlanServerTypeLabel(serverType) {
  if (serverType === "multi") {
    return "مولتی سرور";
  }
  return "تک سرور";
}

function getServerTypeMeta(serverType) {
  const type = serverType === "multi" ? "multi" : "single";
  const label = getPlanServerTypeLabel(type);

  const iconClass =
    type === "multi"
      ? "fa-solid fa-layer-group" // مولتی سرور
      : "fa-solid fa-server";     // تک سرور

  const desc =
    type === "multi"
      ? "چندین سرور در لوکیشن‌های مختلف؛ مناسب چند دستگاه و جابه‌جایی بین سرورها."
      : "اتصال پایدار به یک سرور ثابت؛ مناسب استفاده روزمره و شبکه‌های اجتماعی.";

  return { type, label, iconClass, desc };
}

function getDurationMeta(months) {
  const m = Number(months) || 1;
  const label = getPlanDurationLabel(m);

  let desc = "مدت سرویس: " + label;
  if (m === 1) {
    desc = "مناسب شروع و تست سرویس.";
  } else if (m === 2 || m === 3) {
    desc = "به‌صرفه‌تر از یک‌ماهه برای استفاده منظم.";
  } else if (m >= 6) {
    desc = "اقتصادی‌ترین گزینه برای استفاده طولانی‌مدت.";
  }

  return {
    months: m,
    label,
    iconClass: "fa-regular fa-clock",
    desc,
  };
}

function getPlanDurationLabelForPlan(plan) {
  // مجموع روزها (اگر از بک‌اند فرستاده باشی)
  const daysTotal =
    Number(plan.duration_days ?? plan.durationDays ?? 0) || 0;

  // ماه‌ها (یا از خود پلن، یا از روی روزها)
  let months =
    Number(plan.duration_months ?? plan.durationMonths) || 0;

  if (!months && daysTotal > 0) {
    months = Math.floor(daysTotal / 30);
  }

  // --- حالت‌های خاص بر اساس روز ---

  // ۱ روزه → تست یک روزه
  if (daysTotal === 1) {
    return "تست یک روزه";
  }

  // ۷ روزه → یک هفته‌ای
  if (daysTotal === 7) {
    return "۱ هفته‌ای";
  }

  // ۱۵ روزه → ۱۵ روزه
  if (daysTotal === 15) {
    return toFaDigits("15") + " روزه";
  }

  // کمتر از ۳۰ روز (و جزو سه حالت بالا نیست) → X روزه
  if (daysTotal > 0 && daysTotal < 30 && months <= 0) {
    return toFaDigits(String(daysTotal)) + " روزه";
  }

  // --- حالت‌های ماهیانه ---
  const m = months > 0 ? months : 1;
  const baseLabel = getPlanDurationLabel(m); // مثلا "۱ ماهه"

  // روز اضافه نسبت به ماه‌ها
  let extraDays = 0;
  if (daysTotal > 0) {
    extraDays = daysTotal - m * 30;
  }

  if (extraDays > 0) {
    const extraFa = toFaDigits(String(extraDays));
    return baseLabel + " + " + extraFa + " روز";
  }

  return baseLabel;
}


function getPlanDurationLabel(months) {
  const m = Number(months) || 1;
  if (m === 1) return "۱ ماهه";
  if (m === 2) return "۲ ماهه";
  if (m === 3) return "۳ ماهه";
  const numFa = toFaDigits(String(m));
  return numFa + " ماهه";
}

  // ====================================================
  // [6] Header, login button & logout
  // ====================================================

  function updateHeaderButton() {
    if (!headerLoginBtn) return;

    headerLoginBtn.classList.remove("btn--primary", "btn--danger");

    const notifBtn = document.getElementById("btn-notifications");

    if (state.isLoggedIn) {
      headerLoginBtn.textContent = "خروج";
      headerLoginBtn.classList.add("btn--danger");

      // زنگوله فقط وقتی لاگین هستیم دیده شود
      if (notifBtn) {
        notifBtn.style.display = "inline-flex";
      }
    } else {
      headerLoginBtn.textContent = "ورود به پنل";
      headerLoginBtn.classList.add("btn--primary");

      // موقع مهمان بودن، زنگوله قایم شود
      if (notifBtn) {
        notifBtn.style.display = "none";
      }
    }
  }

  function setupHeaderLogin() {
    if (!headerLoginBtn) return;
    headerLoginBtn.addEventListener("click", () => {
      if (state.isLoggedIn) {
        handleLogoutClick();
      } else {
        openModal("login");
      }
    });
  }

  async function handleLogoutClick() {
    showLoader();
    try {
      await fetch("/logout.php", {
        method: "GET",
        credentials: "same-origin",
      });
    } catch (err) {
      console.error("logout error", err);
    } finally {
      state.isLoggedIn = false;
      state.user.email = "";
      state.user.siteUsername = "";
      state.user.activeCount = 0;
      state.user.expiredCount = 0;
      state.user.walletToman = 0;
      state.services = [];
      state.payments = [];

      updateHeaderButton();
      

      // soft refresh home with loader, even if already on home
      setRoute("home", { force: true });
    }
  }

  function setupBottomNav() {
    bottomNavButtons.forEach((btn) => {
      btn.addEventListener("click", () => {
        const route = btn.getAttribute("data-route");
        setRoute(route);
      });
    });
    updateNavActive();

  }

  // ====================================================
  // [7] API calls: plans + panel data
  // ====================================================

  // [7.1] Load plans
  async function loadPlans() {
    state.plansLoading = true;
    state.plansError = null;
    showLoader();

    try {
      const res = await fetch("/plans.php", {
        method: "GET",
        credentials: "same-origin",
      });
      if (!res.ok) throw new Error("HTTP " + res.status);

      const data = await res.json();
      if (!data.ok) throw new Error("not ok");

const raw = Array.isArray(data.plans) ? data.plans : [];
state.plans = raw.map((row) => {
  const base = {
    id: row.id,
    code: row.code || "",
    title: row.title || "",
    panel_key: row.panel_key || row.panelKey || "",
    plan_desc: row.plan_desc || "",
    imgurl: row.imgurl || "",
    imgurlmob: row.imgurlmob || "",
    amount_rial:
      Number(row.amount_rial ?? row.price_rial ?? row.price ?? 0) || 0,
    amount_old_rial:
      Number(row.amount_old_rial ?? row.old_price_rial ?? 0) || 0,
    is_recommended: row.is_recommended == 1 || row.is_recommended === "1",
    duration_days:
      Number(row.duration_days ?? row.durationDays ?? 0) || 0,
  };

  // مدت سرویس
  const durationMonthsRaw =
    Number(row.duration_months ?? row.durationMonths ?? 0) || 0;
  const durationMonths =
    durationMonthsRaw > 0 ? durationMonthsRaw : detectPlanDurationMonths(row);

  // نوع سرور + لوکیشن از روی panel_key
  const serverType   = detectServerTypeFromPanelKey(base.panel_key);  // "single" | "multi"
  const locationCode = detectLocationFromPanelKey(base.panel_key);    // "tr" | "de" | "us" | "fi" | "multi"

  // 🔹 نوع مسیر (st/tu)
  const tunnelMode = detectTunnelModeFromPanelKey(base.panel_key);    // "direct" | "tunnel" | "none"

  return {
    ...base,
    server_type: serverType,
    location_code: locationCode,
    duration_months: durationMonths,
    tunnel_mode: tunnelMode,
  };
});



      state.plansLoaded = true;
    } catch (err) {
      console.error("plans fetch error", err);
      state.plansError = "خطا در دریافت پلن‌ها";
    } finally {
      state.plansLoading = false;
      hideLoader();
    }
  }

  // [7.2] Load panel data (user + services + payments)
  async function loadPanelData() {
    showLoader();
    try {
      const res = await fetch("/panel_data.php", {
        method: "GET",
        credentials: "same-origin",
      });

      if (res.status === 401) {
        console.warn("panel_data: not logged in (401)");
        state.isLoggedIn = false;
        updateHeaderButton();
        return;
      }

      if (!res.ok) {
        console.warn("panel_data HTTP error", res.status);
        return;
      }

      const data = await res.json();
      console.log("panel_data result:", data);

      if (!data.ok) {
        console.warn("panel_data not ok", data);
        return;
      }

      const u = data.user || {};

      state.user.email = u.email || state.user.email || boot.email || "";
      state.user.siteUsername = u.site_username || u.siteUsername || "";

      // [7.2.1] Telegram info
      const tg = u.telegram || {};
      state.user.telegramLinked = !!(tg.id || tg.username);
      state.user.telegramUsername = tg.username || "";

      // [7.2.2] Wallet (wallet_balance is Rial)
      let walletToman = 0;

      if (u.wallet_balance != null) {
        const rialNum =
          typeof u.wallet_balance === "number"
            ? u.wallet_balance
            : Number(String(u.wallet_balance).replace(/[^\d]/g, "")) || 0;

        walletToman = Math.floor(rialNum / 10);
      } else {
        const wAny =
          u.wallet ??
          u.balance ??
          u.wallet_toman ??
          u.walletToman ??
          u.wallet_balance_toman;

        if (typeof wAny === "number") {
          walletToman = wAny;
        } else if (typeof wAny === "string" && wAny.trim() !== "") {
          walletToman = Number(wAny.replace(/[^\d]/g, "")) || 0;
        }
      }

      state.user.walletToman = walletToman;

      // [7.2.3] Stats
      const stats = data.stats || {};
      if (typeof stats.services_active === "number") {
        state.user.activeCount = stats.services_active;
      }
      if (typeof stats.services_expired === "number") {
        state.user.expiredCount = stats.services_expired;
      }

      // [7.2.4] Services
      const rawServices = Array.isArray(data.services) ? data.services : [];
      const normServices = rawServices.map(normalizeServiceFromApi);
      state.services = normServices;

      if (!stats.services_active && !stats.services_expired) {
        let active = 0;
        let expired = 0;
        normServices.forEach((svc) => {
          if (svc.status === "active") active++;
          else if (svc.status === "expired" || svc.status === "disabled")
            expired++;
        });
        state.user.activeCount = active;
        state.user.expiredCount = expired;
      }

      // [7.2.5] Payments
      const rawPayments = Array.isArray(data.payments) ? data.payments : [];
      state.payments = rawPayments.map(normalizePaymentFromApi);
    } catch (err) {
      console.error("panel_data fetch error", err);
    } finally {
      hideLoader();
    }
  }

  // [7.3] Normalizers
  function normalizeServiceFromApi(row) {
    const username =
      row.username ||
      row.service_username ||
      row.user_name ||
      row.marzban_user ||
      "";

    const statusRaw = (row.status || row.state || "").toString().toLowerCase();
    const status = statusRaw || "active";

    const usedGB =
      parseFloat(
        row.used_gb ??
          row.usedGB ??
          row.usage_gb ??
          row.traffic_used_gb ??
          0
      ) || 0;

    const totalGB =
      parseFloat(
        row.total_gb ??
          row.totalGB ??
          row.data_limit_gb ??
          row.limit_gb ??
          0
      ) || 0;

    let remainingGB =
      parseFloat(
        row.remaining_gb ??
          row.remain_gb ??
          row.left_gb ??
          row.remainingGB ??
          0
      ) || 0;

    if (!remainingGB && totalGB) {
      remainingGB = Math.max(totalGB - usedGB, 0);
    }

    const expireDays =
      parseInt(
        row.expire_days ??
          row.days_left ??
          row.remaining_days ??
          row.left_days ??
          0
      ) || 0;

    const subscriptionUrl =
      row.sub_url ||
      row.subscription_url ||
      row.sub_link ||
      row.link ||
      row.url ||
      "";

    const percent = totalGB > 0 ? Math.round((usedGB / totalGB) * 100) : 0;

    const configs = Array.isArray(row.configs) ? row.configs : [];
    
    const panelKey =
    row.panel_key ||
    row.panelKey ||
    "";
    
      const serverType   = detectServerTypeFromPanelKey(panelKey);   // "single" | "multi"
  const locationCode = detectLocationFromPanelKey(panelKey);     // "tr" | "de" | "us" | "fi" | "multi"
  const tunnelMode   = detectTunnelModeFromPanelKey(panelKey);


    return {
      id: row.id,
      username,
      status,
      usedGB,
      totalGB,
      remainingGB,
      expireDays,
      subscriptionUrl,
      percent,
      configs,
      panelKey,
    serverType,
    locationCode,
    tunnelMode,

    };
  }

  function normalizePaymentFromApi(row) {
    const created =
      row.created_at_fa ||
      row.created_at ||
      row.created ||
      row.date ||
      "";
    const planTitle = row.plan_title || row.plan || "";
    const amountNum =
      Number(row.amount_rial ?? row.amount ?? row.amount_toman ?? 0) || 0;

    const amountFa =
      row.amount_rial_fa ||
      (amountNum ? amountNum.toLocaleString("fa-IR") : row.amountFa || "");

    const refFa = row.ref_id || row.ref || row.ref_code || "";

    let statusRaw = (row.status || "").toString().toLowerCase();
    let status = statusRaw;
    if (["success", "paid", "ok"].includes(statusRaw)) status = "paid";
    else if (["failed", "error", "cancel"].includes(statusRaw))
      status = "failed";

    return {
      id: row.id,
      dateFa: created,
      planTitle,
      amountFa,
      refFa,
      status,
    };
  }

  // ====================================================
  // [8] Announcements & notifications (API + helpers)
  // ====================================================

  async function loadAnnouncements() {
    try {
      const res = await fetch("/announcement.php", {
        method: "GET",
        credentials: "same-origin",
      });
      if (!res.ok) return;
      const data = await res.json().catch(() => null);
      if (!data) return;

      const raw =
        (Array.isArray(data.items) && data.items) ||
        (Array.isArray(data.announcements) && data.announcements) ||
        (Array.isArray(data) && data) ||
        [];

      state.announcements = raw.map((row) => ({
        id: row.id,
        title: row.title || "",
        body: row.body || row.message || "",
        level: row.level || "info",
        createdAt:
          row.created_at_fa ||
          row.date_fa ||
          row.created_at ||
          row.createdAt ||
          "",
      }));
      maybeAutoOpenAnnouncementModalOnce();
    } catch (err) {
      console.error("announcements fetch error", err);
    }
  }
  
  
function maybeAutoOpenAnnouncementModalOnce() {
  const list = Array.isArray(state.announcements)
    ? state.announcements
    : [];
  if (!list.length) return;

  const latest = list[0];
  if (!latest || !latest.id) return;

  const key = "diminet_announcement_seen_" + latest.id;

  // اگه این اعلان رو قبلاً دیدیم، بی‌خیال
  try {
    if (window.localStorage && localStorage.getItem(key) === "1") {
      return;
    }
  } catch (e) {
    // اگه localStorage خطا داد، فقط ادامه می‌دیم
  }

  // محتوا رو برای مودال اطلاعیه‌ها پر کن
  fillAnnouncementsModal();

  const annModalId = modalIds.announcements
    ? String(modalIds.announcements)
    : null;
  if (!annModalId) return;

  const modalEl = document.getElementById(annModalId);
  if (!modalEl) return;

  // اگه همین الان یه مودال دیگه بازه (success, wallet, login ...)
  if (document.body.classList.contains("has-modal")) {
    // بذار تو صف که بعد از بسته شدن اون مودال نشون داده بشه
    pendingAnnouncementModalId = annModalId;
    return;
  }

  // در غیر این صورت همین الان مودال اطلاعیه رو باز کن
  openModalByElement(modalEl);

  // و علامت بزن که این اعلان دیده شده
  try {
    if (window.localStorage) {
      localStorage.setItem(key, "1");
    }
  } catch (e) {}
}

  
  
    // آخرین اطلاعیه عمومی (برای نوار بالای صفحه و مودال خودکار)
  function getLatestAnnouncement() {
    const list = Array.isArray(state.announcements)
      ? state.announcements
      : [];
    if (!list.length) return null;
    return list[0];
  }

  // پر کردن متن مودال اطلاعیه خودکار
  function fillAutoAnnouncementModal(announcement) {
    if (!announcement) return;

    const titleEl = document.getElementById("autoAnnTitle");
    const bodyEl = document.getElementById("autoAnnBody");

    const title =
      (announcement.title && String(announcement.title).trim()) ||
      "اطلاعیه مهم دیمینت";

    const body =
      (announcement.body && String(announcement.body).trim()) ||
      "";

    if (titleEl) {
      titleEl.textContent = title;
    }
    if (bodyEl) {
      bodyEl.textContent = body;
    }
  }

  // اگر لازم بود، مودال اطلاعیه عمومی را یک‌بار برای هر id نشان بده
  function maybeShowAnnouncementAutoModal() {
    const latest = getLatestAnnouncement();
    if (!latest || !latest.id) return;

    const idStr = String(latest.id);
    const LS_KEY = "diminet_announcement_seen_id";

    let seenId = null;
    try {
      seenId = localStorage.getItem(LS_KEY);
    } catch (e) {
      // اگر localStorage در دسترس نبود، بیخیالش
    }

    // اگر این اطلاعیه قبلاً روی همین مرورگر نمایش داده شده، کاری نکن
    if (seenId === idStr) return;

    // اگر الان یک مودال دیگری باز است → این اطلاعیه را برای بعد از بسته شدن مودال، در صف بگذار
    const anyOpenModal = document.querySelector("[data-modal].is-open");
    if (anyOpenModal) {
      pendingAnnouncementId = idStr;
      pendingAnnouncementData = latest;
      return;
    }

    // در غیر این صورت، همین حالا مودال اطلاعیه را نشان بده
    fillAutoAnnouncementModal(latest);

    try {
      localStorage.setItem(LS_KEY, idStr);
    } catch (e) {
      // اگر نشد ذخیره کنیم، حداقل یک بار نمایش داده می‌شود
    }

    openModal("announcement-auto");
  }


async function loadNotifications() {
  if (!state.isLoggedIn) {
    state.notifications = [];
    state.notificationsUnread = 0;
    updateNotificationBadge();
    return;
  }

  try {
    const res = await fetch("/notifications.php", {
      method: "GET",
      credentials: "same-origin",
    });
    if (!res.ok) return;

    const data = await res.json().catch(() => null);
    if (!data) return;

    const raw =
      (Array.isArray(data.notifications) && data.notifications) ||
      (Array.isArray(data.items) && data.items) ||
      (Array.isArray(data) && data) ||
      [];

    state.notifications = raw.map((row) => ({
      id: row.id,
      title: row.title || "",
      body: row.body || row.message || "",
      isRead:
        row.is_read == 1 ||
        row.read == 1 ||
        row.read_at ||
        row.status === "read",
      // اینجا دیگه created_at_fa از PHP رو استفاده می‌کنیم
      createdAt:
        row.created_at_fa ||
        row.date_fa ||
        row.created_at ||
        row.createdAt ||
        "",
      type: row.type || "info",
    }));

    // محاسبهٔ تعداد نخونده‌ها + آپدیت زنگوله
    recalcNotificationsUnread();
  } catch (err) {
    console.error("notifications fetch error", err);
  }
}

  function recalcNotificationsUnread() {
    const list = Array.isArray(state.notifications)
      ? state.notifications
      : [];
    state.notificationsUnread = list.filter((n) => !n.isRead).length;
    updateNotificationBadge();
  }

function updateNotificationBadge() {
  const badge = document.getElementById("notif-badge");
  const btn   = document.querySelector(".header-icon-btn");
  if (!badge) return;

  const count = state.notificationsUnread || 0;

  if (count > 0) {
    badge.textContent = count > 99 ? "99+" : String(count);
    badge.style.display = "inline-flex";
    btn && btn.classList.add("header-icon-btn--active");
  } else {
    badge.textContent = "";
    badge.style.display = "none";
    btn && btn.classList.remove("header-icon-btn--active");
  }
}

  function setupNotificationsBell() {
    const btn = document.getElementById("btn-notifications");
    if (!btn) return;

    btn.addEventListener("click", () => {
      fillNotificationsModal();
      openModal("notifications");
    });

    updateNotificationBadge();
  }
  


  function fillAnnouncementsModal() {
    const wrap = document.getElementById("announcementsList");
    if (!wrap) return;

    const list = Array.isArray(state.announcements)
      ? state.announcements
      : [];

    if (!list.length) {
      wrap.innerHTML =
        '<p class="text-muted small">فعلاً اطلاعیه فعالی ثبت نشده است.</p>';
      return;
    }

    wrap.innerHTML = `
      <ul class="modal-list__items">
        ${list
          .map(
            (a) => `
          <li class="modal-list__item">
            <div class="modal-list__item-head">
              <span class="modal-list__title">${escapeHtml(a.title)}</span>
              ${
                a.createdAt
                  ? `<span class="modal-list__date small">${escapeHtml(
                      a.createdAt
                    )}</span>`
                  : ""
              }
            </div>
            <div class="modal-list__body small">
              ${escapeHtml(a.body)}
            </div>
          </li>
        `
          )
          .join("")}
      </ul>
    `;
  }

function fillNotificationsModal() {
  const wrap = document.getElementById("notificationsList");
  if (!wrap) return;

  const list = Array.isArray(state.notifications)
    ? state.notifications
    : [];

  if (!list.length) {
    wrap.innerHTML =
      '<p class="text-muted small">فعلاً اعلانی برای حساب شما ثبت نشده.</p>';
    return;
  }

  wrap.innerHTML = `
    <ul class="modal-list__items">
      ${list
        .map((n) => {
          const unreadClass = n.isRead ? "" : " modal-list__item--unread";
          const idSafe = escapeHtml(String(n.id ?? ""));
          const titleSafe = escapeHtml(n.title || "");
          const bodySafe = escapeHtml(n.body || "");
          const dateSafe = n.createdAt
            ? escapeHtml(n.createdAt)
            : "";

          return `
            <li
              class="modal-list__item${unreadClass}"
              data-notif-id="${idSafe}"
            >
              <div class="modal-list__item-head">
                <span class="modal-list__title">
                  ${titleSafe}
                </span>
                ${
                  dateSafe
                    ? `<span class="modal-list__date small">${dateSafe}</span>`
                    : ""
                }
              </div>
              <div class="modal-list__body small">
                ${bodySafe}
              </div>
            </li>
          `;
        })
        .join("")}
    </ul>
  `;

  // کلیک روی هر آیتم → خوانده شدن در UI + ثبت در دیتابیس
  wrap
    .querySelectorAll(".modal-list__item")
    .forEach((itemEl) => {
      const id = itemEl.getAttribute("data-notif-id");
      if (!id) return;

      itemEl.addEventListener("click", () => {
        markNotificationAsRead(id, itemEl);
      });
    });
}

async function markNotificationAsRead(id, itemEl) {
  if (!id) return;

  const list = Array.isArray(state.notifications)
    ? state.notifications
    : [];
  const notif = list.find((n) => String(n.id) === String(id));
  if (!notif) return;

  // اگه قبلاً خونده شده، دیگه درخواست اضافه نزنیم
  if (notif.isRead) return;

  // --- 1) اول UI و state را آپدیت کن (Optimistic UI) ---
  notif.isRead = true;

  if (itemEl) {
    itemEl.classList.remove("modal-list__item--unread");
  }

  recalcNotificationsUnread();

  // --- 2) بعد به سرور بگو این نوتیف خونده شده ---
  try {
    const res = await fetch("/notifications.php", {
      method: "POST",
      credentials: "same-origin",
      headers: {
        "Content-Type": "application/json",
        "X-Requested-With": "XMLHttpRequest",
      },
      body: JSON.stringify({
        ids: [Number(id)], // API جدید: آرایهٔ ids
      }),
    });

    // اگه خواستی می‌تونی این رو هم چک کنی ولی اجباری نیست
    // const data = await res.json().catch(() => null);
    // console.log("mark_read result:", data);
  } catch (err) {
    console.error("markNotificationAsRead error:", err);
    // اینجا می‌تونی در صورت خطا UI رو برگردونی، ولی فعلاً لازم نیست
  }
}

  // ====================================================
  // [9] View router (home / panel / help)
  // ====================================================

  function renderRoute(route) {
    if (route === "panel") {
      renderPanelView();
    } else if (route === "help") {
      renderHelpView();
    } else {
      renderHomeView();
    }
    // convert all digits in the current view to Persian
    applyFaDigits(viewContainer);
    
    maybeAutoOpenAnnouncementModalOnce();
  }

  // ====================================================
  // [10] HOME view (guest + logged)
  // ====================================================

  function getPurchaseTitle() {
    if (state.renewUsername) {
      return (
        'تمدید برای اکانت: <span class="renew-username">' +
        escapeHtml(state.renewUsername) +
        "</span>"
      );
    }
    return "پلنت رو انتخاب کن و به صورت آنلاین خرید کن 👇";
  }

  function renderAnnouncementsStrip() {
    const list = Array.isArray(state.announcements)
      ? state.announcements
      : [];
    if (!list.length) return "";
    const latest = list[0];

    return `
      <section class="home-section home-section--announcement">
        <div class="home-box home-box--announcement">
          <div class="announcement-strip">
            <div class="announcement-strip__main">
              <div class="announcement-strip__label">اطلاعیه</div>
              <div class="announcement-strip__title">
                ${escapeHtml(latest.title || "اطلاعیه جدید")}
              </div>
              <div class="announcement-strip__body small">
                ${escapeHtml(latest.body || "")}
              </div>
            </div>
            <div class="announcement-strip__action">
              <button
                type="button"
                class="btn btn--ghost btn--xs js-open-announcements"
              >
                مشاهده همه
              </button>
            </div>
          </div>
        </div>
      </section>
    `;
  }

  // [10.1] Home route main renderer
  function renderHomeView() {
    if (!state.isLoggedIn) {
      viewContainer.innerHTML = homeGuestTemplate();
      attachHomeGuestEvents();
    } else {
      viewContainer.innerHTML = homeLoggedTemplate();
      attachHomeLoggedEvents();
    }
  }

  // [10.2] Plans section renderer
// [10.2] Plans section renderer (3-step)
function renderPlansSection() {
  if (state.plansLoading && !state.plansLoaded) {
    return `
      <div class="plans-loading">
        در حال بارگذاری پلن‌ها...
      </div>
    `;
  }

  if (state.plansError) {
    return `
      <div class="plans-loading">
        ${escapeHtml(state.plansError)}
      </div>
    `;
  }

  const plans = Array.isArray(state.plans) ? state.plans : [];
  if (!plans.length) {
    return `
      <div class="plans-loading">
        در حال حاضر هیچ پلنی فعال نیست.
      </div>
    `;
  }

  return `
    <div class="plans-multistep" id="plansMultiStep">

      <div class="plans-steps-header">
        <div class="plans-step-indicator is-active" data-step="1">
          <span class="plans-step-indicator__icon">
            <i class="fa-solid fa-server" aria-hidden="true"></i>
          </span>
          <span class="plans-step-indicator__label">
            لوکیشن
          </span>
          <span class="plans-step-indicator__check">
            <i class="fa-solid fa-check" aria-hidden="true"></i>
          </span>
        </div>

        <div class="plans-step-indicator" data-step="2">
          <span class="plans-step-indicator__icon">
            <i class="fa-regular fa-clock" aria-hidden="true"></i>
          </span>
          <span class="plans-step-indicator__label">
            زمان
          </span>
          <span class="plans-step-indicator__check">
            <i class="fa-solid fa-check" aria-hidden="true"></i>
          </span>
        </div>

        <div class="plans-step-indicator" data-step="3">
          <span class="plans-step-indicator__icon">
            <i class="fa-solid fa-layer-group" aria-hidden="true"></i>
          </span>
          <span class="plans-step-indicator__label">
            پلن
          </span>
          <span class="plans-step-indicator__check">
            <i class="fa-solid fa-check" aria-hidden="true"></i>
          </span>
        </div>
      </div>

      <div class="plans-steps-body" id="plansStepsBody">
        <!-- Step 1: نوع سرور -->
        <div class="plans-step plans-step--1 is-active" id="plansStep1">
          <p class="small">
لوکیشن سرویس مورد نظرتو انتخاب کن
          </p>
          <div class="plans-step__options" id="serverTypeOptions"></div>
        </div>

        <!-- Step 2: مدت سرویس -->
<div class="plans-step plans-step--2" id="plansStep2">
  <button
    type="button"
    class="plans-step__back-btn"
    data-go-step="1"
  >
    <i class="fa-solid fa-arrow-right" aria-hidden="true"></i>
    <span>برگشت به انتخاب لوکیشن</span>
  </button>

  <p class="small">
    حالا مدت سرویس را انتخاب کن.
  </p>
  <div class="plans-step__options" id="durationOptions"></div>
</div>

        <!-- Step 3: لیست پلن‌ها -->
<div class="plans-step plans-step--3" id="plansStep3">
  <button
    type="button"
    class="plans-step__back-btn"
    data-go-step="1"
  >
    <i class="fa-solid fa-arrow-right" aria-hidden="true"></i>
    <span>برگشت به انتخاب لوکیشن</span>
  </button>

  <p class="small">
    حجم و پلن مورد نظر را انتخاب کن.
  </p>
  <div class="plans" id="plansList">
    ${plans.map(renderPlanCard).join("")}
  </div>
</div>
      </div>
      
            <div class="plans-step__option-extra plans-step__option-extra--legend">
        <div class="plans-step__option-extra-item">
          <span class="plan-meta-tag plan-meta-tag--tunnel">
            <i class="fa-solid fa-bolt" aria-hidden="true"></i>
            <span>VIP تانل</span>
          </span>
          <span class="plans-step__option-extra-desc">
            پرسرعت و پرقدرت – همیشه متصل حتی در اینترنت ملی
          </span>
        </div>

        <div class="plans-step__option-extra-item">
          <span class="plan-meta-tag plan-meta-tag--direct">
            مستقیم
          </span>
          <span class="plans-step__option-extra-desc">
            سرعت پایین‌تر – مناسب اینترنت‌هایی با دسترسی آزاد
          </span>
        </div>
      </div>


    </div>
  `;
}

  // [10.3] Single plan card renderer
function renderPlanCard(plan) {
  const locMeta = getLocationMeta(
    plan.location_code || plan.locationCode || "multi"
  );

  // panel_key برای تشخیص دوباره نوع سرور (اگر از بک‌اند درست نیامده بود)
  const panelKey =
    plan.panel_key ||
    plan.panelKey ||
    "";

  // نوع سرور نهایی (single | multi)
  const serverType =
    (plan.server_type || plan.serverType || "").toLowerCase() ||
    detectServerTypeFromPanelKey(panelKey); // مثلا "multi" یا "single"
    
  const tunnelMode =
    (plan.tunnel_mode || plan.tunnelMode) ||
    detectTunnelModeFromPanelKey(panelKey);

  const tunnelTagHtml = renderTunnelTag(tunnelMode);


  const serverTypeLabel = getPlanServerTypeLabel(serverType);
  const durationLabel = getPlanDurationLabelForPlan(plan);

  // 👉 فقط تک‌سرور اجازه نمایش لوکیشن دارد
  const isMultiServer = serverType === "multi";
  const showLocationTag = !isMultiServer;


    const title = plan.title || "";
    const code = plan.code || "";

    const imgDesk = plan.imgurl || "/assets/logo.png";
    const imgMob = plan.imgurlmob || imgDesk;

    const nowRial =
      Number(
        plan.amount_rial ??
          plan.price_rial ??
          plan.amount ??
          plan.price ??
          0
      ) || 0;
    const oldRial =
      Number(
        plan.amount_old_rial ??
          plan.old_price_rial ??
          plan.old_price ??
          0
      ) || 0;

    const isFree = nowRial === 0;
    const nowShort = isFree ? "" : priceMainBlock(nowRial);
    const oldShort =
      !isFree && oldRial > nowRial ? priceOldBlock(oldRial) : "";
    const hasDiscount = !!oldShort;
    const pctOff = hasDiscount ? discountPercent(oldRial, nowRial) : "";

    const isRec =
      plan.is_recommended == 1 ||
      plan.is_recommended === "1" ||
      plan.recommended === true ||
      plan.isRecommended === true;

    const staticLine = "بدون محدودیت کاربر";

    const extraLines = (plan.plan_desc || "")
      .split(/\r\n|\r|\n/)
      .map((l) => l.trim())
      .filter((l) => l);

    const featuresInnerHtml = extraLines
      .map(
        (line) => `
        <div class="feat-row">
          <span class="feat-ico">✔</span>
          <span class="feat-text">${escapeHtml(line)}</span>
        </div>
      `
      )
      .join("");

    const featuresBlockHtml = featuresInnerHtml
      ? `
                <div class="plan-features">
                  ${featuresInnerHtml}
                </div>
      `
      : "";

    plan._featuresHtml = featuresBlockHtml;

    const desktopPriceBlock = isFree
      ? `<div class="price-free price-desktop-only">رایگان</div>`
      : `
        ${
          hasDiscount
            ? `<div class="price-old-desktop">${escapeHtml(
                oldShort
              )}</div>`
            : ""
        }
        <div class="price-now-desktop">${escapeHtml(nowShort)}</div>
        <div class="price-unit-desktop">هزار تومن</div>
      `;

    const discBadge =
      hasDiscount && pctOff
        ? `<div class="discount-float">٪${pctOff}</div>`
        : "";

    let planLabelClasses = "plan-card";
    if (hasDiscount) planLabelClasses += " has-discount";
    if (isRec) planLabelClasses += " recommended-plan";
    
    return `
<label
  class="${planLabelClasses}"
  data-plan-code="${escapeHtml(code)}"
  data-server-type="${escapeHtml(serverType || "single")}"
  data-location="${escapeHtml(locMeta.code)}"
  data-duration="${escapeHtml(
    String(plan.duration_months != null ? plan.duration_months : 1)
  )}"
  data-panel-key="${escapeHtml(panelKey)}"
>

        <input type="radio" name="plan_radio" value="${escapeHtml(code)}">
        <div class="card-inner">

          ${
            isRec
              ? `<div class="best-badge">پرفروش‌</div>`
              : ""
          }

          ${discBadge}

          <div class="thumb">
            <picture style="width:100%;height:100%;display:block;">
              <source media="(max-width:639px)" srcset="${escapeHtml(
                imgMob
              )}">
              <source media="(min-width:640px)" srcset="${escapeHtml(
                imgDesk
              )}">
              <img
                src="${escapeHtml(imgDesk)}"
                alt="${escapeHtml(title)}"
                style="width:100%;height:100%;object-fit:cover;display:block;"
              >
            </picture>
          </div>

          <div class="info">
            <div class="info-inner">

              <div class="info-main">
                <div class="plan-title">${escapeHtml(title)} </div>
                <div class="plan-desc-static">${escapeHtml(staticLine)}</div>
<div class="plan-meta-row">
  <span class="plan-meta-tag">${escapeHtml(durationLabel)}</span>
  ${
    showLocationTag
    ? `<span class="plan-meta-tag plan-meta-tag--location">${escapeHtml(
        locMeta.name
      )}</span>`
    : ""
  }
    ${tunnelTagHtml}

</div>

              </div>

              <div class="info-price">
                <!-- mobile -->
                <div class="price-row-mobile">
                  ${
                    isFree
                      ? `<div class="price-free">رایگان</div>`
                      : `
                    ${
                      hasDiscount
                        ? `
                          <div class="price-old-inline">
                            <span class="price-old-num">${escapeHtml(
                              oldShort
                            )}</span>
                          </div>
                        `
                        : ""
                    }
                    <div class="price-now-line">
                      <div class="price-main-inline">
                        <span class="price-main-num">${escapeHtml(
                          nowShort
                        )}</span>
                      </div>
                      <div class="price-unit-inline">هزار تومن</div>
                    </div>
                  `
                  }
                </div>

                <!-- desktop -->
                ${desktopPriceBlock}
              </div>

            </div>
          </div>

        </div>
      </label>
    `;
  }

  // [10.4] Checkout section under plans
  function renderCheckoutSection(isLogged) {
    const emailVal = isLogged ? state.user.email || "" : "";
    const emailAttr = emailVal
      ? `value="${escapeHtml(emailVal)}"`
      : `placeholder="لطفا ایمیل خود را به صورت صحیح وارد کنید"`;

    const renewHint = state.renewUsername
      ? `تمدید برای اکانت <b>${escapeHtml(
          state.renewUsername
        )}</b> — لطفاً پلن مورد نظر برای تمدید را انتخاب کنید.`
      : "";

    return `
      <div class="checkout-box" id="checkoutBox">
        <div class="checkout-head">
          <div class="chk-label">پلن :</div>
          <div class="chk-value" id="chkPlanTitle">—</div>

          <!-- mobile -->
          <div class="chk-label chk-price-mobile">قیمت:</div>
          <div class="chk-value chk-price-mobile" id="chkPlanPriceHTML">—</div>

          <!-- desktop -->
          <div class="chk-label chk-price-desktop">قیمت:</div>
          <div class="chk-value chk-price-desktop" id="chkPlanPriceText">—</div>
        </div>

        ${
          renewHint
            ? `<p class="checkout__hint">${renewHint}</p>`
            : ""
        }

      <div class="checkout-row">
        <div class="checkout-field">
          <p class="checkout__hint">
            اطلاعات ورود به پنل و تمدیدها روی همین ایمیل ارسال می‌شود.
          </p>
          <input
            class="checkout-input"
            type="email"
            name="email"
            id="email"
            ${emailAttr}
            required
          >
        </div>

        <div class="checkout-action">
          <button class="btn-submit" type="submit" id="payBtn">
            ادامه به پرداخت
          </button>
        </div>
      </div>

      <input type="hidden" name="plan" id="selectedPlanHidden" value="">
      <input type="hidden" name="server_type" id="selectedServerType" value="">
      <input type="hidden" name="duration_months" id="selectedDurationMonths" value="">
    </div>
    `;
  }

  // [10.5] Home guest template
  function homeGuestTemplate() {
    return `
      <section class="view view--home">
        ${renderAnnouncementsStrip()}

        <section class="home-section">
          <div class="section-header">
            <h2 class="section-title" style=" color: #007eff !important; font-size: 16px;">${getPurchaseTitle()}</h2>
          </div>

          <form id="orderForm" action="/request.php" method="POST" novalidate>
            ${renderPlansSection()}
            ${renderCheckoutSection(false)}
          </form>
        </section>
        ${renderFaqSection()}

        ${renderGuestTermsBox()}
      </section>
    `;
  }
  
  function renderFaqSection() {
  return `
    <section class="home-section">
      <div class="home-box faq-box">
        <div class="section-header">
          <h2 class="section-title">سوالات متداول قبل از خرید</h2>
        </div>

        <div class="faq-accordion" id="faqAccordion">

          <!-- آیتم ۱ -->
          <div class="faq-item-wrap" data-faq-item>
            <button
              type="button"
              class="faq-item__header"
            >
              <div class="faq-item__q">
                <i class="fa-regular fa-circle-question faq-item__icon" aria-hidden="true"></i>
                <span>تفاوت سرور تانل و مستقیم چیه؟</span>
              </div>
              <i class="fa-solid fa-chevron-down faq-item__chevron" aria-hidden="true"></i>
            </button>
            <div class="faq-item__body">
              <div class="faq-item__body-inner small">
                سرور <b>تانل</b> برای ارتباط از سرورهای داخل ایران استفاده می‌کند و به همین خاطر
                در قطعی‌های اینترنت و محدودیت‌های شدید، ارتباطش پایدارتر و یکنواخت‌تر برقرار می‌ماند.<br>
                سرور <b>مستقیم</b> مناسب کسانی است که اینترنت خوب و پایدار دارند و دسترسی اینترنت‌شان
                به IPهای خارج بازتر است.
              </div>
            </div>
          </div>

          <!-- آیتم ۲ -->
          <div class="faq-item-wrap" data-faq-item>
            <button
              type="button"
              class="faq-item__header"
            >
              <div class="faq-item__q">
                <i class="fa-regular fa-circle-question faq-item__icon" aria-hidden="true"></i>
                <span>اکانت دیمینت روی چه سیستم‌عاملی کار می‌کند؟</span>
              </div>
              <i class="fa-solid fa-chevron-down faq-item__chevron" aria-hidden="true"></i>
            </button>
            <div class="faq-item__body">
              <div class="faq-item__body-inner small">
                اکانت‌های دیمینت بر پایه <b>V2Ray</b> هستند و روی
                <b>اندروید</b>، <b>iOS (آیفون)</b> و <b>ویندوز</b> به‌طور کامل پشتیبانی می‌شوند.<br>
                یعنی می‌توانی روی موبایل و لپ‌تاپ هم‌زمان وصل باشی.
              </div>
            </div>
          </div>

          <!-- آیتم ۳ -->
          <div class="faq-item-wrap" data-faq-item>
            <button
              type="button"
              class="faq-item__header"
            >
              <div class="faq-item__q">
                <i class="fa-regular fa-circle-question faq-item__icon" aria-hidden="true"></i>
                <span>با چه برنامه‌ای و چطور باید وصل شوم؟</span>
              </div>
              <i class="fa-solid fa-chevron-down faq-item__chevron" aria-hidden="true"></i>
            </button>
            <div class="faq-item__body">
              <div class="faq-item__body-inner small">
                داخل پنل دیمینت بخشی به اسم <b>آموزش</b> هست که هم
                <b>ویدیویی</b> و هم <b>تصویری</b>، مرحله‌به‌مرحله توضیح می‌دهد
                با چه برنامه‌ای و چطور وصل شوی؛ همان‌جا می‌توانی برنامهٔ مورد نظرت را هم دانلود کنی.
              </div>
            </div>
          </div>

        </div>
      </div>
    </section>
  `;
}


  function renderGuestTermsBox() {
    return `
      <section class="home-section">
        <div class="home-box">
          <p class="small" style="margin:0; line-height:1.8; font-size: 11px; 
    text-align: center;">
            خرید از دیمینت به معنی قبول قوانین دیمینت می‌باشد.
            <button
              type="button"
              id="btn-open-terms"
              style="
                border:none;
                background:none;
                padding:0 4px;
                margin:0;
                color:#2563eb;
                font-weight:800;
                font-size: 11px;
                cursor:pointer;
                text-decoration:underline;
              "
            >
              قوانین
            </button>
          </p>
        </div>
      </section>
    `;
  }

  function attachHomeGuestEvents() {
    attachCheckoutBehavior();

    const termsBtn = document.getElementById("btn-open-terms");
    if (termsBtn) {
      termsBtn.addEventListener("click", () => {
        openModal("terms");
      });
    }

    const viewEl = viewContainer;
    if (viewEl) {
      viewEl
        .querySelectorAll(".js-open-announcements")
        .forEach((btn) => {
          btn.addEventListener("click", () => {
            fillAnnouncementsModal();
            openModal("announcements");
          });
        });
    }
    setupFaqAccordion();
  }
  
  
  function setupFaqAccordion() {
  const root = document.getElementById("faqAccordion");
  if (!root) return;

  const items = Array.from(root.querySelectorAll(".faq-item-wrap"));
  let openItem = null;

  function open(wrap) {
    if (!wrap) return;
    const body = wrap.querySelector(".faq-item__body");
    if (!body) return;

    wrap.classList.add("is-open");
    // برای انیمیشن، max-height رو بر اساس محتوا تنظیم می‌کنیم
    body.style.maxHeight = body.scrollHeight + "px";
  }

  function close(wrap) {
    if (!wrap) return;
    const body = wrap.querySelector(".faq-item__body");
    if (!body) return;

    wrap.classList.remove("is-open");
    body.style.maxHeight = "0px";
  }

  items.forEach((wrap) => {
    const header = wrap.querySelector(".faq-item__header");
    const body = wrap.querySelector(".faq-item__body");
    if (!header || !body) return;

    // حالت اولیه: همه بسته
    body.style.maxHeight = "0px";

    header.addEventListener("click", () => {
      // اگر همون آیتم بازه، ببندش
      if (openItem === wrap) {
        close(wrap);
        openItem = null;
        return;
      }

      // قبلی رو ببند
      if (openItem) {
        close(openItem);
      }

      // جدید رو باز کن
      open(wrap);
      openItem = wrap;
    });
  });
}


  // [10.6] Home logged template
  function homeLoggedTemplate() {
    const services = Array.isArray(state.services) ? state.services : [];
    const servicesHtml =
      services.length === 0
        ? `<p class="text-muted">هنوز هیچ سرویسی نخریدی.</p>`
        : services.map(renderHomeServiceCard).join("");

    return `
      <section class="view view--home-logged">

        ${renderAnnouncementsStrip()}

        <!-- section: new purchase / renew -->
        <section class="home-section">
          <div class="section-header">
            <h2 class="section-title">${getPurchaseTitle()}</h2>
          </div>

          <div class="home-box">
            <form id="orderForm" action="/request.php" method="POST" novalidate style="margin-top:12px;">
              ${renderPlansSection()}
              ${renderCheckoutSection(true)}
            </form>
          </div>
        </section>

        <!-- section: services summary -->
        <section class="home-section">
          <div class="section-header">
            <h2 class="section-title">سرویس‌های شما</h2>
          </div>

          <div class="home-box">
            <div class="service-list service-list--compact">
              ${servicesHtml}
            </div>
          </div>
        </section>

      </section>
    `;
  }

  // [10.7] Home service card
  function renderHomeServiceCard(svc) {
    const statusMap = {
      active: "فعال",
      expired: "منقضی",
      disabled: "غیرفعال",
      on_hold: "معلق",
    };
    const statusLabel = statusMap[svc.status] || svc.status || "نامشخص";

    const percent = clampNumber(svc.percent ?? 0, 0, 100);
    const used = svc.usedGB ?? 0;
    const total = svc.totalGB ?? 0;
    const remain = svc.remainingGB ?? 0;

    let expireText = "—";
    if (typeof svc.expireDays === "number") {
      if (svc.expireDays <= 0) expireText = "منقضی شده";
      else if (svc.expireDays === 1) expireText = "۱ روز مانده";
      else expireText = `${svc.expireDays} روز مانده`;
    }

    const badgeClass =
      svc.status === "active"
        ? "badge badge--ok"
        : svc.status === "expired" || svc.status === "disabled"
        ? "badge badge--danger"
        : "badge badge--warn";
        
let metaHtml = "";
if (svc.panelKey) {
  const serverType = svc.serverType || detectServerTypeFromPanelKey(svc.panelKey);
  const serverTypeLabel = getPlanServerTypeLabel(serverType);

  const locCode = svc.locationCode || detectLocationFromPanelKey(svc.panelKey);
  const locMeta = getLocationMeta(locCode);

  const isMultiLocation = serverType === "multi" || locMeta.code === "multi";

  let flagHtml = "";

  if (isMultiLocation) {
    flagHtml = `
      <span class="service-card__flag-group">
        <img src="/assets/tr.png" alt="ترکیه" loading="lazy">
        <img src="/assets/de.png" alt="آلمان" loading="lazy">
        <img src="/assets/us.png" alt="آمریکا" loading="lazy">
        <img src="/assets/fi.png" alt="فنلاند" loading="lazy">
      </span>
    `;
  } else if (locMeta.flagUrl) {
    flagHtml = `
      <img class="service-card__flag"
           src="${escapeHtml(locMeta.flagUrl)}"
           alt="${escapeHtml(locMeta.name)}"
           loading="lazy">
    `;
  }

  const tunnelMode = svc.tunnelMode || detectTunnelModeFromPanelKey(svc.panelKey);
  const tunnelTagHtml = renderTunnelTag(tunnelMode);

  metaHtml = `
    <div class="service-card__meta-row plan-meta-row">
      <span class="plan-meta-tag plan-meta-tag--location">
        ${escapeHtml(locMeta.name)}
      </span>
      ${flagHtml}
      ${tunnelTagHtml}
    </div>
  `;
}


    return `
      <article
        class="service-card"
        data-service-username="${escapeHtml(svc.username)}"
      >
        <div class="service-card__row service-card__row--top">
          <div class="service-card__title-wrap">
            <div class="service-card__title" data-keep-en-digits>${escapeHtml(
              svc.username
            )}</div>
          </div>
          <div class="service-card__top-meta">
            <span class="service-card__expire">${expireText}</span>
            <span class="${badgeClass}">${statusLabel}</span>
            <button class="btn btn--ghost btn--xs service-renew-btn">
              تمدید
            </button>
          </div>
        </div>
        ${metaHtml}

        <div class="service-card__row service-card__row--usage">
          <div class="service-card__usage-text" data-keep-en-digits>
            <span>${used} / ${total || "∞"} GB</span>
            <span>${total ? percent + "%" : "نامحدود"}</span>
          </div>
          <div class="progress progress--inline">
            <div class="progress__bar" style="width:${
              total ? percent : 0
            }%;"></div>
          </div>
          <div class="service-card__usage-remaining">
            باقیمانده: <b>${remain}</b> GB
          </div>
        </div>
      </article>
    `;
  }

  function attachHomeLoggedEvents() {
    // دکمه تمدید روی لیست سرویس‌ها (در تب هوم)
    document.querySelectorAll(".service-renew-btn").forEach((btn) => {
      btn.addEventListener("click", () => {
        const card = btn.closest(".service-card");
        const username = card?.getAttribute("data-service-username");
        if (!username) return;

        const svc = findServiceByUsername(username);
        const panelKey = svc && svc.panelKey ? svc.panelKey : "";

        const url = new URL("/index.php", window.location.origin);
        url.searchParams.set("tab", "home");
        url.searchParams.set("renew", username);
        if (panelKey) {
          url.searchParams.set("panel_key", panelKey);
        }

        window.location.href = url.toString();
      });
    });

    attachCheckoutBehavior();

    const viewEl = viewContainer;
    if (viewEl) {
      viewEl
        .querySelectorAll(".js-open-announcements")
        .forEach((btn) => {
          btn.addEventListener("click", () => {
            fillAnnouncementsModal();
            openModal("announcements");
          });
        });
    }
  }

  // [10.8] Checkout behavior (plan selection + validation)
  function attachCheckoutBehavior() {
    const form = document.getElementById("orderForm");
    if (!form) return;

    attachPlanClickHandlers(form);
    setupPlansMultiStep();

    form.addEventListener("submit", (e) => {
      const emailInput = form.querySelector('input[name="email"]');
      const hiddenPlan = form.querySelector("#selectedPlanHidden");

      const emailVal = (emailInput?.value || "").trim();
      const emailOk = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailVal);

      if (!hiddenPlan || !hiddenPlan.value) {
        e.preventDefault();
        alert("لطفاً یکی از پلن‌ها را انتخاب کنید.");
        return;
      }
      if (!emailOk) {
        e.preventDefault();
        alert("لطفاً ایمیل معتبر وارد کنید.");
        if (emailInput) emailInput.focus();
        return;
      }
          const payBtn = document.getElementById("payBtn");
    if (payBtn && !payBtn.disabled) {
      payBtn.disabled = true;
      payBtn.classList.add("btn--loading");
      // اگر خواستی بعداً برگردونی می‌تونی از این مقدار استفاده کنی
      payBtn.dataset.originalText = payBtn.textContent || "";
      payBtn.textContent = "در حال انتقال به درگاه...";
    }

    });
  }

  function attachPlanClickHandlers(scopeEl) {
    const root = scopeEl || document;
    const cards = root.querySelectorAll(".plan-card");
    if (!cards.length) return;

    cards.forEach((card) => {
      card.addEventListener("click", () => {
        const code = card.getAttribute("data-plan-code");
        if (!code) return;
        handlePlanSelection(code, card);
      });
    });
  }
  
  
function setupPlansMultiStep() {
  const root = document.getElementById("plansMultiStep");
  if (!root) return;

  const step1 = document.getElementById("plansStep1");
  const step2 = document.getElementById("plansStep2");
  const step3 = document.getElementById("plansStep3");
  const stepsBody = document.getElementById("plansStepsBody");
  const serverWrap = document.getElementById("serverTypeOptions");
  const durationWrap = document.getElementById("durationOptions");
  const planCards = root.querySelectorAll(".plan-card");
    
    function resetPlanAndCheckout() {
  // ۱) همه کارت‌های پلن از حالت انتخاب‌شده دربیان
  planCards.forEach((card) => {
    card.classList.remove("is-selected");
    const radio = card.querySelector('input[type="radio"]');
    if (radio) radio.checked = false;
  });

  // ۲) hidden input ها خالی بشن
  const hiddenPlan     = document.getElementById("selectedPlanHidden");
  const hiddenServer   = document.getElementById("selectedServerType");
  const hiddenDuration = document.getElementById("selectedDurationMonths");

  if (hiddenPlan)     hiddenPlan.value = "";
  if (hiddenServer)   hiddenServer.value = "";
  if (hiddenDuration) hiddenDuration.value = "";

  // ۳) متن‌های بالای checkout خنثی بشن
  const titleEl     = document.getElementById("chkPlanTitle");
  const priceHtmlEl = document.getElementById("chkPlanPriceHTML");
  const priceTextEl = document.getElementById("chkPlanPriceText");

  if (titleEl)     titleEl.textContent     = "—";
  if (priceHtmlEl) priceHtmlEl.textContent = "—";
  if (priceTextEl) priceTextEl.textContent = "—";

  // ۴) خود باکس چک‌اوت بسته و مخفی بشه
  const box = document.getElementById("checkoutBox");
  if (box) {
    box.classList.remove("is-visible");
    box.style.display = "none";
  }

  // ۵) دکمه پرداخت هم به حالت عادی برگرده (اگه قبلاً لودینگ شده بود)
  const payBtn = document.getElementById("payBtn");
  if (payBtn) {
    payBtn.disabled = false;
    payBtn.classList.remove("btn--loading");
    const original = payBtn.dataset.originalText || "ادامه به پرداخت";
    payBtn.textContent = original;
  }
}



const backButtons = root.querySelectorAll(".plans-step__back-btn");
backButtons.forEach((btn) => {
  btn.addEventListener("click", () => {
    // هرچی پلن انتخاب شده و checkout هست رو صفر کن
    resetPlanAndCheckout();
    // و برگرد به استپ ۱
    gotoStep(1);
  });
});


  const plans = Array.isArray(state.plans) ? state.plans : [];

  const steps = [step1, step2, step3].filter(Boolean);

  let currentStepNum = 1;
  let isAnimatingStep = false;

  // ❗️ از این به بعد فیلتر اصلی ما panel_key است
  let currentPanelKeyFilter = null;
  currentServerTypeFilter = null;   // برای سازگاری اگر جایی استفاده شده
  currentLocationFilter   = null;   // "
  currentDurationFilter   = null;

  // 🔹 نقشهٔ مدت‌ها بر اساس panel_key
  const durationsMap = Object.create(null);

  function getFlagsForPanelKey(panelKey) {
    const v = normalizeTextForDetect(panelKey);
    if (!v) return [];

    const flags = [];
    if (v.includes("tr")) flags.push(getLocationMeta("tr"));
    if (v.includes("de")) flags.push(getLocationMeta("de"));
    if (v.includes("us")) flags.push(getLocationMeta("us"));
    if (v.includes("fi")) flags.push(getLocationMeta("fi"));

    // اگر چیزی پیدا نشد، یعنی یا ناشناس است یا multi کلی →
    // یک metaٔ مولتی برمی‌گردونیم (پرچم خالی، فقط برای جلوگیری از باگ)
    if (!flags.length) {
      const m = getLocationMeta("multi");
      if (m.flagUrl) flags.push(m);
    }

    return flags;
  }

  // 🔹 باکت سرورها بر اساس panel_key
  const serverBucketsMap = Object.create(null);

  plans.forEach((p) => {
    const panelKey =
      (p.panel_key || p.panelKey || "").trim();
    if (!panelKey) return;

    const stRaw =
      (p.server_type || p.serverType || detectServerTypeFromPanelKey(panelKey)) || "single";
    const serverType = stRaw.toLowerCase() === "multi" ? "multi" : "single";

    const locRaw =
      p.location_code ||
      p.locationCode ||
      detectLocationFromPanelKey(panelKey);
    const mainLocCode =
      (locRaw || "").toLowerCase() || (serverType === "multi" ? "multi" : "tr");

    const dur =
      Number(p.duration_months || p.durationMonths || 1) || 1;
      const tunnelMode = detectTunnelModeFromPanelKey(panelKey);


    // برای استپ ۲: لیست مدت‌ها برای هر panel_key
    if (!durationsMap[panelKey]) {
      durationsMap[panelKey] = new Set();
    }
    durationsMap[panelKey].add(dur);

    // برای استپ ۱: اطلاعات کلی هر panel_key
    if (!serverBucketsMap[panelKey]) {
      serverBucketsMap[panelKey] = {
        panelKey,
        serverType,
        mainLocCode,
        flagsMeta: getFlagsForPanelKey(panelKey),
        tunnelMode,
        featuresHtml: p._featuresHtml || "",
      };
    }else if (!serverBucketsMap[panelKey].featuresHtml && p._featuresHtml) {
      serverBucketsMap[panelKey].featuresHtml = p._featuresHtml;
    }
  });

  const serverBuckets = Object.values(serverBucketsMap);

  // 🔹 لودینگ وسط استپ‌ها
  let stepsLoadingEl = null;
  if (stepsBody) {
    stepsLoadingEl = document.createElement("div");
    stepsLoadingEl.className = "plans-steps-loading";
    stepsLoadingEl.innerHTML = `
      <div class="plans-steps-loading__spinner"></div>
    `;
    stepsBody.appendChild(stepsLoadingEl);
    stepsLoadingEl.style.display = "none";
  }

  // 🔹 ارتفاع مینیمم بدنه استپ‌ها (برای نپریدن صفحه)
  if (stepsBody) {
    let maxH = 0;
    steps.forEach((s) => {
      if (!s) return;
      const prevDisplay = s.style.display;
      s.style.display = "block";
      const h = s.offsetHeight;
      if (h > maxH) maxH = h;
      s.style.display = prevDisplay;
    });
    if (maxH > 0) {
      stepsBody.style.minHeight = maxH + "px";
    }
  }

  // =============================
  // 🔹 استپ ۱: برای هر panel_key یک دکمه
  // =============================

  if (serverWrap) {
    serverWrap.innerHTML = "";

    const locationOrder = ["tr", "de", "us", "fi"];

    const singleBuckets = [];
    const multiBuckets  = [];

    serverBuckets.forEach((b) => {
      if (b.serverType === "multi") multiBuckets.push(b);
      else singleBuckets.push(b);
    });

    // تک‌سرورها را بر اساس لوکیشن مرتب می‌کنیم (ترکیه، آلمان، ... )
    singleBuckets.sort((a, b) => {
      return locationOrder.indexOf(a.mainLocCode) - locationOrder.indexOf(b.mainLocCode);
    });

    const orderedBuckets = singleBuckets.concat(multiBuckets);

    orderedBuckets.forEach((bucket) => {
      const btn = document.createElement("button");
      btn.type = "button";
      btn.className = "plans-step__option-btn plans-step__option-btn--server";
      btn.dataset.serverType = bucket.serverType;
      btn.dataset.location   = bucket.mainLocCode;
      btn.dataset.panelKey   = bucket.panelKey;

const tunnelHtml = renderTunnelBadgeSmall(bucket.tunnelMode);
const featuresHtml = bucket.featuresHtml || "";

    if (bucket.serverType === "single") {
      const meta = getLocationMeta(bucket.mainLocCode);

      btn.innerHTML = `
          <div class="plans-step__option-flag">
            <img src="${escapeHtml(meta.flagUrl)}" alt="${escapeHtml(meta.name)}" loading="lazy">
          </div>
          <div class="plans-step__option-main">
            <div class="plans-step__option-title">
              تک سرور ${escapeHtml(meta.name)}  ${tunnelHtml}
            </div>
            <div class="plans-step__option-desc">
              از همان ابتدا فقط به سرور ${escapeHtml(meta.name)} متصل می‌شوی.
            </div>
            ${featuresHtml}
          </div>
        `;
      } else {
        // مولتی سرور → ...
        const flags = bucket.flagsMeta && bucket.flagsMeta.length
          ? bucket.flagsMeta
          : [getLocationMeta("multi")];

        const flagsHtml = flags
          .map(
            (f) =>
              f.flagUrl
                ? `<img src="${escapeHtml(f.flagUrl)}" alt="${escapeHtml(f.name)}" loading="lazy">`
                : ""
          )
          .join("");

        btn.innerHTML = `
          <div class="plans-step__option-flag-multi">
            ${flagsHtml}
          </div>
          <div class="plans-step__option-main">
            <div class="plans-step__option-title">
              مولتی سرور
              ${tunnelHtml}
            </div>
            <div class="plans-step__option-desc">
              امکان جابه‌جایی بین سرورهای این پنل.
            </div>
            ${featuresHtml}
          </div>
        `;
      }



      btn.addEventListener("click", () =>
        handleServerChoice(bucket.panelKey, bucket.serverType, bucket.mainLocCode)
      );

      serverWrap.appendChild(btn);
    });
  }

  // =============================
  // 🔹 هندل انتخاب استپ ۱
  // =============================

  function handleServerChoice(panelKey, st, loc) {
    currentPanelKeyFilter = panelKey;
    currentServerTypeFilter = st;
    currentLocationFilter = loc || "multi";
    currentDurationFilter = null;

    if (serverWrap) {
      serverWrap
        .querySelectorAll(".plans-step__option-btn")
        .forEach((b) => {
          const bKey = b.getAttribute("data-panel-key") || "";
          const active = bKey === panelKey;
          b.classList.toggle("is-active", active);
        });
    }

    // استپ ۲: ساخت دکمه‌های مدت برای همین panel_key
    if (durationWrap) {
      durationWrap.innerHTML = "";

      const dursSet = durationsMap[panelKey] || new Set();
      const durs = Array.from(dursSet).sort((a, b) => a - b);

      durs.forEach((m) => {
        const meta = getDurationMeta(m);

        const dbtn = document.createElement("button");
        dbtn.type = "button";
        dbtn.className = "plans-step__option-btn";
        dbtn.dataset.duration = String(meta.months);
        dbtn.innerHTML = `
          <div class="plans-step__option-icon">
            <i class="${meta.iconClass}" aria-hidden="true"></i>
          </div>
          <div class="plans-step__option-main">
            <div class="plans-step__option-title">${meta.label}</div>
            <div class="plans-step__option-desc">${meta.desc}</div>
          </div>
        `;
        dbtn.addEventListener("click", () =>
          handleDurationChosen(meta.months)
        );
        durationWrap.appendChild(dbtn);
      });
    }

    // هنوز پلنی انتخاب نکن؛ فقط برو استپ ۲
    planCards.forEach((card) => {
      card.classList.remove("is-visible", "is-selected");
      const radio = card.querySelector('input[type="radio"]');
      if (radio) radio.checked = false;
    });

    gotoStep(2);
  }

function handleDurationChosen(m) {
  currentDurationFilter = m;

  if (durationWrap) {
    durationWrap
      .querySelectorAll(".plans-step__option-btn")
      .forEach((b) => {
        b.classList.toggle(
          "is-active",
          Number(b.dataset.duration || 0) === Number(m)
        );
      });
  }

  filterPlans();
  // فقط برو استپ ۳، دیگه opts نداریم
  gotoStep(3);
}

  function filterPlans() {
    if (!step3) return;

    step3.classList.add("has-filter");

    planCards.forEach((card) => {
      const pk  = (card.getAttribute("data-panel-key") || "").trim();
      const dur = parseInt(card.getAttribute("data-duration"), 10) || 1;

      const match =
        (!currentPanelKeyFilter || pk === currentPanelKeyFilter) &&
        (!currentDurationFilter ||
          dur === Number(currentDurationFilter));

      card.classList.toggle("is-visible", !!match);

      if (!match) {
        card.classList.remove("is-selected");
        const radio = card.querySelector('input[type="radio"]');
        if (radio) radio.checked = false;
      }
    });
  }

  function updateIndicators(stepNum) {
    const indicators = root.querySelectorAll(".plans-step-indicator");
    indicators.forEach((ind) => {
      const n = parseInt(ind.getAttribute("data-step"), 10) || 1;
      ind.classList.toggle("is-active", n === stepNum);
      ind.classList.toggle("is-done", n < stepNum);
    });
  }

function gotoStep(nextStepNum) {
  if (!stepsBody || !steps.length) return;
  if (nextStepNum === currentStepNum || isAnimatingStep) return;

  const nextEl = steps[nextStepNum - 1];
  if (!nextEl) return;

  isAnimatingStep = true;

  // همه استپ‌ها رو پنهان کن
  steps.forEach((s) => {
    if (!s) return;
    s.style.display = "none";
    s.classList.remove("is-active");
  });

  // لودینگ وسط استپ‌ها (اگه گذاشتیم)
  if (stepsLoadingEl) {
    stepsLoadingEl.style.display = "flex";
  }

  const duration = 260;

  setTimeout(() => {
    if (stepsLoadingEl) {
      stepsLoadingEl.style.display = "none";
    }

    // فقط استپ مقصد رو نشون بده
    steps.forEach((s, idx) => {
      if (!s) return;
      const n = idx + 1;
      s.style.display = n === nextStepNum ? "block" : "none";
      s.classList.toggle("is-active", n === nextStepNum);
    });

    updateIndicators(nextStepNum);
    currentStepNum = nextStepNum;
    isAnimatingStep = false;
  }, duration);
}

  // شروع اولیه: همه کارت‌ها مخفی، فقط استپ ۱ باز
  // شروع اولیه: همه کارت‌ها مخفی، فقط استپ ۱ باز
  planCards.forEach((card) => {
    card.classList.remove("is-visible", "is-selected");
    const radio = card.querySelector('input[type="radio"]');
    if (radio) radio.checked = false;
  });

  steps.forEach((s, idx) => {
    if (!s) return;
    const n = idx + 1;
    s.style.display = n === 1 ? "block" : "none";
    s.classList.toggle("is-active", n === 1);
  });
  updateIndicators(1);
  currentStepNum = 1;

  // ================================
  // 🔹 Initial preselect from URL
  //    (panel_key / duration / plan جدا از هم)
  // ================================
  if (!initialPlanPreselectUsed) {
    const urlPanelKey = (initialPlanFromUrl.panelKey || "").trim();
    const urlDuration = initialPlanFromUrl.durationMonths;
    const urlPlanCode = (initialPlanFromUrl.planCode || "").trim();

    // اگر duration یا plan توی URL داده شده یعنی کاربر صراحتاً استپ ۳ می‌خواد
    // (فقط panel_key → استپ ۲)
    const hasExplicitDuration =
      (urlDuration != null && urlDuration > 0) || !!urlPlanCode;

    let targetPanelKey = urlPanelKey;
    let targetDuration = urlDuration;
    let targetPlan = null;

    // اگر plan از URL اومده، خود پلن رو پیدا کن
    if (urlPlanCode) {
      targetPlan =
        state.plans.find(
          (p) => String(p.code) === String(urlPlanCode)
        ) || null;

      if (targetPlan) {
        const pkRaw =
          targetPlan.panel_key || targetPlan.panelKey || "";
        targetPanelKey = pkRaw.trim();

        let m =
          Number(
            targetPlan.duration_months ??
              targetPlan.durationMonths
          ) || 0;

        if (!m) {
          m = detectPlanDurationMonths(targetPlan);
        }
        if (m > 0) {
          targetDuration = m;
        }
      }
    }

    // اگر فقط panel_key تو URL بود
    if (!targetPanelKey && urlPanelKey) {
      targetPanelKey = urlPanelKey;
    }

    // اگر همچین panel_key ای نداریم، کلاً ولش کن
    if (!targetPanelKey || !serverBucketsMap[targetPanelKey]) {
      return;
    }

    initialPlanPreselectUsed = true;

    const bucket = serverBucketsMap[targetPanelKey];

    // استپ ۱: نوع سرور / پنل رو انتخاب کن
    handleServerChoice(
      bucket.panelKey,
      bucket.serverType,
      bucket.mainLocCode
    );

    // مدت‌های ممکن برای این panel_key
    const dSet = durationsMap[targetPanelKey];
    const allDurations = dSet
      ? Array.from(dSet).sort((a, b) => a - b)
      : [];

    // ✳️ حالت ۱: فقط panel_key تو URL → باید تو استپ ۲ وایسه
    if (!targetDuration) {
      if (!hasExplicitDuration) {
        // یعنی لینک این شکلیه:
        // ?tab=home&panel_key=xui_de  → فقط استپ ۱ done، استپ ۲ active
        return;
      }

      // اگر duration صریحاً خواسته شده، ولی مقدارش نامعتبر بوده
      // (یا فقط plan بوده و از خودش نتونستیم دربیاریم) → یه مدت پیش‌فرض انتخاب کن
      if (!allDurations.length) {
        return;
      }
      targetDuration = allDurations[0];
    }

    // اگر مدت داده شده در لیست این panel_key نبود، نزدیک‌ترین رو بگیر
    if (allDurations.length && !allDurations.includes(targetDuration)) {
      targetDuration = allDurations[0];
    }

    if (!targetDuration) {
      return;
    }

    setTimeout(() => {
      if (currentPanelKeyFilter !== bucket.panelKey) return;
      handleDurationChosen(targetDuration);
      let chosenCard = null;

      if (urlPlanCode) {
        const cards = root.querySelectorAll(".plan-card.is-visible");
        cards.forEach((c) => {
          if (
            String(c.getAttribute("data-plan-code") || "") ===
            String(urlPlanCode)
          ) {
            chosenCard = c;
          }
        });
      }

      // اگر plan تو URL نبود ولی فقط یه کارت بعد فیلتر مونده، همونو بگیر
      if (!chosenCard) {
        const visibleCards = root.querySelectorAll(
          ".plan-card.is-visible"
        );
        if (visibleCards.length === 1) {
          chosenCard = visibleCards[0];
        }
      }

      // اگر کارتی برای انتخاب داریم، انتخابش کن و چک‌اوت رو پر کن
      if (chosenCard) {
        const code = chosenCard.getAttribute("data-plan-code");
        if (code) {
          handlePlanSelection(code, chosenCard);
        }
      }

      // استپ ۳ رو تیک‌دار کن (done)
      try {
        const msRoot = document.getElementById("plansMultiStep");
        if (msRoot) {
          const step3 = msRoot.querySelector(
            '.plans-step-indicator[data-step="3"]'
          );
          if (step3) {
            step3.classList.add("is-done");
          }
        }
      } catch (e) {
        console.warn(
          "could not mark step 3 as done after URL preselect",
          e
        );
      }
    }, 300);
  }
}


  // [10.9] Plan selection logic (fills checkout)
  function handlePlanSelection(planCode, cardEl) {
    const plan =
      state.plans.find((p) => String(p.code) === String(planCode)) || null;
    if (!plan) return;

    document
      .querySelectorAll(".plan-card.is-selected")
      .forEach((el) => el.classList.remove("is-selected"));
    if (cardEl) {
      cardEl.classList.add("is-selected");
      const radio = cardEl.querySelector('input[type="radio"]');
      if (radio) {
        radio.checked = true;
      }
    }

const nowRial =
  Number(
    plan.amount_rial ??
      plan.price_rial ??
      plan.amount ??
      plan.price ??
      0
  ) || 0;

const isFree = nowRial === 0;
const pricePlain = isFree ? "رایگان" : formatTomanFullFa(nowRial);

const titleEl = document.getElementById("chkPlanTitle");
const priceHtmlEl = document.getElementById("chkPlanPriceHTML");
const priceTextEl = document.getElementById("chkPlanPriceText");
const hiddenPlan = document.getElementById("selectedPlanHidden");
const payBtn = document.getElementById("payBtn");
const box = document.getElementById("checkoutBox");

if (titleEl) titleEl.textContent = plan.title || "";
if (priceHtmlEl) priceHtmlEl.textContent = pricePlain || "—";
if (priceTextEl) priceTextEl.textContent = pricePlain || "—";
if (hiddenPlan) hiddenPlan.value = plan.code || "";

// نوع سرور + مدت برای چک‌اوت و hidden fields
const serverTypeLabel = getPlanServerTypeLabel(
  plan.server_type || plan.serverType
);
const durationLabel = getPlanDurationLabelForPlan(plan);
const serverEl       = document.getElementById("chkPlanServerType");
const durationEl     = document.getElementById("chkPlanDuration");
const hiddenServer   = document.getElementById("selectedServerType");
const hiddenDuration = document.getElementById("selectedDurationMonths");

if (serverEl)   serverEl.textContent   = serverTypeLabel;
if (durationEl) durationEl.textContent = durationLabel;
if (hiddenServer)
  hiddenServer.value = plan.server_type || "";
if (hiddenDuration)
  hiddenDuration.value =
    plan.duration_months != null ? String(plan.duration_months) : "";

if (payBtn) {
  payBtn.textContent = isFree ? "دریافت رایگان" : "ادامه به پرداخت";
}

if (box) {
  if (!box.classList.contains("is-visible")) {
    box.style.display = "block";
    void box.offsetWidth;
    box.classList.add("is-visible");
  }

  try {
    box.scrollIntoView({ behavior: "smooth", block: "center" });
  } catch (e) {
    box.scrollIntoView();
  }
}

  try {
    const msRoot = document.getElementById("plansMultiStep");
    if (msRoot) {
      const step3 = msRoot.querySelector(
        '.plans-step-indicator[data-step="3"]'
      );
      if (step3) {
        // استپ ۳ هم تکمیل شده ✅
        step3.classList.add("is-done");
        // اگه می‌خوای هم‌زمان اکتیو هم بمونه، این خط رو دست نزن
        // اگه دوست داری مثل بقیه فقط تیک‌دار بشه، می‌تونی این رو هم بزنی:
        // step3.classList.remove("is-active");
      }
    }
  } catch (e) {
    console.warn("could not mark step 3 as done", e);
  }

  }

  // ====================================================
  // [11] PANEL view (guest + logged)
  // ====================================================

  function renderPanelView() {
    if (!state.isLoggedIn) {
      viewContainer.innerHTML = panelGuestTemplate();
      attachPanelGuestEvents();
    } else {
      viewContainer.innerHTML = panelLoggedTemplate();
      attachPanelLoggedEvents();
    }
  }

  // [11.1] Panel guest
  function panelGuestTemplate() {
    return `
<section class="view view--panel-guest">
  <div class="card">
        <img
      src="/assets/noser.png"
      alt="ورود به پنل دیمینت"
      class="panel-guest-hero"
    >

    <p class="text-muted" style="margin-bottom:12px;">
      برای دیدن سرویس‌های خریداری‌شده، با ایمیلی که با آن خرید کردی وارد شو.
    </p>
    <button class="btn btn--primary" id="btn-panel-login">
      ورود به حساب
    </button>
  </div>
</section>
    `;
  }

  function attachPanelGuestEvents() {
    const btn = document.getElementById("btn-panel-login");
    if (btn) {
      btn.addEventListener("click", () => openModal("login"));
    }
  }


  // [11.2] Panel logged template
  function panelLoggedTemplate() {
    const s = state;
    const services = Array.isArray(s.services) ? s.services : [];
    const servicesHtml =
      services.length === 0
        ? `<p class="text-muted">هیچ سرویسی ثبت نشده.</p>`
        : services.map(renderPanelServiceItem).join("");

    const walletToman = s.user.walletToman || 0;
    const walletText = formatTomanFullFaFromToman(walletToman);

    return `
      <section class="view view--panel-logged">

        <section class="panel-section panel-section--top">
          <div class="panel-top-actions">
            <div class="quick-actions-menu">
              <div class="quick-actions-menu__items">
                <button class="btn btn--outline btn--xs" id="btn-panel-payments">پرداخت‌ها</button>
                <button class="btn btn--outline btn--xs" id="btn-panel-telegram-bot">اتصال به ربات تلگرام</button>
                <button class="btn btn--outline btn--xs" id="btn-panel-change-pass">تغییر رمز</button>
                <button class="btn btn--outline btn--xs" id="btn-panel-telegram-channel">کانال تلگرام</button>
                <button class="btn btn--outline btn--xs" id="btn-enable-push">فعال‌سازی اعلان‌ها</button>

              </div>
            </div>
          </div>

          <div class="card panel-summary">
            <div class="panel-summary__row">
              <div>
                <div class="panel-summary__label">ایمیل</div>
                <div class="panel-summary__value panel-summary__value--ltr">
                  ${escapeHtml(s.user.email || "")}
                </div>
              </div>
              <div>
                <div class="panel-summary__label">نام کاربری سایت</div>
                <div class="panel-summary__value">
                  ${escapeHtml(s.user.siteUsername || "—")}
                </div>
              </div>
            </div>

            <hr class="panel-summary__divider">

            <div class="panel-summary__row panel-summary__row--wallet">
              <div>
                <div class="panel-summary__label">موجودی کیف پول</div>
                <div class="panel-summary__value panel-summary__value--wallet">
                  ${walletText}
                </div>
              </div>
              <div class="panel-summary__wallet-actions">
                <button class="btn btn--primary btn--sm" id="btn-wallet-charge">
                  افزایش موجودی
                </button>
              </div>
            </div>

            <hr class="panel-summary__divider">

            <div class="panel-summary__row panel-summary__row--chips">
              <span class="chip chip--ok">
                فعال: ${s.user.activeCount ?? 0}
              </span>
              <span class="chip chip--danger">
                غیرفعال / منقضی: ${s.user.expiredCount ?? 0}
              </span>
            </div>
          </div>
        </section>

        <!-- کارت پیش‌نمایش اعلان‌ها را در پنل حذف کردیم -->

        <section class="panel-section">
          <div class="section-header section-header--with-action">
            <h2 class="section-title">سرویس‌های شما</h2>
            <button class="btn btn--ghost btn--xs" id="btn-refresh-services">
              ریفرش سرویس‌ها
            </button>
          </div>
          <div class="service-list service-list--full">
            ${servicesHtml}
          </div>
        </section>

      </section>
    `;
  }

  // [11.3] Full service item in panel
  function renderPanelServiceItem(svc) {
    const percent = clampNumber(svc.percent ?? 0, 0, 100);
    const used = svc.usedGB ?? 0;
    const total = svc.totalGB ?? 0;
    const remain = svc.remainingGB ?? 0;

    const statusMap = {
      active: "فعال",
      expired: "منقضی",
      disabled: "غیرفعال",
      on_hold: "معلق",
    };
    const statusLabel = statusMap[svc.status] || svc.status || "نامشخص";

    const badgeClass =
      svc.status === "active"
        ? "badge badge--ok"
        : svc.status === "expired" || svc.status === "disabled"
        ? "badge badge--danger"
        : "badge badge--warn";

    let expireText = "—";
    if (typeof svc.expireDays === "number") {
      if (svc.expireDays <= 0) expireText = "منقضی شده";
      else if (svc.expireDays === 1) expireText = "۱ روز مانده";
      else expireText = `${svc.expireDays} روز مانده`;
    }
    
let metaHtml = "";
if (svc.panelKey) {
  const serverType = svc.serverType || detectServerTypeFromPanelKey(svc.panelKey);
  const serverTypeLabel = getPlanServerTypeLabel(serverType);

  const locCode = svc.locationCode || detectLocationFromPanelKey(svc.panelKey);
  const locMeta = getLocationMeta(locCode);

  const isMultiLocation = serverType === "multi" || locMeta.code === "multi";

  let flagHtml = "";

  if (isMultiLocation) {
    const locCodes = detectAllLocationsFromPanelKey(svc.panelKey || "");
    const codes = locCodes.length ? locCodes : ["tr", "de", "us", "fi"];

    const imgs = codes
      .map((code) => {
        const m = getLocationMeta(code);
        if (!m.flagUrl) return "";
        return `<img src="${escapeHtml(m.flagUrl)}" alt="${escapeHtml(
          m.name
        )}" loading="lazy">`;
      })
      .join("");

    flagHtml = `
      <span class="service-card__flag-group">
        ${imgs}
      </span>
    `;
  } else if (locMeta.flagUrl) {
    flagHtml = `
      <img class="service-card__flag"
           src="${escapeHtml(locMeta.flagUrl)}"
           alt="${escapeHtml(locMeta.name)}"
           loading="lazy">
    `;
  }

  const tunnelMode = svc.tunnelMode || detectTunnelModeFromPanelKey(svc.panelKey);
  const tunnelTagHtml = renderTunnelTag(tunnelMode);

  metaHtml = `
    <div class="service-card__meta-row plan-meta-row">
      <span class="plan-meta-tag plan-meta-tag--location">
        ${escapeHtml(locMeta.name)}
      </span>
      ${flagHtml}
      ${tunnelTagHtml}
    </div>
  `;
}


    const subUrl = svc.subscriptionUrl || "";

    return `
      <article class="service-card service-card--panel"
        data-service-username="${escapeHtml(svc.username)}"
        data-sub-url="${escapeHtml(subUrl)}"
        data-service-id="${escapeHtml(String(svc.id ?? ""))}"
      >
        <!-- row 1: title + expire + badge + renew -->
        <div class="service-card__top service-card__top--compact">
          <div class="service-card__title" data-keep-en-digits>
            ${escapeHtml(svc.username)}
          </div>
          <div class="service-card__top-right">
            <span class="service-card__expire">${expireText}</span>
            <span class="${badgeClass}">${statusLabel}</span>
            <button class="btn btn--ghost btn--xs svc-renew-btn">
              تمدید
            </button>
          </div>
        </div>
        
        ${metaHtml}

        <!-- row 2: usage text + progress + remaining in one line -->
        <div class="service-card__usage service-card__usage--inline">
          <div class="service-card__usage-text" data-keep-en-digits>
            <span>${used} / ${total || "∞"} GB</span>
            <span>${total ? percent + "%" : "نامحدود"}</span>
          </div>
          <div class="progress progress--inline">
            <div class="progress__bar" style="width:${
              total ? percent : 0
            }%;"></div>
          </div>
          <div class="service-card__usage-remaining">
            باقیمانده: <b>${remain}</b> GB
          </div>
        </div>

        <div class="service-card__footer">
          <div class="service-card__sub">
            <span class="small">لینک اشتراک:</span>
            ${
              subUrl
                ? `
              <div class="sub-url__box">
                <input
                  type="text"
                  class="sub-url__input"
                  readonly
                  value="${escapeHtml(subUrl)}"
                >
              </div>
            `
                : `<span class="text-muted small">—</span>`
            }
          </div>

          ${
            subUrl
              ? `
            <div class="service-card__actions">
              <button class="btn btn--primary btn--xs svc-open-btn">باز کردن</button>
              <button class="btn btn--ghost btn--xs svc-copy-btn">کپی</button>
              <button class="btn btn--danger btn--xs svc-qr-btn">QR</button>
              <button class="btn btn--ghost btn--xs svc-configs-btn">کانفیگ‌ها</button>
<button
  class="btn btn--outline btn--xs svc-usage-btn"
  type="button"
  title="نمودار مصرف"
>
  <i class="fa-solid fa-chart-line" aria-hidden="true"></i>
</button>
              <button class="btn btn--outline btn--xs svc-refund-btn">
                عودت وجه
              </button>
            </div>
          `
              : `
            <div class="service-card__actions">
              <button class="btn btn--outline btn--xs svc-refund-btn">
                عودت وجه
              </button>
              <button class="btn btn--outline btn--xs svc-usage-btn">نمودار مصرف</button>
            </div>
          `
          }
        </div>
      </article>
    `;
  }

  function findServiceByUsername(username) {
    return (
      state.services.find((svc) => svc.username === username) || null
    );
  }

  function attachPanelLoggedEvents() {
    const refreshBtn = document.getElementById("btn-refresh-services");
    if (refreshBtn) {
      refreshBtn.addEventListener("click", async () => {
        await loadPanelData();
        await loadNotifications();
        renderPanelView();
        applyFaDigits(viewContainer);
      });
    }

    // shortcuts
    const btnPayments = document.getElementById("btn-panel-payments");
    if (btnPayments) {
      btnPayments.addEventListener("click", () => {
        fillPaymentsModalTable();
        openModal("payments");
      });
    }

    const btnBot = document.getElementById("btn-panel-telegram-bot");
    if (btnBot) {
      btnBot.addEventListener("click", () => {
        const url = state.user.telegramBotUrl || "https://t.me/dimobybot";
        window.open(url, "_blank");
      });
    }

    const btnChangePass = document.getElementById("btn-panel-change-pass");
    if (btnChangePass) {
      btnChangePass.addEventListener("click", () => openModal("change-pass"));
    }

    const btnChannel = document.getElementById("btn-panel-telegram-channel");
    if (btnChannel) {
      btnChannel.addEventListener("click", () => {
        const url =
          state.user.telegramChannelUrl || "https://t.me/diminett";
        window.open(url, "_blank");
      });
    }
    
        const btnEnablePush = document.getElementById("btn-enable-push");
    if (btnEnablePush) {
      btnEnablePush.addEventListener("click", () => {
        enablePushNotifications();
      });
    }


    const btnWalletCharge = document.getElementById("btn-wallet-charge");
    if (btnWalletCharge) {
      btnWalletCharge.addEventListener("click", () => openModal("wallet"));
    }

    const panelEl = viewContainer;

    if (panelEl) {
      panelEl
        .querySelectorAll(".js-open-announcements")
        .forEach((btn) => {
          btn.addEventListener("click", () => {
            fillAnnouncementsModal();
            openModal("announcements");
          });
        });

    }

    document.querySelectorAll(".service-card--panel").forEach((card) => {
      const username = card.getAttribute("data-service-username");
        const serviceIdStr = card.getAttribute("data-service-id");
  const serviceId = serviceIdStr ? Number(serviceIdStr) : null;
      const subUrl = card.getAttribute("data-sub-url") || "";

      const openBtn = card.querySelector(".svc-open-btn");
      const copyBtn = card.querySelector(".svc-copy-btn");
      const renewBtn = card.querySelector(".svc-renew-btn");
      const qrBtn = card.querySelector(".svc-qr-btn");
      const configsBtn = card.querySelector(".svc-configs-btn");
      const refundBtn = card.querySelector(".svc-refund-btn");
      const usageBtn = card.querySelector(".svc-usage-btn");


      if (openBtn && subUrl) {
        openBtn.addEventListener("click", () => {
          window.open(subUrl, "_blank");
        });
      }
      
if (usageBtn && (username || serviceId)) {
  usageBtn.addEventListener("click", () => {
    let svc = null;

    if (username) {
      svc = findServiceByUsername(username);
    }

    // اگر به هر دلیلی از روی username پیدا نشد، سعی کن با id پیدا کنی
    if (!svc && serviceId != null) {
      svc = state.services.find(
        (s) => Number(s.id) === Number(serviceId)
      ) || null;
    }

    if (!svc) return;

    // id رو هم مطمئن شو توی شیء هست
    if (serviceId != null && (svc.id == null)) {
      svc.id = serviceId;
    }

    openUsageModal(svc);
  });
}


      if (copyBtn && subUrl) {
        copyBtn.addEventListener("click", () => {
          navigator.clipboard.writeText(subUrl).then(() => {
            const old = copyBtn.textContent;
            copyBtn.textContent = "کپی ✓";
            setTimeout(() => (copyBtn.textContent = old), 1200);
          });
        });
      }

      if (qrBtn && subUrl) {
        qrBtn.addEventListener("click", () => {
          openQrModal(subUrl);
        });
      }

      if (configsBtn && username) {
        configsBtn.addEventListener("click", () => {
          const svc = findServiceByUsername(username);
          openConfigsModal(svc);
        });
      }

      if (renewBtn && username) {
        renewBtn.addEventListener("click", () => {
          const svcObj = findServiceByUsername(username);
          const panelKey = svcObj && svcObj.panelKey ? svcObj.panelKey : "";

          const url = new URL("/index.php", window.location.origin);
          url.searchParams.set("tab", "home");
          url.searchParams.set("renew", username);
          if (panelKey) {
            url.searchParams.set("panel_key", panelKey);
          }

          window.location.href = url.toString();
        });
      }

      if (refundBtn && username) {
        refundBtn.addEventListener("click", () => {
          startRefundFlow(username);
        });
      }
    });
  }

  // ====================================================
  // [12] Refund flow
  // ====================================================

  function startRefundFlow(username) {
    if (!username) return;
    if (!state.isLoggedIn || !state.user.email) {
      alert("برای ثبت درخواست عودت وجه باید وارد حساب شده باشی.");
      return;
    }
    checkRefundEligibility(username);
  }

  async function checkRefundEligibility(username) {
    showLoader();
    try {
      const res = await fetch("/refund_request_check.php", {
        method: "POST",
        credentials: "same-origin",
        headers: {
          "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
          "X-Requested-With": "XMLHttpRequest",
        },
        body: new URLSearchParams({
          service_username: username,
        }).toString(),
      });

      const data = await res.json().catch(() => null);

      if (!data || data.ok === false || data.can_request === false) {
        const msg =
          (data && data.message) ||
          "مهلت عودت وجه برای این سرویس تمام شده یا امکان عودت وجود ندارد.";
        alert(msg);
        return;
      }

      // اینجا service_id را از پاسخ PHP ذخیره می‌کنیم
      state.refundTargetServiceId = data.service_id || 0;
      state.refundTargetUsername = username;

      openRefundModal(username);
    } catch (err) {
      console.error("refund check error", err);
      alert("خطا در بررسی امکان عودت وجه.");
    } finally {
      hideLoader();
    }
  }

  function openRefundModal(username) {
    const svc = findServiceByUsername(username);

    const svcField = document.getElementById("refundServiceName");
    const emailField = document.getElementById("refundEmail");
    const msgBox = document.getElementById("refundMsg");
    const reasonInput = document.getElementById("refundReason");

    if (svcField) {
      // فقط برای نمایش
      svcField.value = svc ? svc.username : username;
    }
    if (emailField) {
      emailField.value = state.user.email || "";
    }
    if (reasonInput) {
      reasonInput.value = "";
    }
    if (msgBox) {
      msgBox.style.display = "none";
      msgBox.textContent = "";
      msgBox.className = "modal__alert";
    }

    openModal("refund");
  }

  async function handleRefundSubmit() {
    const username = state.refundTargetUsername;
    const serviceId = state.refundTargetServiceId || 0;
    const reasonInput = document.getElementById("refundReason");
    const msgBox = document.getElementById("refundMsg");
    if (!username || !reasonInput || !msgBox) return;

    const reason = (reasonInput.value || "").trim();

    msgBox.style.display = "none";
    msgBox.textContent = "";
    msgBox.className = "modal__alert";

    if (reason.length < 10) {
      msgBox.textContent =
        "لطفاً دلیل عودت وجه را با جزئیات بنویس (حداقل ۱۰ کاراکتر).";
      msgBox.classList.add("modal__alert--error");
      msgBox.style.display = "block";
      return;
    }

    const btn = document.getElementById("refundSubmitBtn");
    if (btn) {
      btn.disabled = true;
      btn.textContent = "در حال ثبت...";
    }

    try {
      const res = await fetch("/refund_request_submit.php", {
        method: "POST",
        credentials: "same-origin",
        headers: {
          "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
          "X-Requested-With": "XMLHttpRequest",
        },
        body: new URLSearchParams({
          service_id: serviceId,
          service_username: username,
          reason: reason,
          ajax: "1",
        }).toString(),
      });

      const data = await res.json().catch(() => null);

      if (!data || data.ok === false) {
        const msg =
          (data && data.message) ||
          "خطا در ثبت درخواست عودت. لطفاً بعداً دوباره تلاش کن.";
        msgBox.textContent = msg;
        msgBox.classList.add("modal__alert--error");
        msgBox.style.display = "block";
        return;
      }

      msgBox.textContent =
        data.message ||
        "درخواستت با موفقیت ثبت شد. تا ۲۴ ساعت آینده نتیجه از طریق اعلان بهت اطلاع داده می‌شود.";
      msgBox.classList.add("modal__alert--ok");
      msgBox.style.display = "block";

      state.refundTargetUsername = "";
      state.refundTargetServiceId = 0;
      reasonInput.value = "";

      setTimeout(() => {
        closeAnyModal();
      }, 1200);
    } catch (err) {
      console.error("refund submit error", err);
      msgBox.textContent = "خطا در ارتباط با سرور.";
      msgBox.classList.add("modal__alert--error");
      msgBox.style.display = "block";
    } finally {
      if (btn) {
        btn.disabled = false;
        btn.textContent = "ثبت درخواست";
      }
    }
  }

  // ====================================================
  // [13] QR & Configs modals
  // ====================================================

  function openQrModal(link) {
    const iframe = document.getElementById("qrImage");
    if (iframe) {
      iframe.src = "/qr.php?data=" + encodeURIComponent(link);
    }
    openModal("qr");
  }

  function openConfigsModal(svc) {
    const listEl = document.getElementById("configsList");
    if (!listEl) return;

    if (!svc || !Array.isArray(svc.configs) || svc.configs.length === 0) {
      listEl.innerHTML = `
        <p class="text-muted small">
          فعلاً کانفیگی برای این سرویس ثبت نشده است. به‌زودی از داخل پنل اضافه می‌شود.
        </p>
      `;
    } else {
      listEl.innerHTML = svc.configs
        .map((cfg, idx) => {
          let label = `کانفیگ ${idx + 1}`;
          let linkText = "";

          if (typeof cfg === "string") {
            linkText = cfg;
          } else if (cfg && typeof cfg === "object") {
            linkText =
              cfg.link ||
              cfg.url ||
              cfg.config ||
              "";

            if (cfg.label) {
              label = cfg.label;
            } else if (cfg.type === "subscription") {
              label = "لینک اشتراک";
            }
          } else {
            linkText = String(cfg ?? "");
          }

          return `
            <div class="config-item">
              <div class="config-item__head">
                <span>${escapeHtml(label)}</span>
                <button type="button" class="btn btn--ghost btn--xs btn-copy-config" data-config-idx="${idx}">
                  کپی
                </button>
              </div>
              <textarea class="config-item__code" readonly>${escapeHtml(
                linkText
              )}</textarea>
            </div>
          `;
        })
        .join("");
    }

    listEl.querySelectorAll(".btn-copy-config").forEach((btn) => {
      btn.addEventListener("click", () => {
        const item = btn.closest(".config-item");
        const ta = item?.querySelector(".config-item__code");
        const val = ta?.value || ta?.textContent || "";
        if (!val) return;

        navigator.clipboard.writeText(val).then(() => {
          const old = btn.textContent;
          btn.textContent = "کپی شد ✓";
          setTimeout(() => (btn.textContent = old), 1200);
        });
      });
    });

    openModal("configs");
  }


function openUsageModal(svc) {
  if (!svc) return;

  const titleEl    = document.getElementById("usageModalTitle");
  const subtitleEl = document.getElementById("usageModalSubtitle");
  const chartEl    = document.getElementById("usageChartContainer");
  if (!chartEl) return;

  const used    = Number(svc.usedGB || 0);
  const total   = Number(svc.totalGB || 0);
  const remain  = Number(svc.remainingGB || 0);
  const percent = total > 0
    ? clampNumber(Math.round((used / total) * 100), 0, 100)
    : 0;

  if (titleEl) {
    titleEl.textContent = "نمودار مصرف سرویس " + (svc.username || "");
  }

  if (subtitleEl) {
    if (total) {
      subtitleEl.innerHTML =
        'حجم کل: <b data-keep-en-digits>' + total +
        "</b> GB — مصرف شده: <b data-keep-en-digits>" +
        used + "</b> GB";
    } else {
      subtitleEl.textContent =
        "این سرویس نامحدود است، عدد مصرف لحظه‌ای فقط برای نمایش است.";
    }
  }

  // بخش وضعیت لحظه‌ای بالای نمودار
  chartEl.innerHTML = `
    <div class="usage-chart__row">
      <div class="usage-chart__label small">مصرف شده</div>
      <div class="usage-chart__value small" data-keep-en-digits>
        ${used} GB (${total ? percent + "%" : "نامحدود"})
      </div>
    </div>

    <div class="progress progress--inline" style="margin-top:8px;">
      <div class="progress__bar" style="width:${total ? percent : 0}%;"></div>
    </div>

    <div class="usage-chart__row" style="margin-top:8px;">
      <div class="usage-chart__label small">باقیمانده</div>
      <div class="usage-chart__value small" data-keep-en-digits>
        ${remain} GB
      </div>
    </div>

    <hr style="margin:12px 0; opacity:0.2;">

    <div id="usageHistorySection">
      <p id="usageHistoryStatus" class="small text-muted">
        در حال دریافت تاریخچه مصرف...
      </p>
    </div>
  `;

  // اول مودال رو باز کن که یوزر لودینگ رو ببینه
  openModal("usage");

  // اگر id نداریم، دیگه تاریخچه‌ای نمی‌تونیم بگیریم
  if (svc.id == null) {
    const statusEl = document.getElementById("usageHistoryStatus");
    if (statusEl) {
      statusEl.textContent =
        "شناسه سرویس در دسترس نیست، تاریخچه قابل نمایش نیست.";
      statusEl.classList.add("text-danger");
    }
    return;
  }

  // تاریخچه مصرف از API
  loadUsageHistoryFromApi(svc.id);
}



async function loadUsageHistoryFromApi(serviceId) {
  const historyWrap = document.getElementById("usageHistorySection");
  const statusEl    = document.getElementById("usageHistoryStatus");

  if (!historyWrap) return;

  if (statusEl) {
    statusEl.textContent = "در حال دریافت تاریخچه مصرف...";
    statusEl.classList.remove("text-danger");
  }

  try {
    const res = await fetch(
      "/api_service_usage.php?service_id=" +
        encodeURIComponent(serviceId) +
        "&limit=200",
      {
        method: "GET",
        credentials: "same-origin",
      }
    );

    if (!res.ok) {
      throw new Error("HTTP " + res.status);
    }

    const data = await res.json();
    if (!data || data.ok !== true) {
      throw new Error(data && data.error ? data.error : "invalid response");
    }

    renderUsageHistoryInModal(historyWrap, data);
  } catch (err) {
    console.warn("loadUsageHistoryFromApi error:", err);
    if (statusEl) {
      statusEl.textContent = "خطا در دریافت تاریخچه مصرف.";
      statusEl.classList.add("text-danger");
    }
  }
}



function renderUsageHistoryInModal(historyWrap, apiData) {
  const statusEl = document.getElementById("usageHistoryStatus");
  if (!historyWrap) return;

  const items = Array.isArray(apiData.items) ? apiData.items : [];
  if (!items.length) {
    if (statusEl) {
      statusEl.textContent = "هنوز تاریخچه‌ای برای این سرویس ثبت نشده.";
      statusEl.classList.remove("text-danger");
    } else {
      historyWrap.innerHTML = `
        <p class="small text-muted">
          هنوز تاریخچه‌ای برای این سرویس ثبت نشده.
        </p>
      `;
    }
    return;
  }

  if (statusEl) {
    statusEl.remove();
  }

  // سقف حجم برای محاسبه درصد مصرف
  const globalLimitBytes =
    apiData.service && apiData.service.data_limit_bytes != null
      ? Number(apiData.service.data_limit_bytes)
      : null;

  const points = items
    .filter((it) => it.used_bytes != null)
    .map((it) => {
      const usedBytes  = Number(it.used_bytes);
      const limitBytes =
        it.data_limit_bytes != null
          ? Number(it.data_limit_bytes)
          : globalLimitBytes;

      const percent = limitBytes
        ? clampNumber(Math.round((usedBytes / limitBytes) * 100), 0, 100)
        : null;

      return {
        snapshot_at: it.snapshot_at, // از API میاد (alias روی snapshot_time)
        percent,
      };
    })
    .filter((p) => p.percent != null);

  if (!points.length) {
    historyWrap.innerHTML = `
      <p class="small text-muted">
        امکان محاسبه درصد مصرف از روی لاگ‌ها وجود نداشت.
      </p>
    `;
    return;
  }

  // فقط آخرین ۳۰ نقطه
  const maxPoints = 30;
  const sliced =
    points.length > maxPoints
      ? points.slice(points.length - maxPoints)
      : points;

  const width  = 320;
  const height = 90;
  const padX   = 8;
  const padY   = 8;

  const innerW = width  - padX * 2;
  const innerH = height - padY * 2;

  const stepX = sliced.length > 1 ? innerW / (sliced.length - 1) : 0;

  let pathD = "";
  sliced.forEach((p, idx) => {
    const x = padX + stepX * idx;
    const y = padY + (innerH * (100 - p.percent) / 100);
    pathD += (idx === 0 ? "M" : " L") + x + " " + y;
  });

  const firstLabel = sliced[0].snapshot_at || "";
  const lastLabel  = sliced[sliced.length - 1].snapshot_at || "";

  historyWrap.innerHTML = `
    <p class="small" style="margin-bottom:4px;">
      تاریخچه مصرف (نسبت به سقف حجم) در آخرین دفعات بروزرسانی:
    </p>

    <svg
      viewBox="0 0 ${width} ${height}"
      xmlns="http://www.w3.org/2000/svg"
      class="usage-chart__svg"
      style="width:100%;max-width:100%;display:block;"
    >
      <defs>
        <linearGradient id="usageFill" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%"   stop-color="#22c55e" stop-opacity="0.35"/>
          <stop offset="100%" stop-color="#22c55e" stop-opacity="0"/>
        </linearGradient>
      </defs>

      <!-- محور افقی -->
      <line
        x1="${padX}"
        y1="${height - padY}"
        x2="${width - padX}"
        y2="${height - padY}"
        stroke="#64748b"
        stroke-width="0.5"
      />

      <!-- خط نمودار -->
      <path
        d="${pathD}"
        fill="none"
        stroke="#22c55e"
        stroke-width="2"
        stroke-linejoin="round"
        stroke-linecap="round"
      />

      <!-- ناحیه زیر نمودار -->
      <path
        d="${pathD}
           L ${padX + innerW} ${height - padY}
           L ${padX} ${height - padY} Z"
        fill="url(#usageFill)"
        stroke="none"
      />
    </svg>

    <div class="usage-chart__timeline-labels small">
      <span class="usage-chart__timeline-label usage-chart__timeline-label--start">
        ${escapeHtml(firstLabel)}
      </span>
      <span class="usage-chart__timeline-label usage-chart__timeline-label--end">
        ${escapeHtml(lastLabel)}
      </span>
    </div>

  `;
}




  // ====================================================
  // [14] HELP view (how-to)
  // ====================================================

  function renderHelpView() {
    viewContainer.innerHTML = helpTemplate();
    attachHelpEvents();
  }

function helpTemplate() {
  return `
      <section class="view view--help">
        <section class="help-section">
          <div class="help-video-actions">
            <button class="btn btn--primary btn--sm" id="btn-help-video-android">
              آموزش ویدیویی اندروید
            </button>
            <button class="btn btn--primary btn--sm" id="btn-help-video-ios">
              آموزش ویدیویی iOS
            </button>
          </div>

          <!-- باکس چک‌لیست قبل از هر چیز -->
          <div class="home-box help-precheck-box">
            <h2 class="section-title small" style="margin-bottom:6px;">
              قبل از هر چیز این موارد را انجام بده 👇
            </h2>
            <ul class="help-precheck-list">
              <li>
                توی <b>اندروید</b> فقط برنامه‌های
                <b>v2rayNG</b> و <b>Hiddify</b> رو نصب کنید.
              </li>
              <li>
                هر برنامه <b>VPN دیگه‌ای</b> که دارید (رایگان و غیررایگان)
                رو قبل از اتصال <b>حتماً پاک کنید</b>.
              </li>
              <li>
                <b>تاریخ و ساعت گوشی</b>تون حتما روی حالت
                <b>خودکار (Automatic)</b> باشه.
              </li>
              <li>
                برای اینترنت همراه (سیم‌کارت)،
                حتماً <b>APN</b> رو درست تنظیم کنید.
              </li>
            </ul>
                      <div class="help-extra-actions" style="margin-top:8px;justify-content: center;">
            <button
              class="btn btn--outline btn--sm"
              id="btn-help-apn"
            >
             طریقه تنظیم (APN)
            </button>
          </div>
          </div>


          <p class="text-muted">
 </p>
          <div class="help-tabs" role="tablist" aria-label="Platforms">
            <button class="help-tab-btn is-active" data-help-tab="android" role="tab" aria-selected="true">
              اندروید
            </button>
            <button class="help-tab-btn" data-help-tab="ios" role="tab" aria-selected="false">
              iOS
            </button>
            <button class="help-tab-btn" data-help-tab="windows" role="tab" aria-selected="false">
              ویندوز
            </button>
          </div>

          <div class="help-tabpanes">
            <!-- Android -->
            <div class="help-pane help-pane--android is-active" data-help-pane="android">
              <h2 class="section-title">اندروید — v2rayNG</h2>
              <p class="text-muted small">
                اپ پیشنهادی: <b>v2rayNG</b> (از گوگل‌پلی یا صفحهٔ دانلود دیمینت).
              </p>

              <ol class="help-steps">
                <li>
                  <p class="small">۱. برنامه v2rayNG رو نصب و باز کن.</p>
                  <img loading="lazy" src="https://dl.dimoby.ir/downloads/learn/a1.jpg" alt="آموزش اندروید ۱">
                </li>
                <li>
                  <p class="small">۲. روی علامت + بزن و گزینه Import from clipboard یا Subscription رو انتخاب کن.</p>
                  <img loading="lazy" src="https://dl.dimoby.ir/downloads/learn/a2.jpg" alt="آموزش اندروید ۲">
                </li>
                <li>
                  <p class="small">۳. لینک اشتراک (Sub) دیمینت رو کپی کن و داخل برنامه وارد کن.</p>
                  <img loading="lazy" src="https://dl.dimoby.ir/downloads/learn/a3.jpg" alt="آموزش اندروید ۳">
                </li>
                <li>
                  <p class="small">۴. روی دکمه Update subscription بزن تا سرورها لود بشن.</p>
                  <img loading="lazy" src="https://dl.dimoby.ir/downloads/learn/a4.jpg" alt="آموزش اندروید ۴">
                </li>
                <li>
                  <p class="small">۵. یک سرور رو انتخاب کن و روی دکمه اتصال بزن.</p>
                  <img loading="lazy" src="https://dl.dimoby.ir/downloads/learn/a5.jpg" alt="آموزش اندروید ۵">
                </li>
                <li>
                  <p class="small">۶. اگر خواستی سرور عوض کنی، فقط از همین لیست، سرور دیگه رو انتخاب کن.</p>
                  <img loading="lazy" src="https://dl.dimoby.ir/downloads/learn/a6.jpg" alt="آموزش اندروید ۶">
                </li>
              </ol>
            </div>

            <!-- iOS -->
            <div class="help-pane help-pane--ios" data-help-pane="ios">
              <h2 class="section-title">iOS — Shadowrocket / Streisand</h2>
              <p class="text-muted small">
                اپ‌های متداول: <b>Shadowrocket</b> یا <b>Streisand</b> (بسته به اپ‌استور و دسترسی).
              </p>
              <ol class="help-steps">
                <li>
                  <p class="small">۱. اپ رو نصب و باز کن.</p>
                  <img loading="lazy" src="https://dl.dimoby.ir/downloads/learn/i1.jpg" alt="آموزش iOS ۱">
                </li>
                <li>
                  <p class="small">۲. از بخش Subscription لینک اشتراک دیمینت رو اضافه کن.</p>
                  <img loading="lazy" src="https://dl.dimoby.ir/downloads/learn/i2.jpg" alt="آموزش iOS ۲">
                </li>
                <li>
                  <p class="small">۳. روی Update بزن تا لیست سرورها بیاد.</p>
                  <img loading="lazy" src="https://dl.dimoby.ir/downloads/learn/i3.jpg" alt="آموزش iOS ۳">
                </li>
                <li>
                  <p class="small">۴. یکی از سرورها رو انتخاب کن و دکمه اتصال رو بزن.</p>
                  <img loading="lazy" src="https://dl.dimoby.ir/downloads/learn/i4.jpg" alt="آموزش iOS ۴">
                </li>
              </ol>
            </div>

            <!-- Windows -->
            <div class="help-pane help-pane--windows" data-help-pane="windows">
              <h2 class="section-title">ویندوز — Clash / Nekoray</h2>
              <p class="text-muted small">
                اپ‌های پیشنهادی: <b>Clash Verge</b> یا <b>Nekoray</b>.
              </p>
              <ol class="help-steps">
                <li>
                  <p class="small">۱. برنامه را از لینک‌های رسمی نصب کن.</p>
                  <img loading="lazy" src="https://dl.dimoby.ir/downloads/learn/w1.jpg" alt="آموزش ویندوز ۱">
                </li>
                <li>
                  <p class="small">۲. از بخش Subscriptions، لینک اشتراک دیمینت را اضافه کن.</p>
                  <img loading="lazy" src="https://dl.dimoby.ir/downloads/learn/w2.jpg" alt="آموزش ویندوز ۲">
                </li>
                <li>
                  <p class="small">۳. روی Update/Refresh بزن تا سرورها لود شوند.</p>
                  <img loading="lazy" src="https://dl.dimoby.ir/downloads/learn/w3.jpg" alt="آموزش ویندوز ۳">
                </li>
                <li>
                  <p class="small">۴. یک پروفایل را انتخاب و روی Start/Connect کلیک کن.</p>
                  <img loading="lazy" src="https://dl.dimoby.ir/downloads/learn/w4.jpg" alt="آموزش ویندوز ۴">
                </li>
                <li>
                  <p class="small">۵. اگر از Clash استفاده می‌کنی، مطمئن شو System Proxy روشن است.</p>
                  <img loading="lazy" src="https://dl.dimoby.ir/downloads/learn/w5.jpg" alt="آموزش ویندوز ۵">
                </li>
              </ol>
            </div>
          </div>
        </section>
      </section>
    `;
}

  function attachHelpEvents() {
    const tabButtons = document.querySelectorAll(".help-tab-btn");
    const panes = document.querySelectorAll(".help-pane");

    tabButtons.forEach((btn) => {
      btn.addEventListener("click", () => {
        const key = btn.getAttribute("data-help-tab");
        tabButtons.forEach((b) => b.classList.remove("is-active"));
        panes.forEach((p) => p.classList.remove("is-active"));

        btn.classList.add("is-active");
        const pane = document.querySelector(
          `[data-help-pane="${key}"]`
        );
        if (pane) pane.classList.add("is-active");
      });
    });

    const btnAndroid = document.getElementById("btn-help-video-android");
    if (btnAndroid) {
      btnAndroid.addEventListener("click", () => openVideoModal("android"));
    }
    const btnIos = document.getElementById("btn-help-video-ios");
    if (btnIos) {
      btnIos.addEventListener("click", () => openVideoModal("ios"));
    }
    
        const btnApn = document.getElementById("btn-help-apn");
    if (btnApn) {
      btnApn.addEventListener("click", () => {
        openModal("apn_guide");
      });
    }


    const viewEl = viewContainer;
    if (viewEl) {
      viewEl
        .querySelectorAll(".js-open-announcements")
        .forEach((btn) => {
          btn.addEventListener("click", () => {
            fillAnnouncementsModal();
            openModal("announcements");
          });
        });
    }
  }

  // ====================================================
  // [15] Modals: build + generic open/close
  // ====================================================

  function buildModals() {
    if (document.getElementById(modalIds.login)) return;

    document.body.insertAdjacentHTML(
      "beforeend",
      `
      <!-- Loader -->
      <div id="app-loader" class="app-loader">
        <div class="app-loader__spinner"></div>
      </div>

<!-- Modal: Login -->
<div id="${modalIds.login}" class="modal-backdrop" data-modal>
  <div class="modal">
    <h2 class="modal__title">ورود به پنل</h2>

    <!-- تصویر بعد از عنوان -->
    <div class="modal__illustration">
      <img src="/assets/login.png" alt="ورود به پنل دیمینت">
    </div>

    <p class="modal__text small">
      ایمیلی که با آن خرید انجام دادی را وارد کن. رمز همان رمزی است که بعد از خرید بهت نمایش داده شد.
    </p>
    <div id="loginMsg" class="modal__alert" style="display:none;"></div>
    <form class="modal__form" id="loginForm" method="POST" action="/login.php">
      <input class="modal__input" type="email" name="email" placeholder="ایمیل" required>
      <input class="modal__input" type="password" name="password" placeholder="رمز عبور" required>
      <p class="small modal__forgot">
        <span>رمزتو فراموش کردی؟</span>
        <span class="modal__forgot-link" id="openForgotFromLogin">
          بازنشانی رمز عبور
        </span>
      </p>
      <div class="modal__actions">
        <button type="button" class="btn btn--ghost" data-modal-close>انصراف</button>
        <button type="submit" class="btn btn--primary" id="loginSubmitBtn">ورود</button>
      </div>
    </form>
  </div>
</div>

      <!-- Modal: Forgot Password -->
      <div id="${modalIds.forgot}" class="modal-backdrop" data-modal>
        <div class="modal">
          <h2 class="modal__title">بازیابی رمز عبور</h2>
          <p class="modal__text small">
            ایمیلی که با آن خرید انجام دادی را وارد کن. اگر ایمیل در سیستم وجود داشته باشد، لینک تغییر رمز برایت ارسال می‌شود.
          </p>
          <div id="forgotMsg" class="modal__alert" style="display:none;"></div>
          <input
            id="forgotEmail"
            class="modal__input"
            type="email"
            placeholder="ایمیل"
            autocomplete="email"
            required
          >
          <div class="modal__actions">
            <button type="button" class="btn btn--ghost" data-modal-close>انصراف</button>
            <button type="button" class="btn btn--primary" id="forgotSubmitBtn">
              ارسال لینک
            </button>
          </div>
        </div>
      </div>

      <!-- Modal: Payments -->
      <div id="${modalIds.payments}" class="modal-backdrop" data-modal>
        <div class="modal modal--lg">
          <h2 class="modal__title">تاریخچه پرداخت‌ها</h2>
          <p class="modal__text small">
            لیست پرداخت‌های شما بر اساس ایمیلی که وارد کرده‌اید.
          </p>
          <div class="payments-table-wrap">
            <table class="payments-table">
              <thead>
                <tr>
                  <th>تاریخ</th>
                  <th>پلن</th>
                  <th>مبلغ (ریال)</th>
                  <th>کد رسید</th>
                  <th>وضعیت</th>
                </tr>
              </thead>
              <tbody id="payments-table-body">
              </tbody>
            </table>
          </div>
          <div class="modal__actions" style="margin-top:12px;">
            <button type="button" class="btn btn--primary" data-modal-close>بستن</button>
          </div>
        </div>
      </div>

      <!-- Modal: Change Password -->
      <div id="${modalIds["change-pass"]}" class="modal-backdrop" data-modal>
        <div class="modal">
          <h2 class="modal__title">تغییر رمز ورود</h2>
          <p class="modal__text small">
            رمز جدید را وارد و تأیید کن. حداقل ۶ کاراکتر.
          </p>
          <div id="cpMsg" class="modal__alert" style="display:none;"></div>
          <input id="cpNew" class="modal__input" type="password" placeholder="رمز جدید" minlength="6">
          <input id="cpConfirm" class="modal__input" type="password" placeholder="تکرار رمز جدید" minlength="6">
          <div class="modal__actions">
            <button type="button" class="btn btn--ghost" data-modal-close>انصراف</button>
            <button type="button" class="btn btn--primary" id="cpSubmit">ذخیره</button>
          </div>
        </div>
      </div>

      <!-- Modal: Wallet Charge -->
      <div id="${modalIds.wallet}" class="modal-backdrop" data-modal>
        <div class="modal">
          <h2 class="modal__title">افزایش موجودی کیف پول</h2>
          <p class="modal__text small">
            مبلغ مورد نظر را به تومان وارد کن. حداقل ۱۰,۰۰۰ تومان.
          </p>
          <div id="walletMsg" class="modal__alert" style="display:none;"></div>
          <input
            id="walletAmount"
            class="modal__input"
            type="number"
            min="10000"
            step="1000"
            placeholder="مثلاً ۵۰۰۰۰"
          >
          <div class="modal__actions">
            <button type="button" class="btn btn--ghost" data-modal-close>انصراف</button>
            <button type="button" class="btn btn--primary" id="walletSubmitBtn">
              ادامه به پرداخت
            </button>
          </div>
        </div>
      </div>

      <!-- Modal: Wallet Success -->
      <div id="${modalIds.wallet_success}" class="modal-backdrop" data-modal>
        <div class="modal">
          <h2 class="modal__title">شارژ کیف پول موفق بود 🎉</h2>
          <p class="modal__text small">
            پرداخت با موفقیت انجام شد و مبلغ به کیف پولت اضافه شد.
          </p>
          <div class="modal__actions">
            <button type="button" class="btn btn--primary" id="walletSuccessGoPanel">
              رفتن به پنل
            </button>
            <button type="button" class="btn btn--ghost" data-modal-close>بستن</button>
          </div>
        </div>
      </div>

      <!-- Modal: Success -->
      <div id="${modalIds.success}" class="modal-backdrop" data-modal>
        <div class="modal">
          <h2 class="modal__title">پرداخت موفق 🎉</h2>
          <p class="modal__text small">
            پرداخت شما با موفقیت انجام شد و سرویس جدید به حساب‌تان اضافه شد.
            برای دیدن جزئیات، به تب <b>پنل</b> بروید.
          </p>
          <div class="modal__actions">
            <button type="button" class="btn btn--primary" data-modal-close>باشه</button>
          </div>
        </div>
      </div>

      <!-- Modal: Failed -->
      <div id="${modalIds.failed}" class="modal-backdrop" data-modal>
        <div class="modal">
          <h2 class="modal__title">پرداخت ناموفق</h2>
          <p class="modal__text small">
            پرداخت شما ناموفق بود. اگر از حساب شما مبلغ کم شده،
            از طریق پشتیبانی تلگرام با ما در ارتباط باشید تا پیگیری کنیم.
          </p>
          <div class="modal__actions">
            <a href="${escapeHtml(
              state.user.telegramSupportUrl || "https://t.me/dimynet"
            )}" target="_blank" rel="noopener" class="btn btn--primary">
              پشتیبانی تلگرام
            </a>
            <button type="button" class="btn btn--ghost" data-modal-close>بستن</button>
          </div>
        </div>
      </div>

      <!-- Modal: QR -->
      <div id="${modalIds.qr}" class="modal-backdrop" data-modal>
        <div class="modal">
          <h2 class="modal__title">QR کد اشتراک</h2>
          <p class="modal__text small">
            این QR را با دوربین یا برنامه VPN اسکن کن تا لینک اشتراک اضافه شود.
          </p>
          <div class="qr-modal-body">
            <iframe id="qrImage" class="qr-modal-iframe" frameborder="0"></iframe>
          </div>
          <div class="modal__actions">
            <button type="button" class="btn btn--primary" data-modal-close>بستن</button>
          </div>
        </div>
      </div>

      <!-- Modal: Configs -->
      <div id="${modalIds.configs}" class="modal-backdrop" data-modal>
        <div class="modal modal--lg">
          <h2 class="modal__title">کانفیگ‌های سرویس</h2>
          <p class="modal__text small">
            لیست کانفیگ‌های این سرویس. هر کدام را خواستی کپی کن و در برنامه‌ی مورد نظر وارد کن.
          </p>
          <div id="configsList" class="configs-list"></div>
          <div class="modal__actions" style="margin-top:12px;">
            <button type="button" class="btn btn--primary" data-modal-close>بستن</button>
          </div>
        </div>
      </div>

      <!-- Modal: Video -->
      <div id="${modalIds.video}" class="modal-backdrop" data-modal>
        <div class="modal modal--video">
          <button type="button" class="modal__close" data-modal-close>بستن ✕</button>
          <video id="helpVideo" controls playsinline preload="metadata"></video>
        </div>
      </div>

      <!-- Modal: Terms -->
      <div id="${modalIds.terms}" class="modal-backdrop" data-modal>
        <div class="modal modal--lg">
          <h2 class="modal__title">قوانین دیمینت</h2>

          <ul class="modal__list"
              style="margin:8px 0 16px; padding-right:1.4rem; list-style:disc;">
            <li class="modal__text small">
              پشتیبانی دیمینت از لحظهٔ خرید تا آخرین ثانیهٔ اکانت، کنار شماست تا اتصال پایدار و باکیفیت داشته باشید.
            </li>

            <li class="modal__text small">
              اگر سرویس برای دستگاه یا اینترنت شما کار نکرد و مشکل اتصال برطرف نشد، می‌توانید تا ۷۲ ساعت بعد از خرید درخواست عودت وجه ثبت کنید.
            </li>
            <li class="modal__text small">
              اکانت‌های دیمینت تمام کلاینت‌های مبتنی بر پروتکل V2Ray (مثل v2rayNG، Shadowrocket، Clash و غیره) را پشتیبانی می‌کنند.
            </li>

            <li class="modal__text small">
              رضایت شما افتخار ماست. بیش از ۱۰ سال تجربه در حوزهٔ خدمات VPN، شبکه‌های مجازی و مارکتینگ، پشت سرویس‌های دیمینت است.
            </li>
            <li class="modal__text small" style="color:#b91c1c; font-weight:900;">
              عودت وجه فقط و فقط در صورت حل نشدن مشکل اتصال می‌باشد.
            </li>
          </ul>

          <div class="modal__actions" style="margin-top:4px;">
            <button type="button" class="btn btn--primary" data-modal-close>باشه</button>
          </div>
        </div>
      </div>

      <!-- Modal: Refund Request -->
      <div id="${modalIds.refund}" class="modal-backdrop" data-modal>
        <div class="modal">
          <h2 class="modal__title">درخواست عودت وجه</h2>
<p class="modal__text small">
  این درخواست عودت وجه برای یکی از سرویس‌های خریداری‌شدهٔ شما ثبت می‌شود.
  
  اگر سرویس برای شما قابل استفاده نبوده و مشکل اتصال در ۷۲ ساعت اول خرید حل نشده،
  می‌توانید دلیل را بنویسی تا بررسی کنیم.
</p>
          <div id="refundMsg" class="modal__alert" style="display:none;"></div>

          <div class="modal__field-group">
            <label class="modal__label small">سرویس</label>
            <input id="refundServiceName" class="modal__input" type="text" readonly data-keep-en-digits>
          </div>

          <div class="modal__field-group">
            <label class="modal__label small">ایمیل حساب</label>
            <input id="refundEmail" class="modal__input" type="email" readonly>
          </div>

          <div class="modal__field-group">
            <label class="modal__label small">شرح مشکل و دلیل عودت وجه</label>
            <textarea id="refundReason" class="modal__textarea" rows="4" placeholder="مثلاً: روی اینترنت همراه اول وصل نمی‌شود، چند بار تست کردم ولی همچنان مشکل پابرجاست."></textarea>
          </div>

          <div class="modal__actions">
            <button type="button" class="btn btn--ghost" data-modal-close>انصراف</button>
            <button type="button" class="btn btn--primary" id="refundSubmitBtn">
              ثبت درخواست
            </button>
          </div>
        </div>
      </div>

      <!-- Modal: Announcements list -->
      <div id="${modalIds.announcements}" class="modal-backdrop" data-modal>
        <div class="modal modal--lg">
          <h2 class="modal__title">همه اطلاعیه‌ها</h2>
          <div id="announcementsList" class="modal-list modal-list--announcements"></div>
          <div class="modal__actions" style="margin-top:12px;">
            <button type="button" class="btn btn--primary" data-modal-close>بستن</button>
          </div>
        </div>
      </div>
      
            <!-- Modal: Auto Announcement (یک‌بار برای هر اطلاعیه) -->
      <div id="${modalIds.announcement_auto}" class="modal-backdrop" data-modal>
        <div class="modal">
          <h2 class="modal__title" id="autoAnnTitle">اطلاعیه مهم</h2>
          <p class="modal__text small" id="autoAnnBody">
            <!-- متن اینجا توسط جاوااسکریپت پر می‌شود -->
          </p>
          <div class="modal__actions">
            <button type="button" class="btn btn--primary" data-modal-close>
              باشه
            </button>
          </div>
        </div>
      </div>


      <!-- Modal: Notifications list -->
      <div id="${modalIds.notifications}" class="modal-backdrop" data-modal>
        <div class="modal modal--lg">
          <h2 class="modal__title">اعلان‌های حساب شما</h2>
          <div id="notificationsList" class="modal-list modal-list--notifications"></div>
          <div class="modal__actions" style="margin-top:12px;">
            <button type="button" class="btn btn--primary" data-modal-close>بستن</button>
          </div>
        </div>
      </div>
      
            <!-- Modal: Service Usage (نمودار مصرف) -->
      <div id="${modalIds.usage}" class="modal-backdrop" data-modal>
        <div class="modal">
          <h2 class="modal__title" id="usageModalTitle">نمودار مصرف سرویس</h2>
          <p class="modal__text small" id="usageModalSubtitle"></p>

          <div id="usageChartContainer" class="usage-chart" style="margin-top:12px;">
            <!-- با جاوااسکریپت پر می‌شود -->
          </div>

          <div class="modal__actions" style="margin-top:16px;">
            <button type="button" class="btn btn--primary" data-modal-close>بستن</button>
          </div>
        </div>
      </div>

      <!-- Modal: Push Permission Guide (راهنمای مجوز نوتیف) -->
      <div id="${modalIds.push_guide}" class="modal-backdrop" data-modal>
        <div class="modal">
          <h2 class="modal__title">فعال‌سازی اعلان‌های دیمینت</h2>
          <p class="modal__text small">
            همین الان یک پیام از طرف مرورگر بالای صفحه برات ظاهر شده.
            روی دکمه <b>Allow / اجازه</b> بزن تا وقتی سرویس‌هات در حال اتمام هستن،
            دیمینت بتونه بهت خبر بده.
          </p>
          <p class="modal__text small">
            اگر چیزی نمی‌بینی، احتمالاً قبلاً بسته یا مسدودش کردی.
            از تنظیمات سایت در مرورگر، بخش <b>Notifications</b> را روی <b>Allow</b> بگذار.
          </p>
          <div class="modal__actions">
            <button type="button" class="btn btn--primary" data-modal-close>باشه</button>
          </div>
        </div>
      </div>

      <!-- Modal: PWA Install Guide (راهنمای نصب وب‌اپ) -->
      <div id="${modalIds.pwa_guide}" class="modal-backdrop" data-modal>
        <div class="modal">
          <h2 class="modal__title">نصب وب‌اپ دیمینت</h2>
              <!-- تصویر بعد از عنوان -->
    <div class="modal__illustrationwpa">
      <img src="/assets/wpa.png" alt="ورود به پنل دیمینت">
    </div>

          <p class="modal__text small">
            مرورگر الان گزینهٔ نصب اپ دیمینت را بهت نمایش داده (بالای صفحه یا به صورت پنجره).
            روی دکمه <b>Install / نصب</b> بزن تا دیمینت مثل یک برنامهٔ جدا
            روی گوشی‌ات اضافه شود.
          </p>
          <p class="modal__text small">
            اگر پیغام را نمی‌بینی، معمولاً از منوی مرورگر گزینه
            <b>Add to Home Screen</b> یا <b>Install app</b> را انتخاب کن.
          </p>
          <div class="modal__actions">
            <button type="button" class="btn btn--primary" data-modal-close>فهمیدم</button>
          </div>
        </div>
      </div>

            <!-- Modal: APN Guide (اصلاح و افزایش سرعت اینترنت) -->
      <div id="${modalIds.apn_guide}" class="modal-backdrop" data-modal>
        <div class="modal modal--lg">
          <h2 class="modal__title">اصلاح و افزایش سرعت اینترنت (APN)</h2>

          <p class="modal__text small">
            اگر اینترنت همراهت وصل می‌شود ولی سرعت پایینه، بعضی سایت‌ها باز نمی‌شن
            یا بعد از روشن کردن VPN هم هنوز مشکل داری، با تنظیم درست
            <b>نقطه‌دسترسی (APN)</b> معمولاً مشکل حل می‌شه.
          </p>

          <!-- تب‌ها -->
          <div class="apn-tabs" role="tablist" aria-label="APN platforms">
            <button
              class="apn-tab-btn is-active"
              data-apn-tab="android"
              role="tab"
              aria-selected="true"
            >
              اندروید
            </button>
            <button
              class="apn-tab-btn"
              data-apn-tab="ios"
              role="tab"
              aria-selected="false"
            >
              آیفون (iOS)
            </button>
          </div>

          <div class="apn-panes">
            <!-- ANDROID -->
            <div
              class="apn-pane is-active"
              data-apn-pane="android"
            >
              <h3 class="section-title small">تنظیم APN روی اندروید</h3>
              <ol class="help-steps">
                <li>
                  <p class="small">
                    ۱. وارد <b>تنظیمات (Settings)</b> گوشی شو.
                  </p>
                </li>
                <li>
                  <p class="small">
                    ۲. برو به بخش
                    <b>اتصالات / Network &amp; Internet</b>
                    و بعد
                    <b>Mobile Network / شبکه موبایل</b>.
                  </p>
                </li>
                <li>
                  <p class="small">
                    ۳. گزینهٔ
                    <b>Access Point Names (نام‌های نقطه‌دسترسی / APN)</b>
                    رو باز کن.
                  </p>
                </li>
                <li>
                  <p class="small">
                    ۴. بالا روی دکمهٔ
                    <b>افزودن / Add / +</b>
                    بزن تا یک APN جدید بسازی.
                  </p>
                </li>
                <li>
                  <p class="small">
                    ۵. فیلدها رو این‌طوری تنظیم کن:
                  </p>
                  <ul class="small" style="margin-right:1.5rem;list-style:disc;">
                    <li>
                      <b>Name</b> : یک اسم دلخواه، مثلاً
                      <b>Diminet</b>
                    </li>
                    <li>
                      <b>APN</b> :
                      برای بیشتر سیم‌کارت‌ها:
                      <br>
                      ایرانسل:
                      <code dir="ltr" data-keep-en-digits>mtnirancell</code>
                      <br>
                      همراه اول:
                      <code dir="ltr" data-keep-en-digits>mcinet</code>
                      <br>
                      رایتل:
                      <code dir="ltr" data-keep-en-digits>rightel</code>
                      <br>
                      (اگر اپراتورت چیز دیگه‌ای نوشته، طبق همون عمل کن.)
                    </li>
                  </ul>
                  <p class="small">
                    بقیهٔ گزینه‌ها (Proxy, Port, Username, Password و...) رو
                    دست نزن و خالی بذار
                  </p>                </li>
                <li>
                  <p class="small">
                    ۶. از منو روی <b>Save / ذخیره</b> بزن و بعد APN جدید
                    (مثلاً Diminet) رو در لیست انتخاب کن تا فعال بشه.
                  </p>
                </li>
                <li>
                  <p class="small">
                    ۷. یک‌بار
                    <b>حالت هواپیما (Airplane mode)</b>
                    رو روشن و بعد خاموش کن، یا گوشی رو ری‌استارت کن
                    و دوباره اینترنت موبایل و VPN رو تست کن.
                  </p>
                </li>
              </ol>
            </div>

            <!-- iOS -->
            <div
              class="apn-pane"
              data-apn-pane="ios"
            >
              <h3 class="section-title small">تنظیم APN روی آیفون (iOS)</h3>
              <ol class="help-steps">
                <li>
                  <p class="small">
                    ۱. وارد <b>Settings</b> شو.
                  </p>
                </li>
                <li>
                  <p class="small">
                    ۲. به بخش
                    <b>Cellular</b>
                    برو و مطمئن شو
                    <b>Cellular Data</b> روشنه.
                  </p>
                </li>
                <li>
                  <p class="small">
                    ۳. روی
                    <b>Cellular Data Options</b>
                    و بعد
                    <b>Cellular Data Network</b>
                    بزن.
                    (اگر این گزینه رو نمی‌بینی، یعنی تنظیم APN روی اون اپراتور
                    از طرف خود اپراتور قفل شده و قابل تغییر نیست.)
                  </p>
                </li>
                <li>
                  <p class="small">
                    ۴. تو بخش <b>Cellular Data</b> فیلدها رو این‌طور پر کن:
                  </p>
                  <ul class="small" style="margin-right:1.5rem;list-style:disc;">
                    <li>
                      <b>APN</b> :
                      مشابه اندروید، مثلاً
                      <code dir="ltr" data-keep-en-digits>mtnirancell</code>
                      برای ایرانسل،
                      <code dir="ltr" data-keep-en-digits>mcinet</code>
                      برای همراه اول و
                      <code dir="ltr" data-keep-en-digits>rightel</code>
                      برای رایتل.
                    </li>
                    <li>
                      <b>Username / Password</b> :
                      خالی بگذار مگر این‌که اپراتور چیز دیگری گفته باشد.
                    </li>
                  </ul>
                </li>
                <li>
                  <p class="small">
                    ۵. اگر بخش‌های جداگانه‌ای مثل
                    <b>LTE</b>
                    یا
                    <b>Personal Hotspot</b>
                    هم APN دارند، همین مقدار را در آن‌ها هم تکرار کن.
                  </p>
                </li>
                <li>
                  <p class="small">
                    ۶. از تنظیمات خارج شو، یک‌بار
                    <b>Airplane mode</b>
                    را روشن و خاموش کن یا گوشی را ری‌استارت کن
                    و دوباره اینترنت و VPN را تست کن.
                  </p>
                </li>
              </ol>

              <p class="small" style="margin-top:8px;">
                اگر گزینهٔ <b>Cellular Data Network</b> برای سیم‌کارتت وجود ندارد،
                یعنی تنظیمات APN توسط اپراتور کنترل می‌شود و در این حالت بهتر است
                با پشتیبانی اپراتور تماس بگیری یا تنظیمات شبکه را
                از مسیر
                <b>Settings → General → Transfer or Reset → Reset Network Settings</b>
                ریست کنی (توجه: این کار وای‌فای‌های ذخیره‌شده را هم پاک می‌کند).
              </p>
            </div>
          </div>

          <div class="modal__actions" style="margin-top:16px;">
            <button type="button" class="btn btn--primary" data-modal-close>
              بستن
            </button>
          </div>
        </div>
      </div>



    `
    );

    // [15.1] Backdrop click close
    document.querySelectorAll("[data-modal]").forEach((backdrop) => {
      backdrop.addEventListener("click", (e) => {
        if (e.target === backdrop) {
          closeAnyModal();
        }
      });
    });
    
        // تب‌های داخل مدال APN (اندروید / iOS)
    const apnTabButtons = document.querySelectorAll(".apn-tab-btn");
    const apnPanes = document.querySelectorAll(".apn-pane");

    apnTabButtons.forEach((btn) => {
      btn.addEventListener("click", () => {
        const key = btn.getAttribute("data-apn-tab");
        if (!key) return;

        apnTabButtons.forEach((b) => {
          b.classList.remove("is-active");
          b.setAttribute("aria-selected", "false");
        });
        apnPanes.forEach((p) => p.classList.remove("is-active"));

        btn.classList.add("is-active");
        btn.setAttribute("aria-selected", "true");

        const pane = document.querySelector(
          `[data-apn-pane="${key}"]`
        );
        if (pane) {
          pane.classList.add("is-active");
        }
      });
    });


    // [15.2] Buttons with data-modal-close
    document.querySelectorAll("[data-modal-close]").forEach((btn) => {
      btn.addEventListener("click", () => closeAnyModal());
    });

    const btnCp = document.getElementById("cpSubmit");
    if (btnCp) {
      btnCp.addEventListener("click", handleChangePasswordSubmit);
    }

    const openForgotBtn = document.getElementById("openForgotFromLogin");
    if (openForgotBtn) {
      openForgotBtn.addEventListener("click", () => {
        closeAnyModal();
        openModal("forgot");
      });
    }

    const forgotSubmitBtn = document.getElementById("forgotSubmitBtn");
    if (forgotSubmitBtn) {
      forgotSubmitBtn.addEventListener("click", handleForgotSubmit);
    }

    const loginForm = document.getElementById("loginForm");
    if (loginForm) {
      loginForm.addEventListener("submit", handleLoginSubmit);
    }

    const walletBtn = document.getElementById("walletSubmitBtn");
    if (walletBtn) {
      walletBtn.addEventListener("click", handleWalletSubmit);
    }

    const walletSuccessGoPanel = document.getElementById(
      "walletSuccessGoPanel"
    );
    if (walletSuccessGoPanel) {
      walletSuccessGoPanel.addEventListener("click", () => {
        closeAnyModal();
        const url = new URL(window.location.href);
        url.searchParams.set("tab", "panel");
        url.searchParams.delete("show_popup");
        window.location.href = url.toString();
      });
    }

    const refundSubmitBtn = document.getElementById("refundSubmitBtn");
    if (refundSubmitBtn) {
      refundSubmitBtn.addEventListener("click", handleRefundSubmit);
    }
  }

  function openModal(key) {
    const id = modalIds[key];
    if (!id) return;
    const modal = document.getElementById(id);
    if (!modal) return;
    // همیشه قبل از باز کردن، بقیه مودال‌ها را ببند
    closeAnyModal();
    openModalByElement(modal);
  }

  function closeAnyModal() {
    // همه مودال‌های باز را ببند
    document
      .querySelectorAll("[data-modal].is-open")
      .forEach((m) => m.classList.remove("is-open"));
    document.body.classList.remove("has-modal");

    // اگر ویدیو در حال پخش بود، متوقف کن
    const vid = document.getElementById("helpVideo");
    if (vid && !vid.paused) {
      try {
        vid.pause();
      } catch (e) {}
    }

    // ۱) اگر مودال اطلاعیه در صف داریم، اول همونو باز کن
    if (pendingAnnouncementModalId) {
      const modalEl = document.getElementById(pendingAnnouncementModalId);
      if (modalEl) {
        openModalByElement(modalEl);

        const list = Array.isArray(state.announcements)
          ? state.announcements
          : [];
        const latest = list[0];
        if (latest && latest.id) {
          const key = "diminet_announcement_seen_" + latest.id;
          try {
            if (window.localStorage) {
              localStorage.setItem(key, "1");
            }
          } catch (e) {}
        }
      }
      pendingAnnouncementModalId = null;
      return; // اول اطلاعیه، بعد اگر دوباره بست، نوبت بقیه مودال‌هاست
    }

    // ۲) اگر مودال راهنما (push / pwa) در صف داریم، حالا نشانش بده
    if (pendingExtraModalKey) {
      const key = pendingExtraModalKey;
      pendingExtraModalKey = null;

      const id = modalIds[key];
      if (id) {
        const modalEl = document.getElementById(id);
        if (modalEl) {
          openModalByElement(modalEl);
        }
      }
    }
  }
  
    function openModalByElement(modalEl) {
    if (!modalEl) return;
    modalEl.classList.add("is-open");
    document.body.classList.add("has-modal");
  }
  
    // اگر مودالی باز بود، این مودال را در صف نگه می‌دارد تا بعد از بسته شدن نمایش داده شود
  function openModalQueued(key) {
    const id = modalIds[key];
    if (!id) return;
    const modalEl = document.getElementById(id);
    if (!modalEl) return;

    const anyOpenModal = document.querySelector("[data-modal].is-open");
    if (anyOpenModal) {
      // بعداً توی closeAnyModal این رو باز می‌کنیم
      pendingExtraModalKey = key;
      return;
    }

    // اگر الان مودالی باز نیست، مثل معمول باز کن
    openModal(key);
  }



  // [15.3] Payments modal table
  function fillPaymentsModalTable() {
    const tbody = document.getElementById("payments-table-body");
    if (!tbody) return;
    const payments = Array.isArray(state.payments) ? state.payments : [];
    if (payments.length === 0) {
      tbody.innerHTML = `
        <tr>
          <td colspan="5" class="text-muted small">پرداختی ثبت نشده.</td>
        </tr>
      `;
      return;
    }

    tbody.innerHTML = payments
      .map((p) => {
        const statusLabel =
          p.status === "paid"
            ? "موفق"
            : p.status === "failed"
            ? "ناموفق"
            : p.status;
        const statusClass =
          p.status === "paid"
            ? "pay-status pay-status--ok"
            : "pay-status pay-status--bad";

        return `
          <tr>
            <td>${escapeHtml(p.dateFa || "")}</td>
            <td>${escapeHtml(p.planTitle || "")}</td>
            <td>${escapeHtml(p.amountFa || "")}</td>
            <td class="payments-table__ref">${escapeHtml(
              p.refFa || ""
            )}</td>
            <td><span class="${statusClass}">${statusLabel}</span></td>
          </tr>
        `;
      })
      .join("");
  }


  // ============================
  // [X] Offline overlay helpers
  // ============================

  function showOfflineOverlay() {
    if (!offlineOverlay) return;
    if (isOfflineLocked) return;

    isOfflineLocked = true;
    offlineOverlay.classList.add("is-visible");
    document.body.classList.add("is-offline-locked");
  }

  function hideOfflineOverlay() {
    if (!offlineOverlay) return;
    if (!isOfflineLocked) return;

    isOfflineLocked = false;
    offlineOverlay.classList.remove("is-visible");
    document.body.classList.remove("is-offline-locked");
  }


  // ====================================================
  // [16] Change password (panel modal)
  // ====================================================

  async function handleChangePasswordSubmit() {
    const newInput = document.getElementById("cpNew");
    const confirmInput = document.getElementById("cpConfirm");
    const msgBox = document.getElementById("cpMsg");
    if (!newInput || !confirmInput || !msgBox) return;

    const p1 = (newInput.value || "").trim();
    const p2 = (confirmInput.value || "").trim();

    msgBox.style.display = "none";
    msgBox.textContent = "";
    msgBox.className = "modal__alert";

    if (p1.length < 6) {
      msgBox.textContent = "رمز باید حداقل ۶ کاراکتر باشد.";
      msgBox.classList.add("modal__alert--error");
      msgBox.style.display = "block";
      return;
    }
    if (p1 !== p2) {
      msgBox.textContent = "تکرار رمز با رمز جدید یکسان نیست.";
      msgBox.classList.add("modal__alert--error");
      msgBox.style.display = "block";
      return;
    }

    const btn = document.getElementById("cpSubmit");
    if (btn) {
      btn.disabled = true;
      btn.textContent = "در حال ذخیره...";
    }

    try {
      const res = await fetch("/change_password.php", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ new_password: p1 }),
      });

      const data = await res.json().catch(() => ({
        ok: false,
        message: "پاسخ نامعتبر",
      }));

      if (data.ok) {
        msgBox.textContent = "رمز با موفقیت تغییر کرد.";
        msgBox.classList.add("modal__alert--ok");
        msgBox.style.display = "block";
        setTimeout(() => {
          closeAnyModal();
        }, 900);
      } else {
        msgBox.textContent = data.message || "خطا در تغییر رمز.";
        msgBox.classList.add("modal__alert--error");
        msgBox.style.display = "block";
      }
    } catch (err) {
      msgBox.textContent = "ارتباط ناموفق بود.";
      msgBox.classList.add("modal__alert--error");
      msgBox.style.display = "block";
    } finally {
      if (btn) {
        btn.disabled = false;
        btn.textContent = "ذخیره";
      }
    }
  }

  // ====================================================
  // [17] forgot password → forgot.php
  // ====================================================

  async function handleForgotSubmit() {
    const emailInput = document.getElementById("forgotEmail");
    const msgBox = document.getElementById("forgotMsg");
    if (!emailInput || !msgBox) return;

    const email = (emailInput.value || "").trim();

    msgBox.style.display = "none";
    msgBox.textContent = "";
    msgBox.className = "modal__alert";

    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(email)) {
      msgBox.textContent = "لطفاً ایمیل معتبر وارد کن.";
      msgBox.classList.add("modal__alert--error");
      msgBox.style.display = "block";
      return;
    }

    const btn = document.getElementById("forgotSubmitBtn");
    if (btn) {
      btn.disabled = true;
      btn.textContent = "در حال ارسال...";
    }

    try {
      const res = await fetch("/forgot.php", {
        method: "POST",
        headers: {
          "Content-Type":
            "application/x-www-form-urlencoded;charset=UTF-8",
          "X-Requested-With": "XMLHttpRequest",
          Accept: "application/json",
        },
        body: new URLSearchParams({
          email: email,
          ajax: "1",
        }).toString(),
      });

      const data = await res.json().catch(() => null);

      if (
        data &&
        typeof data === "object" &&
        Object.prototype.hasOwnProperty.call(data, "ok")
      ) {
        msgBox.textContent =
          data.message ||
          "اگر ایمیل در سیستم وجود داشته باشد، لینک تغییر رمز ارسال می‌شود.";
        msgBox.classList.add(
          data.ok ? "modal__alert--ok" : "modal__alert--error"
        );
        msgBox.style.display = "block";

        if (data.ok) {
          emailInput.value = "";
        }
      } else {
        msgBox.textContent =
          "درخواست ارسال شد. اگر ایمیل در سیستم وجود داشته باشد، لینک تغییر رمز ارسال می‌شود.";
        msgBox.classList.add("modal__alert--ok");
        msgBox.style.display = "block";
      }
    } catch (err) {
      console.error("forgot error", err);
      msgBox.textContent = "خطا در ارتباط با سرور.";
      msgBox.classList.add("modal__alert--error");
      msgBox.style.display = "block";
    } finally {
      if (btn) {
        btn.disabled = false;
        btn.textContent = "ارسال لینک";
      }
    }
  }

  // ====================================================
  // [18] Wallet charge modal → request.php
  // ====================================================

  function handleWalletSubmit() {
    const amountInput = document.getElementById("walletAmount");
    const msgBox = document.getElementById("walletMsg");
    if (!amountInput || !msgBox) return;

    msgBox.style.display = "none";
    msgBox.textContent = "";
    msgBox.className = "modal__alert";

    const raw = (amountInput.value || "").trim();
    let amount = Number(raw.replace(/[^\d]/g, ""));
    if (!amount || amount < 10000) {
      msgBox.textContent = "حداقل مبلغ شارژ ۱۰,۰۰۰ تومان است.";
      msgBox.classList.add("modal__alert--error");
      msgBox.style.display = "block";
      return;
    }

    if (!state.isLoggedIn || !state.user.email) {
      msgBox.textContent = "برای شارژ کیف پول باید وارد حساب شده باشی.";
      msgBox.classList.add("modal__alert--error");
      msgBox.style.display = "block";
      return;
    }
      const walletBtn = document.getElementById("walletSubmitBtn");
  if (walletBtn && !walletBtn.disabled) {
    walletBtn.disabled = true;
    walletBtn.classList.add("btn--loading");
    walletBtn.dataset.originalText = walletBtn.textContent || "";
    walletBtn.textContent = "در حال انتقال به درگاه...";
  }


    const form = document.createElement("form");
    form.method = "POST";
    form.action = "/request.php";

    const fEmail = document.createElement("input");
    fEmail.type = "hidden";
    fEmail.name = "email";
    fEmail.value = state.user.email;

    const fWalletMode = document.createElement("input");
    fWalletMode.type = "hidden";
    fWalletMode.name = "wallet_mode";
    fWalletMode.value = "1";

    const fWalletAmount = document.createElement("input");
    fWalletAmount.type = "hidden";
    fWalletAmount.name = "wallet_amount_toman";
    fWalletAmount.value = String(amount);

    form.appendChild(fEmail);
    form.appendChild(fWalletMode);
    form.appendChild(fWalletAmount);

    document.body.appendChild(form);
    form.submit();
  }

  // ====================================================
  // [19] Login modal (AJAX)
  // ====================================================

  async function handleLoginSubmit(e) {
    e.preventDefault();

    const form = e.target;
    const emailInput = form.querySelector('input[name="email"]');
    const passInput = form.querySelector('input[name="password"]');
    const msgBox = document.getElementById("loginMsg");
    const submitBtn = document.getElementById("loginSubmitBtn");

    if (!emailInput || !passInput || !msgBox || !submitBtn) return;

    const email = (emailInput.value || "").trim();
    const password = (passInput.value || "").trim();

    msgBox.style.display = "none";
    msgBox.textContent = "";
    msgBox.className = "modal__alert";

    if (!email || !password) {
      msgBox.textContent = "ایمیل و رمز عبور را وارد کنید.";
      msgBox.classList.add("modal__alert--error");
      msgBox.style.display = "block";
      return;
    }

    submitBtn.disabled = true;
    submitBtn.textContent = "در حال ورود...";
    showLoader();

    try {
      const res = await fetch("/login.php", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        credentials: "same-origin",
        body: JSON.stringify({ email, password }),
      });

      const data = await res.json().catch(() => ({
        ok: false,
        message: "پاسخ نامعتبر از سرور.",
      }));

      if (!res.ok || !data.ok) {
        msgBox.textContent = data.message || "ایمیل یا رمز اشتباه است.";
        msgBox.classList.add("modal__alert--error");
        msgBox.style.display = "block";
        return;
      }

      state.isLoggedIn = true;
      state.user.email = data.email || email;
      updateHeaderButton();

      const url = new URL(window.location.href);
      url.searchParams.set("tab", "panel");
      url.searchParams.delete("show_popup");
      window.location.href = url.toString();
      return;
    } catch (err) {
      console.error("login error", err);
      msgBox.textContent = "خطا در ارتباط با سرور.";
      msgBox.classList.add("modal__alert--error");
      msgBox.style.display = "block";
    } finally {
      hideLoader();
      submitBtn.disabled = false;
      submitBtn.textContent = "ورود";
    }
  }

  // ====================================================
  // [20] Help video modal
  // ====================================================

  function openVideoModal(platform) {
    const video = document.getElementById("helpVideo");
    if (!video) return;

    let src = "/assets/guide.mp4";
    if (platform === "android") src = "/assets/guide.mp4";
    else if (platform === "ios") src = "/assets/guide.mp4";

    if (video.getAttribute("src") !== src) {
      video.setAttribute("src", src);
      video.load();
    }

    openModal("video");

    video
      .play()
      .catch(() => {
        // ignore autoplay errors
      });
  }

  // ====================================================
  // [21] Auto popup from boot / URL
  // ====================================================

  function handleAutoPopupFromURL() {
    if (boot.popup === "success") {
      openModal("success");
      return;
    }
    if (boot.popup === "failed") {
      openModal("failed");
      return;
    }
    if (boot.popup === "wallet_ok") {
      openModal("wallet_success");
      return;
    }

    const params = new URLSearchParams(location.search);
    const type = params.get("show_popup");
    if (type === "success") {
      openModal("success");
    } else if (type === "failed") {
      openModal("failed");
    } else if (type === "wallet_ok") {
      openModal("wallet_success");
    }
  }

  // ====================================================
  // [22] PWA: SW register + install button
  // ====================================================

function setupPwaInstallButton() {
  const btn = document.getElementById("btn-pwa-install");
  if (!btn) return;

  // دیگه لازم نیست غیرفعال باشه
  btn.disabled = false;
  btn.classList.remove("btn--disabled");

  btn.addEventListener("click", () => {
    alert(
      "برای نصب دیمینت، از پیغام نصب بالای مرورگر یا گزینهٔ Install App / Add to Home Screen در منو استفاده کن."
    );
  });
}

  function initPwaAndPush() {
    if ("serviceWorker" in navigator) {
      navigator.serviceWorker
        .register("/service-worker.js")
        .then((reg) => {
          console.log("service worker registered", reg.scope);
          swRegistration = reg;   // رجیستریشن رو نگه می‌داریم

          // اگر قبلاً ساب پوش داشته، با سرور sync کن
          initPush(reg);

          // به محض آماده شدن SW، اگر نوتیف فعال نیست از مرورگر بخواه مجوز بپرسد
          autoInitPushOnLoad();
        })
        .catch((err) => {
          console.warn("service worker register failed", err);
        });
    }

window.addEventListener("beforeinstallprompt", (e) => {

  // ✔ اگر یوزر لاگین هست، فقط یه مودال راهنما براش صف کن
  if (state.isLoggedIn) {
    openModalQueued("pwa_guide");
  }

  // دیگه نه prompt اینجا صدا می‌زنیم، نه deferredPwaPrompt لازم داریم
});
  }
  
  
    // ============================
  // [P] Enable Push Notifications (UI flow)
  // ============================
  async function enablePushNotifications() {
    if (!("Notification" in window) || !("serviceWorker" in navigator)) {
      alert("مرورگر شما از اعلان‌ها پشتیبانی نمی‌کند.");
      return;
    }

    if (!swRegistration) {
      alert("سرویس‌ورکر هنوز آماده نیست. یکبار صفحه را رفرش کن.");
      return;
    }

    // اگر قبلاً کاربر بلاک کرده
    if (Notification.permission === "denied") {
      alert(
        "دسترسی اعلان‌ها قبلاً مسدود شده. از تنظیمات مرورگر اجازه اعلان را برای سایت فعال کن."
      );
      return;
    }

    // اگه هنوز نپرسیدیم، الان بپرس
    if (Notification.permission === "default") {
      try {
        const perm = await Notification.requestPermission();
        if (perm !== "granted") {
          alert("بدون اجازه اعلان، امکان ارسال پوش وجود ندارد.");
          return;
        }
      } catch (e) {
        console.warn("Notification.requestPermission error:", e);
        alert("نتوانستیم اجازه اعلان بگیریم.");
        return;
      }
    }

    try {
      // اگر اینجا رسیدیم یعنی permission = granted
      let subscription = await swRegistration.pushManager.getSubscription();
      if (!subscription) {
        // نیاز به VAPID public key از سرور
        const vapidRes = await fetch("/push_config.php", {
          method: "GET",
          credentials: "same-origin",
        });

        if (!vapidRes.ok) {
          alert("خطا در دریافت تنظیمات پوش از سرور.");
          return;
        }

        const vapidData = await vapidRes.json().catch(() => null);
        if (!vapidData || !vapidData.ok || !vapidData.vapid_public_key) {
          alert("کلید پوش روی سرور تنظیم نشده است.");
          return;
        }

        const applicationServerKey = urlBase64ToUint8Array(
          vapidData.vapid_public_key
        );

        subscription = await swRegistration.pushManager.subscribe({
          userVisibleOnly: true,
          applicationServerKey,
        });
      }

      // سابسکریپشن را برای سرور بفرست
      await fetch("/push_subscribe.php", {
        method: "POST",
        credentials: "same-origin",
        headers: {
          "Content-Type": "application/json",
          "X-Requested-With": "XMLHttpRequest",
        },
        body: JSON.stringify(subscription),
      });

      alert("اعلان‌های دیمینت برای این مرورگر فعال شد ✅");
    } catch (err) {
      console.error("enablePushNotifications error:", err);
      alert("خطا در فعال‌سازی اعلان‌ها. دوباره تلاش کن.");
    }
  }

  // helper: base64 VAPID → UInt8Array
  function urlBase64ToUint8Array(base64String) {
    const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
    const base64 = (base64String + padding)
      .replace(/-/g, "+")
      .replace(/_/g, "/");

    const rawData = window.atob(base64);
    const outputArray = new Uint8Array(rawData.length);

    for (let i = 0; i < rawData.length; ++i) {
      outputArray[i] = rawData.charCodeAt(i);
    }
    return outputArray;
  }
  
  
  // به محض لود صفحه، اگر نوتیف فعال نیست، از مرورگر بخواه مجوز بپرسد
function autoInitPushOnLoad() {
  if (!("Notification" in window)) return;
  if (!swRegistration) return;

  // اگر قبلاً اجازه داده → فقط برای لاگین‌ها ساب رو sync کن
  if (Notification.permission === "granted") {
    if (state.isLoggedIn) {
      autoSubscribeToPushSilently();
    }
    return;
  }

  // اگر کاربر قبلاً بلاک کرده
  if (Notification.permission === "denied") {
    // فقط برای لاگین‌ها مودال راهنما را نشان بده
    if (state.isLoggedIn) {
      openModalQueued("push_guide");
    }
    return;
  }

  // حالت default → اولین بار / هنوز نپرسیده
  try {
    Notification.requestPermission().then((result) => {
      // بعد از نمایش پرمیژن، فقط برای لاگین‌ها مودال راهنما را نشان بده
      if (state.isLoggedIn) {
        openModalQueued("push_guide");
      }

      if (result === "granted" && state.isLoggedIn) {
        // سابسکریپشن silent فقط برای کاربر لاگین شده
        autoSubscribeToPushSilently();
      }
    });
  } catch (err) {
    console.warn("autoInitPushOnLoad error:", err);
  }
}

  async function autoSubscribeToPushSilently() {
    if (!swRegistration || !swRegistration.pushManager) return;
    

    try {
      let subscription = await swRegistration.pushManager.getSubscription();

      if (!subscription) {
        const vapidRes = await fetch("/push_config.php", {
          method: "GET",
          credentials: "same-origin",
        });
        if (!vapidRes.ok) return;

        const vapidData = await vapidRes.json().catch(() => null);
        if (!vapidData || !vapidData.ok || !vapidData.vapid_public_key) {
          return;
        }

        const applicationServerKey = urlBase64ToUint8Array(
          vapidData.vapid_public_key
        );

        subscription = await swRegistration.pushManager.subscribe({
          userVisibleOnly: true,
          applicationServerKey,
        });
      }

      await fetch("/push_subscribe.php", {
        method: "POST",
        credentials: "same-origin",
        headers: {
          "Content-Type": "application/json",
          "X-Requested-With": "XMLHttpRequest",
        },
        body: JSON.stringify(subscription),
      });
    } catch (err) {
      console.warn("autoSubscribeToPushSilently error:", err);
    }
  }

  
    // ============================
  // [Y] Connectivity check (ping)
  // ============================

  async function checkConnectivity() {
    // اگر خود مرورگر بگه offline ـی، بدون بحث لاک کن
    if (typeof navigator !== "undefined" && navigator.onLine === false) {
      showOfflineOverlay();
      return;
    }

    try {
      const res = await fetch("/ping.php?ts=" + Date.now(), {
        method: "GET",
        cache: "no-store",
      });

      if (!res.ok) {
        showOfflineOverlay();
        return;
      }

      const data = await res.json().catch(() => null);

      if (!data || data.ok !== true) {
        showOfflineOverlay();
        return;
      }

      // همه‌چی اوکیه → لاک رو بردار
      hideOfflineOverlay();
    } catch (err) {
      // هر خطای شبکه‌ای = قطع بودن
      console.warn("connectivity check error:", err);
      showOfflineOverlay();
    }
  }


  // [22.1] Push init → push_subscribe.php
  async function initPush(registration) {
    try {
      if (!registration || !registration.pushManager) {
        console.log("PushManager not available on registration.");
        return;
      }

      if (!("Notification" in window)) {
        console.log("Notification API not supported.");
        return;
      }

      // فقط اگر کاربر قبلاً اجازه داده و سابسکریپشن دارد، ارسال می‌کنیم
      if (Notification.permission !== "granted") {
        console.log(
          "Push not enabled. Permission is:",
          Notification.permission
        );
        return;
      }

      const sub = await registration.pushManager.getSubscription();
      if (!sub) {
        console.log("No existing push subscription to sync.");
        return;
      }

      await fetch("/push_subscribe.php", {
        method: "POST",
        credentials: "same-origin",
        headers: {
          "Content-Type": "application/json",
          "X-Requested-With": "XMLHttpRequest",
        },
        body: JSON.stringify(sub),
      });

      console.log("Push subscription synced with server.");
    } catch (err) {
      console.warn("initPush error:", err);
    }
  }
  


});
