/* =========================
   PCMOD Plus Reels — reels.js (Unified + Fixed)

   ✅ Autoplay (muted) مثل اینستاگرام
   ✅ Unlock با اولین تعامل کاربر (iOS/Chrome policy)
   ✅ Double tap/click = Like + قلب
   ✅ Share: لینک خود Reel روی reels.html?r=slug (feed ادامه‌دار)
   ✅ Topic Follow (لوکال): نام تاپیک + ذخیره واقعی
   ✅ Drawer کامنت: جلوگیری از blur بلاک‌کننده‌ی تایپ
   ✅ Unified feed: reads from ./data.json (single source of truth)
   ✅ Thumbnails: https://media.pcmod.ir/thumbs/<slug>.png
   ========================= */

const DATA_JSON_URL = "./data.json";
const MEDIA_BASE = "https://media.pcmod.ir/shorts";
const THUMBS_BASE = "https://media.pcmod.ir/thumbs";

const slidesEl = document.getElementById("slides");
const toastEl  = document.getElementById("toast");

const drawer = document.getElementById("drawer");
const drawerClose = document.getElementById("drawerClose");
const drawerBackdrop = document.getElementById("drawerBackdrop");
const commentsList = document.getElementById("commentsList");
const commentInput = document.getElementById("commentInput");
const commentSendBtn = document.getElementById("commentSendBtn");

const state = new Map();

let activeIndex = 0;
let swiperRef = null;

// autoplay policies:
let userActivated = false; // first user gesture happened
let soundOn = false;       // user chose unmute

// unified (filled from data.json)
let TOPIC_TITLES = {};
let DATA = [];

// topic-follow storage
const TOPIC_KEY = "pcmod_followed_topics";

/* -------------------------
   Utils
------------------------- */
function toast(msg){
  if(!toastEl) return;
  toastEl.textContent = msg;
  toastEl.classList.add("show");
  clearTimeout(toastEl._t);
  toastEl._t = setTimeout(()=>toastEl.classList.remove("show"), 900);
}

function isIOS(){
  const ua = navigator.userAgent || "";
  return /iPad|iPhone|iPod/.test(ua) || (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
}
function canNativeHLS(v){
  return v.canPlayType("application/vnd.apple.mpegurl") !== "";
}
function urlOf(slug){
  return `${MEDIA_BASE}/${slug}/master.m3u8`;
}
function thumbOf(slug){
  return `${THUMBS_BASE}/${slug}.png`;
}
function escapeHtml(s){
  return String(s).replace(/[&<>"']/g, (m)=>( {
    "&":"&amp;","<":"&lt;",">":"&gt;","\"":"&quot;","'":"&#039;"
  }[m]));
}
function storageKey(type, slug){ return `pcmod_${type}_${slug}`; }
function getBool(type, slug){ return localStorage.getItem(storageKey(type, slug)) === "1"; }
function setBool(type, slug, val){ localStorage.setItem(storageKey(type, slug), val ? "1" : "0"); }

function topicTitle(topic){
  return TOPIC_TITLES[topic] || topic || "PCMOD";
}

function getFollowedTopics(){
  try{
    const arr = JSON.parse(localStorage.getItem(TOPIC_KEY) || "[]");
    return Array.isArray(arr) ? arr : [];
  }catch{ return []; }
}
function setFollowedTopics(arr){
  localStorage.setItem(TOPIC_KEY, JSON.stringify([...new Set(arr)]));
}
function isTopicFollowed(topic){
  return getFollowedTopics().includes(topic);
}
function toggleFollowTopic(topic){
  const list = getFollowedTopics();
  const has = list.includes(topic);
  const next = has ? list.filter(x=>x!==topic) : [...list, topic];
  setFollowedTopics(next);
  return !has;
}

function activeSlug(){
  const s = state.get(activeIndex);
  return s?.slug || DATA[activeIndex]?.slug;
}

/* -------------------------
   Unified data loader
------------------------- */
async function loadDataJson(){
  const res = await fetch(`${DATA_JSON_URL}?ts=${Date.now()}`, { cache: "no-store" });
  if(!res.ok) throw new Error("data.json load failed");
  const json = await res.json();

  TOPIC_TITLES = {};
  (json.categories || []).forEach(c => {
    if(c?.id) TOPIC_TITLES[c.id] = c.title || c.id;
  });

  DATA = (json.items || [])
    .filter(it => it?.slug)
    .map(it => ({
      slug: it.slug,
      topic: it.cat || "pcmod",
      title: it.title || it.slug,
      desc: it.desc || it.title || it.slug,
      audio: "PCMOD • Original audio",
      thumb: thumbOf(it.slug)
    }));
}

/* -------------------------
   Deep-linking
------------------------- */
function buildReelLink(slug){
  const url = new URL(window.location.href);
  url.searchParams.set("r", slug);
  return url.toString();
}
function setReelUrl(slug){
  try{
    const url = new URL(window.location.href);
    url.searchParams.set("r", slug);
    history.replaceState({}, "", url.toString());
  }catch{}
}
function getInitialIndexFromUrl(){
  try{
    const url = new URL(window.location.href);
    const slug = url.searchParams.get("r");
    if(!slug) return 0;
    const idx = DATA.findIndex(x => x.slug === slug);
    return idx >= 0 ? idx : 0;
  }catch{ return 0; }
}

/* -------------------------
   Sound
------------------------- */
function applySoundStateToVideo(video){
  if(!video) return;
  video.muted = !soundOn;
  if(soundOn){
    try{ video.volume = 1.0; }catch{}
  }
}
function updateSoundIcon(s){
  if(!s?.soundBtn) return;
  s.soundBtn.innerHTML = soundOn
    ? `<i class="fa-solid fa-volume-high"></i>`
    : `<i class="fa-solid fa-volume-xmark"></i>`;
}
function toggleSound(){
  userActivated = true;
  soundOn = !soundOn;
  const s = state.get(activeIndex);
  if(s?.video){
    applySoundStateToVideo(s.video);
    updateSoundIcon(s);
    try{ s.video.play(); }catch{}
  }
  toast(soundOn ? "Sound on 🔊" : "Muted 🔇");
}

/* -------------------------
   Drawer (comments)
------------------------- */
function openDrawer(){
  if(!drawer) return;
  drawer.classList.add("open");
  setTimeout(()=>{
    if(commentInput){
      commentInput.focus({ preventScroll:true });
    }
  }, 80);
}
function closeDrawer(){
  drawer?.classList.remove("open");
}

// close actions
drawerClose?.addEventListener("click", (e)=>{ e.stopPropagation(); closeDrawer(); });
drawerBackdrop?.addEventListener("click", (e)=>{ e.stopPropagation(); closeDrawer(); });
// prevent click bubbling inside sheet
drawer?.querySelector(".drawerSheet")?.addEventListener("click", (e)=> e.stopPropagation());

/* -------------------------
   Comments local demo
------------------------- */
function loadComments(slug){
  const key = `pcmod_comments_${slug}`;
  try{
    const arr = JSON.parse(localStorage.getItem(key) || "[]");
    return Array.isArray(arr) ? arr : [];
  }catch{ return []; }
}
function saveComments(slug, arr){
  const key = `pcmod_comments_${slug}`;
  localStorage.setItem(key, JSON.stringify(arr));
}
function renderComments(slug){
  const arr = loadComments(slug);
  const base = [{u:"pcmod", t:"این بخش فعلاً لوکاله 😄 بعداً وصلش می‌کنیم به وردپرس."}].concat(arr);
  if(!commentsList) return;
  commentsList.innerHTML = base.map(c => `
    <div class="comment">
      <div class="cAvatar"></div>
      <div class="cBody">
        <div class="cName">${escapeHtml(c.u)}</div>
        <div class="cText">${escapeHtml(c.t)}</div>
      </div>
    </div>
  `).join("");
}

/* -------------------------
   Like helpers
------------------------- */
function heartBurstAt(videoEl){
  if(!videoEl) return;
  const heart = document.createElement("div");
  heart.className = "tapHeart";
  heart.innerHTML = `<i class="fa-solid fa-heart"></i>`;
  videoEl.parentElement?.appendChild(heart);
  requestAnimationFrame(()=>heart.classList.add("show"));
  setTimeout(()=>heart.remove(), 650);
}

function setLikeUI(s, liked){
  if(!s?.likeBtn || !s?.likeText) return;
  const baseCount = s.likeBaseCount || 0;
  s.likeBtn.classList.toggle("liked", liked);
  s.likeBtn.innerHTML = `<i class="fa-${liked ? "solid" : "regular"} fa-heart"></i>`;
  s.likeText.textContent = String(baseCount + (liked ? 1 : 0));
}

function likeReel(slug, forceLike, idx){
  const cur = getBool("like", slug);
  const next = forceLike ? true : !cur;
  setBool("like", slug, next);

  const s = state.get(idx);
  if(s){
    setLikeUI(s, next);
    if(next && forceLike && s.video) heartBurstAt(s.video);
  }
}

/* -------------------------
   Slide template
------------------------- */
function slideTemplate(item, index){
  const url = urlOf(item.slug);
  const topic = item.topic || "pcmod";

  const slide = document.createElement("div");
  slide.className = "swiper-slide";

  const video = document.createElement("video");
  video.className = "video";
  video.setAttribute("playsinline","");
  video.setAttribute("webkit-playsinline","");
  video.setAttribute("crossorigin","anonymous");
  video.muted = true;
  video.loop = true;
  video.preload = "none";
  video.controls = false;

  // ✅ poster thumbnail
  video.poster = item.thumb || "";

  // overlays
  const overlayTop = document.createElement("div");
  overlayTop.className = "overlayTop";
  const overlayBottom = document.createElement("div");
  overlayBottom.className = "overlayBottom";

  // progress
  const progress = document.createElement("div");
  progress.className = "progress";
  const bar = document.createElement("div");
  progress.appendChild(bar);

  // sound button
  const soundBtn = document.createElement("button");
  soundBtn.type = "button";
  soundBtn.className = "soundBtn";
  soundBtn.innerHTML = `<i class="fa-solid fa-volume-xmark"></i>`;
  soundBtn.style.cssText = "position:absolute;left:12px;top:12px;z-index:30;width:44px;height:44px;border-radius:16px;border:1px solid rgba(255,255,255,.18);background:rgba(0,0,0,.25);color:#fff;backdrop-filter:blur(10px)";
  soundBtn.addEventListener("click", (e)=>{ e.stopPropagation(); toggleSound(); });

  // meta
  const meta = document.createElement("div");
  meta.className = "meta";
  meta.innerHTML = `
    <div class="userRow">
      <div class="userAvatar"></div>
      <div class="username">${escapeHtml(topicTitle(topic))}</div>
      <button class="followBtn topicFollowBtn" type="button" data-topic="${escapeHtml(topic)}"></button>
    </div>

    <div class="caption" data-collapsed="1">
      ${escapeHtml(item.desc)} <span class="moreBtn">more</span>
    </div>

    <div class="audioRow">
      <i class="fa-solid fa-music"></i>
      <div>${escapeHtml(item.audio || "Original audio")}</div>
    </div>
  `;

  // actions
  const actions = document.createElement("div");
  actions.className = "actions";

  const likeBase = 128 + index * 41;
  const commentCount = 12 + index * 4;
  const shareCount = 7 + index * 2;

  const isLiked = getBool("like", item.slug);
  const isSaved = getBool("save", item.slug);

  // like
  const likeGroup = document.createElement("div");
  likeGroup.className = "actionGroup";
  const likeBtn = document.createElement("button");
  likeBtn.className = "actionBtn" + (isLiked ? " liked" : "");
  likeBtn.type = "button";
  likeBtn.innerHTML = `<i class="fa-${isLiked ? "solid" : "regular"} fa-heart"></i>`;
  const likeText = document.createElement("div");
  likeText.className = "actionCount";
  likeText.textContent = String(likeBase + (isLiked ? 1 : 0));
  likeGroup.appendChild(likeBtn);
  likeGroup.appendChild(likeText);

  // comment
  const cGroup = document.createElement("div");
  cGroup.className = "actionGroup";
  const cBtn = document.createElement("button");
  cBtn.className = "actionBtn";
  cBtn.type = "button";
  cBtn.innerHTML = `<i class="fa-regular fa-comment"></i>`;
  const cText = document.createElement("div");
  cText.className = "actionCount";
  cText.textContent = String(commentCount);
  cGroup.appendChild(cBtn);
  cGroup.appendChild(cText);

  // share
  const sGroup = document.createElement("div");
  sGroup.className = "actionGroup";
  const shareBtn = document.createElement("button");
  shareBtn.className = "actionBtn";
  shareBtn.type = "button";
  shareBtn.innerHTML = `<i class="fa-solid fa-paper-plane"></i>`;
  const sText = document.createElement("div");
  sText.className = "actionCount";
  sText.textContent = String(shareCount);
  sGroup.appendChild(shareBtn);
  sGroup.appendChild(sText);

  // save
  const saveGroup = document.createElement("div");
  saveGroup.className = "actionGroup";
  const saveBtn = document.createElement("button");
  saveBtn.className = "actionBtn" + (isSaved ? " saved" : "");
  saveBtn.type = "button";
  saveBtn.innerHTML = `<i class="fa-${isSaved ? "solid" : "regular"} fa-bookmark"></i>`;
  const saveText = document.createElement("div");
  saveText.className = "actionCount";
  saveText.textContent = isSaved ? "Saved" : "Save";
  saveGroup.appendChild(saveBtn);
  saveGroup.appendChild(saveText);

  // more
  const moreGroup = document.createElement("div");
  moreGroup.className = "actionGroup";
  const moreBtn = document.createElement("button");
  moreBtn.className = "actionBtn";
  moreBtn.type = "button";
  moreBtn.innerHTML = `<i class="fa-solid fa-ellipsis"></i>`;
  moreGroup.appendChild(moreBtn);

  const musicDisc = document.createElement("div");
  musicDisc.className = "musicDisc";

  actions.appendChild(likeGroup);
  actions.appendChild(cGroup);
  actions.appendChild(sGroup);
  actions.appendChild(saveGroup);
  actions.appendChild(moreGroup);
  actions.appendChild(musicDisc);

  // ------- Interactions -------
  async function togglePlay(){
    userActivated = true;
    try{
      if(video.paused) await video.play();
      else video.pause();
    }catch{
      toast("برای شروع پخش یک بار تعامل کن.");
    }
  }

  video.addEventListener("click", (e)=>{ e.preventDefault(); togglePlay(); });

  // double tap/click -> like
  let lastTap = 0;
  video.addEventListener("pointerup", (e)=>{
    if(e.pointerType === "mouse" && e.button !== 0) return;
    const now = Date.now();
    if(now - lastTap < 260){
      likeReel(item.slug, true, index);
      lastTap = 0;
    }else{
      lastTap = now;
    }
  }, { passive:true });

  // caption more/less
  const capEl = meta.querySelector(".caption");
  const moreEl = meta.querySelector(".moreBtn");
  moreEl?.addEventListener("click", (e)=>{
    e.stopPropagation();
    const collapsed = capEl.getAttribute("data-collapsed") === "1";
    capEl.setAttribute("data-collapsed", collapsed ? "0" : "1");
    capEl.style.webkitLineClamp = collapsed ? "999" : "2";
    moreEl.textContent = collapsed ? "less" : "more";
  });

  // topic follow button
  const tBtn = meta.querySelector(".topicFollowBtn");
  function refreshTopicBtn(){
    const followed = isTopicFollowed(topic);
    tBtn.textContent = followed ? "فالو شده ✓" : "فالو تاپیک";
  }
  refreshTopicBtn();

  tBtn?.addEventListener("click", (e)=>{
    e.stopPropagation();
    const nowFollowing = toggleFollowTopic(topic);
    refreshTopicBtn();
    toast(nowFollowing ? `تاپیک «${topicTitle(topic)}» فالو شد ✅` : `تاپیک حذف شد`);
  });

  // like button manual
  likeBtn.addEventListener("click", (e)=>{
    e.stopPropagation();
    likeReel(item.slug, false, index);
  });

  // comment
  cBtn.addEventListener("click", (e)=>{
    e.stopPropagation();
    renderComments(item.slug);
    openDrawer();
  });

  // save
  saveBtn.addEventListener("click", (e)=>{
    e.stopPropagation();
    userActivated = true;
    const cur = getBool("save", item.slug);
    const next = !cur;
    setBool("save", item.slug, next);
    saveBtn.classList.toggle("saved", next);
    saveBtn.innerHTML = `<i class="fa-${next ? "solid" : "regular"} fa-bookmark"></i>`;
    saveText.textContent = next ? "Saved" : "Save";
    toast(next ? "Saved ✅" : "Unsaved");
  });

  // share (deep-link)
  shareBtn.addEventListener("click", async (e)=>{
    e.stopPropagation();
    userActivated = true;
    const shareUrl = buildReelLink(item.slug);
    try{
      if(navigator.share){
        await navigator.share({ title: "PCMOD Plus Reels", text: item.desc, url: shareUrl });
      }else{
        await navigator.clipboard.writeText(shareUrl);
        toast("لینک کپی شد ✅");
      }
    }catch{}
  });

  moreBtn.addEventListener("click", (e)=>{ e.stopPropagation(); toast("More menu (مرحله بعد)"); });

  // progress
  video.addEventListener("timeupdate", ()=>{
    if(!video.duration || !isFinite(video.duration)) return;
    const p = (video.currentTime / video.duration) * 100;
    bar.style.width = `${Math.min(100, Math.max(0, p))}%`;
  });
  video.addEventListener("loadedmetadata", ()=>{ bar.style.width = "0%"; });

  // assemble
  slide.appendChild(video);
  slide.appendChild(soundBtn);
  slide.appendChild(overlayTop);
  slide.appendChild(overlayBottom);
  slide.appendChild(meta);
  slide.appendChild(actions);
  slide.appendChild(progress);

  state.set(index, {
    video,
    url,
    hls: null,
    attached: false,
    slug: item.slug,
    soundBtn,
    likeBtn,
    likeText,
    likeBaseCount: likeBase
  });

  return slide;
}

/* -------------------------
   HLS attach/detach
------------------------- */
function attach(index){
  const s = state.get(index);
  if(!s || s.attached) return;

  const { video, url } = s;

  applySoundStateToVideo(video);
  updateSoundIcon(s);

  if(isIOS() || canNativeHLS(video)){
    video.src = url;
    s.attached = true;
    return;
  }

  if(window.Hls && window.Hls.isSupported()){
    const hls = new window.Hls({
      enableWorker:true,
      backBufferLength:30,
      maxBufferLength:20,
      maxMaxBufferLength:40,
      startLevel:0
    });

    hls.loadSource(url);
    hls.attachMedia(video);

    hls.on(window.Hls.Events.ERROR, (_, data)=>{
      if(data && data.fatal){
        toast("HLS error (MIME/CORS را چک کن).");
        try{ hls.destroy(); }catch{}
        s.hls = null;
        s.attached = false;
      }
    });

    s.hls = hls;
    s.attached = true;
    return;
  }

  toast("Browser does not support HLS");
}

function detach(index){
  const s = state.get(index);
  if(!s) return;

  try{ s.video.pause(); }catch{}
  try{ s.video.removeAttribute("src"); s.video.load(); }catch{}

  if(s.hls){
    try{ s.hls.destroy(); }catch{}
    s.hls = null;
  }

  s.attached = false;
}

async function playIndex(index){
  const s = state.get(index);
  if(!s) return;

  applySoundStateToVideo(s.video);
  updateSoundIcon(s);

  try{
    if(!soundOn) s.video.muted = true;
    await s.video.play();
  }catch{
    // blocked until first gesture
  }
}

function pauseAllExcept(index){
  for(const [i, s] of state.entries()){
    if(i !== index){
      try{ s.video.pause(); }catch{}
    }
  }
}

function manageBuffers(index){
  [index, index-1, index+1].forEach(i=>{
    if(i>=0 && i<DATA.length) attach(i);
  });
  for(const [i] of state.entries()){
    if(Math.abs(i - index) >= 3) detach(i);
  }
}

/* -------------------------
   Keyboard controls
------------------------- */
function initKeyboard(){
  window.addEventListener("keydown", async (e)=>{
    const ae = document.activeElement;
    const tag = (ae?.tagName || "").toLowerCase();
    const typing = (tag === "input" || tag === "textarea" || ae?.isContentEditable);
    if(typing) return;
    if(drawer?.classList.contains("open")) return;

    const key = e.key;

    if(key === "ArrowDown" || key === "ArrowRight" || key === "PageDown"){
      e.preventDefault();
      swiperRef?.slideNext();
      return;
    }
    if(key === "ArrowUp" || key === "ArrowLeft" || key === "PageUp"){
      e.preventDefault();
      swiperRef?.slidePrev();
      return;
    }

    if(key === " " || key === "k" || key === "K"){
      e.preventDefault();
      userActivated = true;
      const s = state.get(activeIndex);
      if(!s) return;
      try{
        if(s.video.paused) await s.video.play();
        else s.video.pause();
      }catch{}
      return;
    }

    if(key === "m" || key === "M"){
      e.preventDefault();
      toggleSound();
      return;
    }
  });
}

/* -------------------------
   First gesture unlock
------------------------- */
function initGestureUnlock(){
  const unlock = async ()=>{
    userActivated = true;
    await playIndex(activeIndex);
  };

  window.addEventListener("pointerdown", unlock, { once:true, capture:true });
  window.addEventListener("touchstart", unlock, { once:true, passive:true, capture:true });
  window.addEventListener("keydown", unlock, { once:true, capture:true });
  window.addEventListener("wheel", unlock, { once:true, passive:true, capture:true });
}

/* -------------------------
   Comment send bind once
------------------------- */
function initCommentSend(){
  if(!commentSendBtn || !commentInput) return;

  function sendComment(){
    const slug = activeSlug();
    const t = (commentInput.value || "").trim();
    if(!t) return;

    const arr = loadComments(slug);
    arr.unshift({u:"you", t});
    saveComments(slug, arr);

    commentInput.value = "";
    renderComments(slug);
  }

  if(commentSendBtn._bound) return;
  commentSendBtn._bound = true;

  commentSendBtn.addEventListener("click", (e)=>{ e.stopPropagation(); sendComment(); });
  commentInput.addEventListener("keydown", (e)=>{
    if(e.key === "Enter"){ e.preventDefault(); sendComment(); }
  });
}

/* -------------------------
   Feed reorder by followed topics (followed first)
------------------------- */
function reorderByFollowedTopics(){
  const followed = new Set(getFollowedTopics());
  const arr = DATA.map((x,i)=>({x,i}));
  arr.sort((a,b)=>{
    const aw = followed.has(a.x.topic || "pcmod") ? 1 : 0;
    const bw = followed.has(b.x.topic || "pcmod") ? 1 : 0;
    if(bw !== aw) return bw - aw;
    return a.i - b.i;
  });
  DATA.length = 0;
  arr.forEach(o => DATA.push(o.x));
}

/* -------------------------
   Init
------------------------- */
function init(){
  if(!slidesEl){
    console.warn("slides element not found");
    return;
  }

  if(DATA.length === 0){
    slidesEl.innerHTML = `<div class="swiper-slide" style="display:flex;align-items:center;justify-content:center;padding:24px;text-align:center">
      <div style="max-width:420px;line-height:1.9;color:rgba(255,255,255,.85)">
        <div style="font-weight:900;font-size:16px;margin-bottom:10px">هیچ ویدئویی پیدا نشد</div>
        <div style="font-size:13px">فایل data.json را بررسی کن و مطمئن شو slugها با فولدرهای /shorts یکی هستند.</div>
      </div>
    </div>`;
    return;
  }

  reorderByFollowedTopics();

  DATA.forEach((item, i)=> slidesEl.appendChild(slideTemplate(item, i)));

  const initial = getInitialIndexFromUrl();

  swiperRef = new Swiper(".reels", {
    direction: "vertical",
    slidesPerView: 1,
    spaceBetween: 0,
    speed: 260,
    resistanceRatio: 0.85,
    longSwipesRatio: 0.15,
    threshold: 10,
    touchReleaseOnEdges: true,
    shortSwipes: true,
    initialSlide: initial
  });

  activeIndex = initial;

  manageBuffers(activeIndex);
  pauseAllExcept(activeIndex);
  setReelUrl(DATA[activeIndex]?.slug || "");
  playIndex(activeIndex);

  swiperRef.on("slideChange", async ()=>{
    activeIndex = swiperRef.activeIndex;

    pauseAllExcept(activeIndex);
    manageBuffers(activeIndex);

    const slug = DATA[activeIndex]?.slug;
    if(slug) setReelUrl(slug);

    await playIndex(activeIndex);
  });

  document.addEventListener("visibilitychange", ()=>{
    if(document.hidden){
      const s = state.get(activeIndex);
      if(s){ try{ s.video.pause(); }catch{} }
    }else{
      playIndex(activeIndex);
    }
  });

  initKeyboard();
  initGestureUnlock();
  initCommentSend();

  setTimeout(()=>toast("برای صدا روی 🔊 یا کلید M بزن"), 600);
}

/* ---- Boot (load data.json then init) ---- */
(async function boot(){
  try{
    await loadDataJson();
  }catch(e){
    console.error(e);
    DATA = [];
  }
  init();
})();
