(() => {
  // v223: CDP remains schedule-only; OS mouse-idle gating is retired.

  // IGblue DEBUG v70: set IG_DBG_ON=0 or put `return;` at top of dbg() to disable all logs.
  const IG_DBG_ON = 1;
  function dbg(fn, loc, line, value) {
    // return; // DEBUG OFF: uncomment this one line later.
    if (!IG_DBG_ON) return;
    try {
      const clip = (v) => {
        const seen = new WeakSet();
        let s = JSON.stringify(v, (k, val) => {
          if (typeof val === "string") return val.length > 500 ? val.slice(0, 500) + `...<${val.length}>` : val;
          if (val && typeof val === "object") { if (seen.has(val)) return "[Circular]"; seen.add(val); }
          return val;
        });
        if (s && s.length > 2200) s = s.slice(0,2200) + `...<${s.length}>`;
        return s || "";
      };
      const payload = { ts:new Date().toISOString(), file:"content_instagram.js", fn, loc, line, href:location.href, value };
      const out = clip(payload);
      const key = `IG_dbg/content_instagram.js/${fn}/${loc}/${line}`.replace(/\s+/g,"_");
      localStorage.setItem(key, out);
      localStorage.setItem("IG_dbg/content_instagram.js/_last", out);
      console.log(`[IG_DBG][content][${fn}][${loc}:${line}]`, value);
    } catch (e) {}
  }
  function dbgInfoSummary(info){
    const arr = Array.isArray(info) ? info : [];
    return { count: arr.length, first: arr[0] ? {code:arr[0].code, has_img_origin:!!arr[0].img_origin, has_video_url:!!arr[0].video_url, has_carousel:!!arr[0].carousel_media, play_count:arr[0].play_count, username:arr[0].username} : null };
  }

  const IGBLUE_EXT_VERSION_NAME = (() => { try { const m = chrome.runtime.getManifest(); return String(m.version_name || m.version || "2.3.8.170"); } catch (_) { return "2.3.8.170"; } })();
  const IGBLUE_EXT_BUILD_TAG = (() => { try { const m = String(IGBLUE_EXT_VERSION_NAME || "").match(/(\d+(?:a\d*)?)$/i); return m ? ("v" + m[1]) : String(IGBLUE_EXT_VERSION_NAME || "v170"); } catch (_) { return "v170"; } })();

  // v127: version-aware bridge guard.  A simple boolean guard kept old v116/v119
  // listeners alive after extension reloads and made logs appear to jump between builds.
  const IGBLUE_PREV_CONTENT_VERSION = String(window.__IGBLUE_CONTENT_BRIDGE_LOADED_VERSION__ || (window.__IGBLUE_CONTENT_BRIDGE_LOADED__ ? "unknown_old_boolean_guard" : ""));
  if (window.__IGBLUE_CONTENT_BRIDGE_LOADED__ && IGBLUE_PREV_CONTENT_VERSION === IGBLUE_EXT_VERSION_NAME) return;
  window.__IGBLUE_CONTENT_BRIDGE_LOADED__ = true;
  window.__IGBLUE_CONTENT_BRIDGE_LOADED_VERSION__ = IGBLUE_EXT_VERSION_NAME;
  window.__IGBLUE_CONTENT_BRIDGE_BUILD_TAG__ = IGBLUE_EXT_BUILD_TAG;
  window.__IGBLUE_CONTENT_BRIDGE_PREV_VERSION__ = IGBLUE_PREV_CONTENT_VERSION;

  const OVERLAY_MODE_KEY = "ndy_ig_overlay_mode";
  const OVERLAY_MODES = ["detail", "download", "none"];
  const DEFAULT_OVERLAY_MODE = "detail";
  const ER_WEIGHTS_KEY = "ndy_ig_er_weights";
  const ER_KEYS = Object.freeze(["like", "comment", "repost"]);
  const DEFAULT_ER_WEIGHTS = Object.freeze({ like: 1, comment: 4, repost: 4 });
  const VISUAL_SETTINGS_KEY = "ndy_ig_visual_settings";
  // v223: OS mouse, origin calibration, hover verification and owner-heart UI retired.

  // v86: main rank labels는 side.js가 정렬/필터/다운로드 위치 기준으로만 만든다.
  // content bridge는 page-world media 원본만 background/side로 전달한다.
  // 임시 No/download_root/download_account fallback 생성 금지.

  function sendIgInfo(evt) {
    dbg("sendIgInfo", "entry", 48, {visible:document.visibilityState, event:evt&&evt.type, summary:dbgInfoSummary(evt&&evt.detail&&evt.detail.root)});
    if (document.visibilityState !== "visible") return;
    if (!evt || evt.type !== "ndy_ig_info") return;
    const payload = { type: "ndy_bg_ig", info: evt.detail && evt.detail.root };
    try { chrome.runtime.sendMessage(payload, () => { if(chrome.runtime.lastError) dbg("sendIgInfo", "runtime_error", 53, {message:chrome.runtime.lastError.message}); else dbg("sendIgInfo", "sent", 53, {summary:dbgInfoSummary(payload.info)}); }); } catch (e) { dbg("sendIgInfo", "exception", 53, {message:String(e&&e.message||e)}); }
  }

  function sendDownloadQueue(evt) {
    const count = Number(evt && evt.detail && evt.detail.count);
    dbg("sendDownloadQueue", "entry", 57, {count, detail:evt&&evt.detail});
    try { chrome.runtime.sendMessage({ type: "ndy_dq", count: Number.isFinite(count) ? count : 0 }, () => { chrome.runtime.lastError; }); } catch {}
  }

  // IGblue local patch v15: forward byte-level download progress from the page-world downloader to side.js.
  function sendDownloadProgress(evt) {
    const d = (evt && evt.detail) || {};
    dbg("sendDownloadProgress", "entry", 65, {status:d.status,file:d.file,file_path:d.file_path,shortcode:d.shortcode,loaded:d.loaded,total:d.total,percent:d.percent,message:d.message});
    try {
      chrome.runtime.sendMessage({
        type: "ndy_down_progress",
        status: d.status || "",
        file: d.file || "",
        file_path: d.file_path || "",
        file_paths: Array.isArray(d.file_paths) ? d.file_paths : [],
        json_path: d.json_path || "",
        shortcode: d.shortcode || (d.meta && (d.meta.shortcode || d.meta.code)) || "",
        asset_kind: d.asset_kind || "",
        media_index: d.media_index != null ? d.media_index : 0,
        loaded: Number(d.loaded) || 0,
        total: Number(d.total) || 0,
        percent: Number(d.percent) || 0,
        message: d.message || "",
        meta: d.meta || null
      }, () => { chrome.runtime.lastError; });
    } catch {}
  }

  function postMainDevStatus(message, level, detail) {
    try {
      window.postMessage({ source: "ndy_ig_sorter", type: "igblue_dev_status", message: String(message || ""), level: level || "info", detail: detail || null }, "*");
    } catch {}
  }


  // v179a32: CDP field office gate. Auto/Auth proves CDP Runtime.evaluate communication by dispatching a page event; content owns the UI marker and diagnostics.
  let igblueCdpGateState = { ok:false, source:'startup', target_id:'', tab_id:'', cdp_schedule_only:true, ts:0 };
  let igblueCdpGateTip = null;
  function igblueCdpGateText() {
    const s = igblueCdpGateState || {};
    return [
      'CDP:' + (s.ok ? 'O' : 'X'),
      'source=' + String(s.source || '-'),
      'target_id=' + String(s.target_id || '-'),
      'tab_id=' + String(s.tab_id || s.request_tab_id || '-'),
      'cdp_scope=schedule_only',
      'ts=' + (s.ts ? new Date(Number(s.ts)).toISOString() : '-')
    ].join('\n') + '\n\n\n';
  }
  function igbluePlaceCdpGate(el) {
    try {
      if (!el) return;
      const panel = document.getElementById('IGBLUE_NoTop') || document.querySelector('[id*="IGBLUE_NoTop"]');
      const gateW = 58, gateH = 18, pad = 8;
      let left = pad;
      let top = 56;
      if (panel && panel.getBoundingClientRect) {
        const r = panel.getBoundingClientRect();
        if (r && Number.isFinite(r.left) && r.width > 20) left = r.left;
      }
      if (panel && panel.getBoundingClientRect) {
        const r = panel.getBoundingClientRect();
        if (r && Number.isFinite(r.top)) top = Math.max(pad, r.top - gateH - 8);
      }
      const maxX = Math.max(pad, (window.innerWidth || 0) - gateW - pad);
      const maxY = Math.max(pad, (window.innerHeight || 0) - gateH - pad);
      el.style.left = Math.round(Math.max(pad, Math.min(maxX, left))) + 'px';
      el.style.top = Math.round(Math.max(pad, Math.min(maxY, top))) + 'px';
      el.style.right = 'auto';
    } catch (_) {}
  }
  function igblueEnsureCdpGate() {
    let el = document.getElementById('igblue_cdp_gate');
    if (!el) {
      el = document.createElement('div');
      el.id = 'igblue_cdp_gate';
      el.textContent = 'CDP:X';
      el.style.cssText = 'position:fixed;right:10px;top:56px;z-index:2147483646;width:58px;height:18px;box-sizing:border-box;padding:2px 5px;border-radius:999px;background:rgba(15,23,42,.54);border:1px solid rgba(148,163,184,.55);color:#e5e7eb;font:700 11px/13px Consolas,Arial,sans-serif;text-align:center;letter-spacing:.1px;backdrop-filter:blur(2px);cursor:default;user-select:text;pointer-events:auto;white-space:nowrap;overflow:hidden;';
      el.addEventListener('mouseenter', (ev)=>igblueShowCdpGateTip(ev));
      el.addEventListener('mousemove', (ev)=>igbluePlaceCdpGateTip(ev));
      el.addEventListener('mouseleave', ()=>{ if(igblueCdpGateTip) igblueCdpGateTip.style.display='none'; });
      el.addEventListener('dblclick', (ev)=>{ try{ ev.preventDefault(); ev.stopPropagation(); const txt=igblueCdpGateText(); if(navigator.clipboard&&navigator.clipboard.writeText) navigator.clipboard.writeText(txt); else { const ta=document.createElement('textarea'); ta.value=txt; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); ta.remove(); } igblueShowCdpGateTip(ev, 'CDP 게이트 진단 복사 완료'); }catch(_){} });
      (document.documentElement || document.body).appendChild(el);
    }
    try { igbluePlaceCdpGate(el); } catch (_) {}
    return el;
  }
  function igblueCdpGateTooltip() {
    if (igblueCdpGateTip && document.body.contains(igblueCdpGateTip)) return igblueCdpGateTip;
    igblueCdpGateTip = document.createElement('div');
    igblueCdpGateTip.style.cssText = 'position:fixed;z-index:2147483647;max-width:440px;max-height:55vh;overflow:auto;padding:8px 10px;border-radius:10px;background:rgba(15,23,42,.94);border:1px solid rgba(148,163,184,.5);color:#e5e7eb;font:12px/1.45 Consolas,monospace;white-space:pre-wrap;box-shadow:0 12px 28px rgba(0,0,0,.28);pointer-events:none;display:none;';
    document.body.appendChild(igblueCdpGateTip);
    return igblueCdpGateTip;
  }
  function igbluePlaceCdpGateTip(ev) {
    const tip = igblueCdpGateTooltip();
    let x = (ev && ev.clientX || 0) + 14, y = (ev && ev.clientY || 0) + 14;
    const pad = 12, w = tip.offsetWidth || 300, h = tip.offsetHeight || 150;
    if (x + w + pad > innerWidth) x = Math.max(pad, innerWidth - w - pad);
    if (y + h + pad > innerHeight) y = Math.max(pad, innerHeight - h - pad);
    tip.style.left = x + 'px'; tip.style.top = y + 'px';
  }
  function igblueShowCdpGateTip(ev, text) {
    const tip = igblueCdpGateTooltip();
    tip.textContent = String(text || igblueCdpGateText()).trimEnd();
    tip.style.display = 'block';
    igbluePlaceCdpGateTip(ev || {clientX: innerWidth-160, clientY: 84});
    if (text) setTimeout(()=>{ if(igblueCdpGateTip) igblueCdpGateTip.style.display='none'; }, 1000);
  }
  function igblueUpdateCdpGate(detail, source) {
    try {
      const d = detail && typeof detail === 'object' ? detail : {};
      igblueCdpGateState = Object.assign({}, igblueCdpGateState, d, {ok: d.ok !== false, source: source || d.source || 'cdp_event', ts: Date.now()});
      const el = igblueEnsureCdpGate();
      try { igbluePlaceCdpGate(el); } catch (_) {}
      el.textContent = igblueCdpGateState.ok ? 'CDP:O' : 'CDP:X';
      el.style.background = igblueCdpGateState.ok ? 'rgba(22,101,52,.58)' : 'rgba(69,44,4,.58)';
      el.title = igblueCdpGateText().trimEnd();
      try { localStorage.setItem('IGBLUE/cdp_gate', JSON.stringify(igblueCdpGateState)); } catch (_) {}
      try { postMainDevStatus('CDP gate ' + (igblueCdpGateState.ok ? 'OK' : 'XX'), igblueCdpGateState.ok ? 'ok' : 'warn', igblueCdpGateState); } catch (_) {}
    } catch (_) {}
  }
  try { setTimeout(()=>igblueEnsureCdpGate(), 300); setInterval(()=>{ try { igbluePlaceCdpGate(document.getElementById('igblue_cdp_gate')); } catch (_) {} }, 1500); } catch (_) {}
  window.addEventListener('igblue_auto8700_cdp_gate', (ev)=>igblueUpdateCdpGate(ev && ev.detail || {}, 'document_event'));
  window.addEventListener('message', (ev)=>{ const d=ev&&ev.data; if(d && d.source === 'AUTO8700' && d.type === 'IGBLUE_AUTO8700_CDP_GATE') igblueUpdateCdpGate(d.detail || d, 'window_message'); });

  function postRtcStatus(value) {
    try {
      window.postMessage({ source: "ndy_ig_sorter", type: "igblue_rtc_status", value: value || {} }, "*");
    } catch {}
  }

  function postDownloadResult(value) {
    try {
      window.postMessage({ source: "ndy_ig_sorter", type: "igblue_download_result", value: value || {} }, "*");
    } catch {}
  }

  function postPyState(value) {
    try {
      window.postMessage({ source: "ndy_ig_sorter", type: "igblue_py_state", value: value || {} }, "*");
    } catch {}
  }

  function checkRtcStatus(force) {
    try {
      chrome.runtime.sendMessage({ type:"IGBLUE_RTC_STATUS", force:!!force, href:location.href, host:location.host }, (res) => {
        if (chrome.runtime.lastError) {
          const st = { connected:false, status:"offline", message:"RTC 꺼짐 / 다운로드 불가", error:chrome.runtime.lastError.message };
          postRtcStatus(st);
          postMainDevStatus("RTC 꺼짐 / 다운로드 불가", "warn", st);
          return;
        }
        const st = Object.assign({ connected:false, status:"offline", message:"RTC 꺼짐 / 다운로드 불가" }, res || {});
        postRtcStatus(st);
        postMainDevStatus(st.connected ? "RTC 연결됨" : "RTC 꺼짐 / 다운로드 불가", st.connected ? "ok" : "warn", st);
      });
    } catch (err) {
      const st = { connected:false, status:"offline", message:"RTC 꺼짐 / 다운로드 불가", error:String(err && err.message || err) };
      postRtcStatus(st);
      postMainDevStatus("RTC 꺼짐 / 다운로드 불가", "warn", st);
    }
  }

  setTimeout(() => checkRtcStatus(false), 900);
  setInterval(() => checkRtcStatus(false), 10000);





  // IGblue v51: MAIN-world overlay downloads must use the same Chrome downloads API path as side.js.
  function sendMainBackgroundDownload(evt) {
    const d = (evt && evt.detail) || {};
    const url = String(d.url || "");
    const prefix = String(d.prefix || "media");
    const root = String(d.root || "10만");
    const account = String(d.account || "instagram");
    dbg("sendMainBackgroundDownload", "entry", 82, {url:url.slice(0,260), prefix, root, account});
    if (!url) return;
    try {
      const flowId = `manual-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
      const flowMeta = Object.assign({href: location.href, page_url: location.href}, d.meta || {}, { source_mode: "MANUAL_SINGLE", client_batch_key: `batch-${flowId}`, client_job_key: `job-${flowId}`, client_event_id: `job-${flowId}`, batch_requested_count: 1 });
      chrome.runtime.sendMessage({
        type: "IGBLUE_DOWNLOAD_URL",
        url,
        prefix,
        root,
        account,
        href: location.href,
        ext: d.ext || "",
        meta: flowMeta,
        notify_aiwk: true
      }, (res) => {
        if (chrome.runtime.lastError) {
          dbg("sendMainBackgroundDownload", "runtime_error", 95, {message: chrome.runtime.lastError.message, prefix});
          return;
        }
        dbg("sendMainBackgroundDownload", "response", 99, {prefix, res});
        if(res && (res.status === "ok" || res.status === "already_done" || res.error_code === "IGBLUE_ALREADY_DOWNLOADED")){
          postDownloadResult(res);
          postMainDevStatus(res.status === "already_done" ? "이미 완료\n다운로드 차단" : "다운로드 완료", "ok", res);
        }
        if(res && res.status === "partial"){
          postDownloadResult(res);
          postMainDevStatus("파일 저장 완료\nDB 처리 실패", "warn", res);
        }
        if(res && (res.error_code === "IGBLUE_RTC_OFFLINE" || res.error_code === "IGBLUE_RTC_STATUS_ERROR" || res.error_code === "IGBLUE_DUP_CHECK_ERROR")){
          postRtcStatus({connected:false,status:"offline",message:res.message||"RTC 꺼짐 / 다운로드 불가",raw:res});
          postMainDevStatus(res.error_code === "IGBLUE_DUP_CHECK_ERROR" ? "중복확인 실패\n다운로드 불가" : "RTC 꺼짐 / 다운로드 불가", "warn", res);
        }
      });
    } catch (err) {
      dbg("sendMainBackgroundDownload", "exception", 103, {message:String(err && err.message || err), prefix});
    }
  }

  async function scrollBottom() {
    window.scrollTo(0, Number.MAX_SAFE_INTEGER);
  }

  function sleep(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }

  async function swipe(count) {
    for (let i = 0; i < count; i++) {
      await scrollBottom();
      if (i < 5) await sleep(3000);
      else if (i < 10) await sleep(6000);
      else await sleep(10000);
    }
  }


  function sendPySetting(evt) {
    const d = (evt && evt.detail) || {};
    try { chrome.runtime.sendMessage({ type: "IGBLUE_PY_SETTING", data: Object.assign({ host: location.host, href: location.href, forwarded_at: new Date().toISOString(), content_version: IGBLUE_EXT_VERSION_NAME, content_build_tag: IGBLUE_EXT_BUILD_TAG }, d) }, (res) => { chrome.runtime.lastError; if(res) postPyState(res); }); } catch {}
  }

  document.addEventListener("ndy_ig_info", sendIgInfo);
  document.addEventListener("ndy_dq", sendDownloadQueue);
  document.addEventListener("ndy_down_progress", sendDownloadProgress);
  document.addEventListener("ndy_ig_bg_down", sendMainBackgroundDownload);
  document.addEventListener("igblue_py_setting", sendPySetting);

  // v179a39: relay MAIN-world screenX/screenY movement to side/background.
  function sendWindowMoveState(ev) {
    try {
      const detail = Object.assign({}, ev && ev.detail || {});
      chrome.runtime.sendMessage({type:'IGBLUE_WINDOW_MOVE_STATE', window_move:detail, tab_url:String(location.href||''), ts:Date.now()}).catch(()=>{});
    } catch (_) {}
  }
  document.addEventListener('igblue_window_move_state', sendWindowMoveState);


  chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
    try {
      if (!msg || !msg.type) return false;
      dbg("onMessage", "recv", 95, {type:msg.type, url:msg.url, prefix:msg.prefix, value:msg.value});
      if (msg.type === "ndy_swipe") {
        swipe(Number(msg.count) || 0).then(() => {}).catch(() => {});
        sendResponse("ok");
        return true;
      }
      if (msg.type === "ndy_top") {
        window.scrollTo(0, 0);
        sendResponse("ok");
        return true;
      }
      if (msg.type === "ndy_refresh") {
        window.location.reload();
        sendResponse("ok");
        return true;
      }
      if (msg.type === "ndy_goto") {
        if (msg && msg.newpage === 1) window.open(msg.url, "_blank");
        else window.location.href = msg.url;
        sendResponse("ok");
        return true;
      }
      if (msg.type === "ndy_content_down") {
        dbg("onMessage", "ndy_content_down", 113, {url:String(msg.url||"").slice(0,260), prefix:msg.prefix});
        const ev = new CustomEvent("ndy_ig_down", {
          detail: {
            type: "single",
            video_src: {
              video_url: msg.url,
              video_id: "22",
              page: "content",
              prefix: msg.prefix,
            },
          },
        });
        document.dispatchEvent(ev);
        sendResponse("ok");
        return true;
      }
      if (msg.type === "ndy_ig_overlay_mode_changed") {
        const mode = msg.value;
        if (isOverlayMode(mode)) postOverlayMode(mode);
        sendResponse("ok");
        return true;
      }
      if (msg.type === "ndy_ig_er_weights_changed") {
        const weights = msg.value;
        if (validWeights(weights)) postErWeights(weights);
        sendResponse("ok");
        return true;
      }
      if (msg.type === "ndy_ig_visual_settings_changed") {
        postVisualSettings(msg.value || {});
        sendResponse("ok");
        return true;
      }
      if (msg.type === "ndy_ig_main_rank_labels_changed") {
        postMainRankLabels(msg.labels || []);
        sendResponse("ok");
        return true;
      }
      if (msg.type === "igblue_get_main_injection_state") {
        const waitMs = Math.max(40, Math.min(1200, Number(msg.wait_ms || 120) || 120));
        const requestedNo = Math.max(0, Number(msg.requested_no || 0) || 0);
        const readCurrent = () => {
          const el = document.querySelector("#IGBLUE_NoNav_Current");
          const text = String(el && el.textContent || "").trim();
          const m = text.match(/No\s*0*(\d+)/i);
          return { el, text, no: m ? Number(m[1]) : 0 };
        };
        const scan = () => {
          const nums = [];
          try {
            document.querySelectorAll("[data-rank-no],.igblue-rank-id,.igblue-nav-no").forEach((el) => {
              const raw = String((el && el.getAttribute && el.getAttribute("data-rank-no")) || (el && el.textContent) || "");
              const m = raw.match(/No\s*0*(\d+)|^\s*0*(\d+)\s*$/i);
              const n = m ? Number(m[1] || m[2] || 0) : 0;
              if (Number.isFinite(n) && n > 0) nums.push(n);
            });
          } catch (_) {}
          const unique = Array.from(new Set(nums)).sort((a,b)=>a-b);
          const cur = readCurrent();
          const panel = document.querySelector("#IGBLUE_NoTop");
          const activeRaw = (() => { try { return localStorage.getItem("IGBLUE/active_download_target") || ""; } catch (_) { return ""; } })();
          let activeTarget = null;
          try { activeTarget = activeRaw ? JSON.parse(activeRaw) : null; } catch (_) { activeTarget = null; }
          sendResponse({ ok: true, action: "main_injection_state", panel_found: !!panel, current_button_found: !!cur.el, current_text: cur.text, current_rank_no: cur.no, requested_no: requestedNo, has_requested: requestedNo > 0 ? unique.includes(requestedNo) : false, loaded_max_no: unique.length ? unique[unique.length - 1] : 0, loaded_count: unique.length, loaded_nos_sample: unique.slice(-40), active_target_found: !!activeTarget, active_rank_no: Number(activeTarget && activeTarget.rank || 0) || 0, active_shortcode: String(activeTarget && (activeTarget.code || activeTarget.shortcode) || ""), href: location.href });
        };
        setTimeout(scan, waitMs);
        return true;
      }
      if (msg.type === "igblue_auto_py_loaded_rank_state") {
        const waitMs = Math.max(40, Math.min(1200, Number(msg.wait_ms || 120) || 120));
        const requestedNo = Math.max(0, Number(msg.requested_no || 0) || 0);
        const readCurrent = () => {
          const el = document.querySelector("#IGBLUE_NoNav_Current");
          const text = String(el && el.textContent || "").trim();
          const m = text.match(/No\s*0*(\d+)/i);
          return m ? Number(m[1]) : 0;
        };
        const readHashNo = () => {
          try {
            const raw = String(location && location.hash || "").replace(/^#/, "").trim();
            const m = raw.match(/^No\s*0*(\d{1,6})$/i);
            const n = m ? Number(m[1]) : 0;
            return Number.isFinite(n) && n > 0 ? n : 0;
          } catch (_) { return 0; }
        };
        const scan = () => {
          const nums = [];
          try {
            document.querySelectorAll("[data-rank-no],.igblue-rank-id,.igblue-nav-no").forEach((el) => {
              const raw = String((el && el.getAttribute && el.getAttribute("data-rank-no")) || (el && el.textContent) || "");
              const m = raw.match(/No\s*0*(\d+)|^\s*0*(\d+)\s*$/i);
              const n = m ? Number(m[1] || m[2] || 0) : 0;
              if (Number.isFinite(n) && n > 0) nums.push(n);
            });
          } catch (_) {}
          const unique = Array.from(new Set(nums)).sort((a,b)=>a-b);
          const loadedMax = unique.length ? unique[unique.length - 1] : 0;
          const de = document.documentElement || document.body || {};
          const body = document.body || {};
          const scrollY = Math.max(0, Number(window.scrollY || window.pageYOffset || de.scrollTop || body.scrollTop || 0) || 0);
          const innerHeight = Math.max(0, Number(window.innerHeight || de.clientHeight || body.clientHeight || 0) || 0);
          const scrollHeight = Math.max(Number(de.scrollHeight || 0) || 0, Number(body.scrollHeight || 0) || 0, innerHeight);
          const atBottom = !!(scrollHeight > 0 && scrollY + innerHeight >= scrollHeight - 16);
          const currentNo = readCurrent();
          const hashNo = readHashNo();
          sendResponse({ ok: true, action: "loaded_rank_state", loaded_max_no: loadedMax, loaded_min_no: unique[0] || 0, loaded_count: unique.length, loaded_nos_sample: unique.slice(-40), requested_no: requestedNo, has_requested: requestedNo > 0 ? unique.includes(requestedNo) : false, current_rank_no: currentNo, active_rank_no: currentNo, active_hash_no: hashNo, hash_rank_no: hashNo, href: location.href, scroll_y: scrollY, inner_height: innerHeight, scroll_height: scrollHeight, at_bottom: atBottom });
        };
        setTimeout(scan, waitMs);
        return true;
      }
      if (msg.type === "igblue_auto_py_read_current_no") {
        const waitMs = Math.max(80, Math.min(1800, Number(msg.wait_ms || 300) || 300));
        const readTarget = () => {
          try { const raw = localStorage.getItem("IGBLUE/active_download_target") || ""; return raw ? JSON.parse(raw) : null; } catch (err) { return null; }
        };
        const readCurrent = () => {
          const el = document.querySelector("#IGBLUE_NoNav_Current");
          const text = String(el && el.textContent || "").trim();
          const m = text.match(/No\s*0*(\d+)/i);
          const target = readTarget();
          const rank = m ? Number(m[1]) : Number(target && target.rank || 0) || 0;
          return { ok: !!rank, action: "read_current", rank_no: rank, rank_label: rank ? ("No" + String(rank).padStart(3, "0")) : "", text, shortcode: String(target && target.code || ""), target_found: !!target, target_age_ms: target && target.ts ? Math.max(0, Date.now() - Number(target.ts || 0)) : 0 };
        };
        setTimeout(() => sendResponse(readCurrent()), waitMs);
        return true;
      }
      if (msg.type === "igblue_auto_py_current") {
        const waitMs = Math.max(180, Math.min(3000, Number(msg.wait_ms || 1200) || 1200));
        const readTarget = () => {
          try { const raw = localStorage.getItem("IGBLUE/active_download_target") || ""; return raw ? JSON.parse(raw) : null; } catch (err) { return null; }
        };
        const readRank = () => {
          const el = document.querySelector("#IGBLUE_NoNav_Current");
          const text = String(el && el.textContent || "").trim();
          const m = text.match(/No\s*0*(\d+)/i);
          const target = readTarget();
          return { text, rank: m ? Number(m[1]) : Number(target && target.rank || 0) || 0, shortcode: String(target && target.code || "") };
        };
        const before = readRank();
        const btn = document.querySelector("#IGBLUE_NoNav_Current");
        if (!btn && !before.rank) { sendResponse({ ok: false, action: "click_current", error: "NoNav_Current_missing", before_rank: before.rank, before_label: "", before_shortcode: before.shortcode, current_button_found: false }); return true; }
        try { localStorage.removeItem("IGBLUE/active_download_target"); } catch (_) {}
        try {
          if (btn) btn.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true, view: window }));
          else window.postMessage({ source: "ndy_ig_sorter", type: "igblue_auto_py_go_no", no: before.rank }, "*");
        } catch (_) {
          try { if (btn) btn.click(); } catch (e) {}
        }
        setTimeout(() => {
          const after = readRank();
          const target = readTarget();
          const same = !!(after.rank && before.rank && after.rank === before.rank);
          sendResponse({ ok: !!(after.rank || target), action: "click_current", before_rank: before.rank, after_rank: after.rank, before_label: before.rank ? ("No" + String(before.rank).padStart(3, "0")) : "", after_label: after.rank ? ("No" + String(after.rank).padStart(3, "0")) : "", before_shortcode: before.shortcode, after_shortcode: after.shortcode, changed: !!(after.rank && before.rank && after.rank !== before.rank), current_clicked: true, current_button_found: !!btn, target_found: !!target, target_age_ms: target && target.ts ? Math.max(0, Date.now() - Number(target.ts || 0)) : 0, confirmed_current: same || !!target });
        }, waitMs);
        return true;
      }
      if (msg.type === "igblue_auto_py_next") {
        const waitMs = Math.max(300, Math.min(3000, Number(msg.wait_ms || 1200) || 1200));
        const readTarget = () => {
          try { const raw = localStorage.getItem("IGBLUE/active_download_target") || ""; return raw ? JSON.parse(raw) : null; } catch (err) { return null; }
        };
        const readRank = () => {
          const el = document.querySelector("#IGBLUE_NoNav_Current");
          const text = String(el && el.textContent || "").trim();
          const m = text.match(/No\s*0*(\d+)/i);
          const target = readTarget();
          return { text, rank: m ? Number(m[1]) : Number(target && target.rank || 0) || 0, shortcode: String(target && target.code || "") };
        };
        const before = readRank();
        const btn = document.querySelector("#IGBLUE_NoNav_Next");
        if (!btn) { sendResponse({ ok: false, action: "click_next", error: "NoNav_Next_missing", before_rank: before.rank, before_label: before.rank ? ("No" + String(before.rank).padStart(3, "0")) : "", before_shortcode: before.shortcode, next_button_found: false }); return true; }
        try { localStorage.removeItem("IGBLUE/active_download_target"); } catch (_) {}
        try { btn.dispatchEvent(new MouseEvent("click", { bubbles: true, cancelable: true, view: window })); } catch (_) { try { btn.click(); } catch (e) {} }
        setTimeout(() => {
          const after = readRank();
          sendResponse({ ok: true, action: "click_next", before_rank: before.rank, after_rank: after.rank, before_label: before.rank ? ("No" + String(before.rank).padStart(3, "0")) : "", after_label: after.rank ? ("No" + String(after.rank).padStart(3, "0")) : "", before_shortcode: before.shortcode, after_shortcode: after.shortcode, changed: !!(after.rank && after.rank !== before.rank), target_found: !!readTarget(), next_button_found: true });
        }, waitMs);
        return true;
      }
      if (msg.type === "igblue_auto_py_go_no") {
        const requestedNo = Number(msg.no || 1) || 1;
        const waitMs = Math.max(180, Math.min(2200, Number(msg.wait_ms || 750) || 750));
        const readTarget = () => {
          try {
            const raw = localStorage.getItem("IGBLUE/active_download_target") || "";
            return raw ? JSON.parse(raw) : null;
          } catch (err) {
            return null;
          }
        };
        const before = readTarget();
        try { localStorage.removeItem("IGBLUE/active_download_target"); } catch (_) {}
        window.postMessage({ source: "ndy_ig_sorter", type: "igblue_auto_py_go_no", no: requestedNo }, "*");
        setTimeout(() => {
          const after = readTarget();
          const beforeRank = Number(before && before.rank || 0) || 0;
          const afterRank = Number(after && after.rank || 0) || 0;
          const beforeCode = String(before && before.code || "");
          const afterCode = String(after && after.code || "");
          sendResponse({
            ok: true,
            action: "go_no",
            requested_no: requestedNo,
            before_rank: beforeRank,
            after_rank: afterRank,
            before_shortcode: beforeCode,
            after_shortcode: afterCode,
            changed: !!(afterRank && (afterRank !== beforeRank || afterRank === requestedNo || afterCode !== beforeCode)),
            target_found: !!after,
            stale_cleared_before_go: true,
            target_age_ms: after && after.ts ? Math.max(0, Date.now() - Number(after.ts || 0)) : 0
          });
        }, waitMs);
        return true;
      }
      if (msg.type === "igblue_auto_status_update") {
        try {
          const v = msg.value && typeof msg.value === 'object' ? msg.value : {};
          igblueAutoStatusSnapshot = Object.assign({}, v, { text: String(v.text || ''), updated_at: Date.now() });
          try { window.postMessage({ source: "ndy_ig_sorter", type: "igblue_main_panel_auto_state", value: { active: !!v.active, reason: String(v.reason || ""), no_label: String(v.no_label || "") } }, "*"); } catch (_) {}
        } catch (_) {}
        sendResponse({ ok: true });
        return true;
      }
      if (msg.type === "igblue_dev_status") {
        window.postMessage({ source: "ndy_ig_sorter", type: "igblue_dev_status", message: String(msg.message || ""), level: msg.level || "info", detail: msg.detail || null }, "*");
        sendResponse({ ok: true });
        return true;
      }
      if (msg.type === "igblue_py_state") {
        postPyState(msg.value || msg);
        sendResponse({ ok: true });
        return true;
      }
      if (msg.type === "igblue_set_tts_level") {
        const value = msg.value || {};
        window.postMessage({ source: "ndy_ig_sorter", type: "igblue_set_tts_level", value }, "*");
        sendResponse({ ok: true });
        return true;
      }
      if (msg.type === "igblue_get_active_download_target") {
        try {
          const raw = localStorage.getItem("IGBLUE/active_download_target") || "";
          const target = raw ? JSON.parse(raw) : null;
          sendResponse({ ok: !!target, target });
        } catch (err) {
          sendResponse({ ok: false, error: String(err && err.message || err) });
        }
        return true;
      }
    } catch {}
    return false;
  });

  chrome.storage.onChanged.addListener((changes, area) => {
    if (area !== "local") return;
    if (Object.prototype.hasOwnProperty.call(changes, OVERLAY_MODE_KEY)) {
      const mode = changes[OVERLAY_MODE_KEY].newValue;
      postOverlayMode(isOverlayMode(mode) ? mode : DEFAULT_OVERLAY_MODE);
    }
    if (Object.prototype.hasOwnProperty.call(changes, ER_WEIGHTS_KEY)) {
      const weights = changes[ER_WEIGHTS_KEY].newValue;
      postErWeights(validWeights(weights) ? normalizeWeights(weights) : { ...DEFAULT_ER_WEIGHTS });
    }
    if (Object.prototype.hasOwnProperty.call(changes, VISUAL_SETTINGS_KEY)) {
      postVisualSettings(changes[VISUAL_SETTINGS_KEY].newValue || {});
    }
  });

  chrome.storage.local.get(OVERLAY_MODE_KEY, (data = {}) => {
    const mode = data[OVERLAY_MODE_KEY];
    const value = isOverlayMode(mode) ? mode : DEFAULT_OVERLAY_MODE;
    if (!isOverlayMode(mode)) chrome.storage.local.set({ [OVERLAY_MODE_KEY]: value });
    postOverlayMode(value);
  });

  chrome.storage.local.get(ER_WEIGHTS_KEY, (data = {}) => {
    const weights = data[ER_WEIGHTS_KEY];
    const value = validWeights(weights) ? normalizeWeights(weights) : { ...DEFAULT_ER_WEIGHTS };
    if (!validWeights(weights)) chrome.storage.local.set({ [ER_WEIGHTS_KEY]: value });
    postErWeights(value);
  });

  chrome.storage.local.get(VISUAL_SETTINGS_KEY, (data = {}) => {
    postVisualSettings(data[VISUAL_SETTINGS_KEY] || {});
  });

  // Wake the background so it can remember this tab even before the first media payload arrives.


  // IGblue v179a14: Auto8700 예약 실행 bridge.
  // Auto8700이 Chrome CDP로 Instagram page에 CustomEvent/window message를 던지면,
  // content script가 background/side로 전달한다. 실제 자동다운로드 루프는 side.js 기존 함수를 재사용한다.
  function igblueForwardAuto8700StartRequest(payload, source) {
    try {
      const d = payload && typeof payload === "object" ? payload : {};
      const req = Object.assign({
        type: "IGBLUE_AUTO8700_START_REQUEST",
        source: "Auto8700",
        bridge_source: source || "content",
        href: location.href,
        url: location.href,
        ts: Date.now()
      }, d || {});
      if (!req.request_id) req.request_id = "auto8700_content_" + Date.now() + "_" + Math.floor(Math.random() * 100000);
      try { window.postMessage({ source: "ndy_ig_sorter", type: "igblue_main_panel_auto_start", value: { active: true, reason: String(req.reason || req.trigger || "auto8700_start") } }, "*"); } catch (_) {}
      chrome.runtime.sendMessage(req, () => {
        try {
          if (chrome.runtime.lastError) postMainDevStatus("Auto8700 예약 요청 전달 실패", "warn", { message: chrome.runtime.lastError.message, request_id: req.request_id });
          else postMainDevStatus("Auto8700 예약 요청 전달", "info", { request_id: req.request_id });
        } catch (_) {}
      });
    } catch (err) {
      try { postMainDevStatus("Auto8700 예약 bridge 예외", "warn", { error: String(err && err.message || err) }); } catch (_) {}
    }
  }


  // IGblue v179a16: Auto8700 CDP-owned schedule storage mirror.
  // Auto8700이 Instagram origin localStorage에 만든 예약 기본틀을 chrome.storage로 복사한다.
  const IGBLUE_AUTO8700_SCHEDULE_KEY = "IGBLUE_AUTO8700_SCHEDULE_V1";
  function igblueMirrorAuto8700ScheduleStorage(reason) {
    try {
      const raw = localStorage.getItem(IGBLUE_AUTO8700_SCHEDULE_KEY);
      if (!raw) return;
      const parsed = JSON.parse(raw);
      if (!parsed || typeof parsed !== "object") return;
      chrome.runtime.sendMessage({ type: "IGBLUE_AUTO8700_SCHEDULE_MIRROR", schedule: parsed, reason: reason || "content_mirror", href: location.href }, () => { chrome.runtime.lastError; });
    } catch (_) {}
  }
  window.addEventListener("igblue_auto8700_schedule_sync", () => igblueMirrorAuto8700ScheduleStorage("cdp_sync_event"));
  window.addEventListener("storage", (ev) => { if (ev && ev.key === IGBLUE_AUTO8700_SCHEDULE_KEY) igblueMirrorAuto8700ScheduleStorage("storage_event"); });
  setTimeout(() => igblueMirrorAuto8700ScheduleStorage("startup"), 800);
  setInterval(() => igblueMirrorAuto8700ScheduleStorage("poll"), 5000);

  document.addEventListener("igblue_auto8700_schedule_start", (ev) => {
    igblueForwardAuto8700StartRequest(ev && ev.detail || {}, "document_event");
  });
  window.addEventListener("message", (ev) => {
    const d = ev && ev.data;
    if (!d || d.source !== "AUTO8700" || d.type !== "IGBLUE_AUTO8700_START_REQUEST") return;
    igblueForwardAuto8700StartRequest(d, "window_message");
  });

  function igblueForwardAuto8700SideOpenRequest(payload, source) {
    try {
      const d = payload && typeof payload === "object" ? payload : {};
      const req = Object.assign({
        type: "IGBLUE_AUTO8700_SIDE_OPEN_REQUEST",
        source: "Auto8700",
        bridge_source: source || "content",
        href: location.href,
        url: location.href,
        ts: Date.now()
      }, d || {});
      if (!req.request_id) req.request_id = "auto8700_side_content_" + Date.now() + "_" + Math.floor(Math.random() * 100000);
      chrome.runtime.sendMessage(req, () => { chrome.runtime.lastError; });
    } catch (_) {}
  }

  document.addEventListener("igblue_auto8700_side_open_request", (ev) => {
    igblueForwardAuto8700SideOpenRequest(ev && ev.detail || {}, "document_event");
  });
  window.addEventListener("message", (ev) => {
    const d = ev && ev.data;
    if (!d || d.source !== "AUTO8700" || d.type !== "IGBLUE_AUTO8700_SIDE_OPEN_REQUEST") return;
    igblueForwardAuto8700SideOpenRequest(d, "window_message");
  });

  try { chrome.runtime.sendMessage({ type: "IGBLUE_CONTENT_READY", url: location.href }, () => { if(chrome.runtime.lastError) dbg("content_ready", "runtime_error", 172, {message:chrome.runtime.lastError.message}); else dbg("content_ready", "sent", 172, {url:location.href}); }); } catch (e) { dbg("content_ready", "exception", 172, {message:String(e&&e.message||e)}); }
})();
